Search code examples
variablesgroovyclosures

Groovy: Update external variable form inside a closure


I have the following groovy code snippet below:

import groovy.xml.StreamingMarkupBuilder;

def processData() {
     // xml builder
    def xmlBuilder = new StreamingMarkupBuilder()
    xmlBuilder.encoding = 'UTF-8'
    
    // data
    def body = [1, 2, 3]
    flag = '0'
    
    def idocXML = xmlBuilder.bind {
        ROOT {
            body.each { line ->
                flag = '1'
            }
        }
    }// end of xml builder bind
   
    println("$flag")
}

processData()

In the code snippet, there is a variable flag inside the processData initialized with 0, and inside the xmlBuilder.bind and iteration of body, I updated flag to 1. But outside of those closures, when I print the flag, it shows as 0.

How to have flag updated as 1 from inside the body.each closure? Thanks


Solution

  • Your code is generally correct and will do what it supposed to do. The problem is that this bind closure is declared, but, actually, never called. You declared, how the XML should be constructed, but you never "triggered" the construction itself. Let me show you what I mean and how did I find it out. My first question was, is this flag = '1' line is executed at all? I've changed the code by adding an additional output:

    body.each { line ->
        println "line = $line" // this is the line I've added.
        flag = '1'
    }
    

    As you see, it prints nothing. Why? Because, as I wrote above, you described how your idocXML object must be constructed, but you never used the object. That is why this binding closure was never called. Here is the code that works as expected:

    import groovy.xml.StreamingMarkupBuilder;
    
    def processData() {
        // xml builder
        def xmlBuilder = new StreamingMarkupBuilder()
        xmlBuilder.encoding = 'UTF-8'
    
        // data
        def body = [1, 2, 3]
        flag = '0'
    
        def idocXML = xmlBuilder.bind {
            ROOT {
                body.each { line ->
                    println "line = $line" // make sure that binding is actually calledd
                    flag = '1'
                }
            }
        }// end of xml builder bind
    
        println idocXML.toString() // do something with the object to actually call binding
    
        println("$flag") // now, at that point, the flag will have a new value, because the binding logic updated it.
    }
    
    processData()
    

    I hope it helps.