javaxmlgrailsgroovymarkupbuilder

Groovy's MarkupBuilder exception on generating "id" XML tag


I'm trying to construct a XML feed, and Groovy's MarkupBuilder is giving me headaches:

 def newsstandFeed(def id) {
    def publication = Publication.get(id)
    def issues = issueService.getActiveIssuesForPublication(publication)
    def updateDate = DateUtil.getRFC3339DateString(publication.lastIssueUpdate)

    def writer = new StringWriter()
    writer.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
    def xml = new MarkupBuilder(writer)
    xml.feed('xmlns':"http://www.w3.org/2005/Atom", 'xmlns:news':"http://itunes.apple.com/2011/Newsstand") {
        updated("${updateDate}")
        issues.each { issue ->
            entry {
                id (issue.id)
                updated("${DateUtil.getRFC3339DateString(issue.lastUpdated)}")
                published("${DateUtil.getRFC3339DateString(issue.releaseDate)}")
                summary(issue.summary)
                "news:cover_art_icons" {
                    "news:cover_art_icon" (size:"SOURCE", src:"${issue.cover.remotePath}")
                }
            }
        }
    }

    return writer.toString()
}

I get this exception:

Class groovy.lang.MissingMethodException 
No signature of method: java.lang.String.call() is applicable for argument types: (org.codehaus.groovy.runtime.GStringImpl) values: [CYB_001] Possible solutions: wait(), any(), wait(long), any(groovy.lang.Closure), take(int), each(groovy.lang.Closure)

"CYB_001" is the first "id" attribute.

If I rename "id" it to "ids" or anything else, it works, and returns a proper XML document:

            ....
            issues.each { issue ->
            entry {
                ids ("${issue.id}")
                ...

Any ideas why this is happening, and how I can work around the problem?

The environment is Grails 2.1.1 (so Groovy 1.8, I assume)


Solution

  • It seems to me that your XML builder is trying to reference some String variable in the environment. Since groovy builder intercept missing method calls, if they find a reference they will try to apply to it. The following code can reproduce your error:

    def id = ""
    
    new groovy.xml.MarkupBuilder().xml {
      id "90"
    }
    

    And the following is fine:

    def ids = ""
    
    new groovy.xml.MarkupBuilder().xml {
      id "90"
    }
    

    Renaming your id variable should do the trick


    Update:

    An alternative way to use a tag with same name as a variable in the scope is with a (ugly) GString:

    def id = ""
    
    new groovy.xml.MarkupBuilder().xml {
      "${'id'}" "90"
    }