groovy
  1. groovy
  2. GROOVY-3983

Using xpath returns arraylist instead of nodelist

    Details

    • Type: Bug Bug
    • Status: Closed Closed
    • Priority: Major Major
    • Resolution: Not A Bug
    • Affects Version/s: 1.6.7
    • Fix Version/s: None
    • Component/s: XML Processing
    • Labels:
      None
    • Environment:
      Windows Vista
    • Number of attachments :
      0

      Description

      I just moved code from grails 1.1 to grails 1.2.

      I am parsing an XML structure similar to
      <Items><Item>...</Item><Item>...</Item></Items>

      My groovy code looks like
      for (item in doc.Items.Item)

      {...}

      It used to be that doc.Items.Item returned a Node List and then the variable item got bound to each Item from xml.

      Now the doc.Items.Item returns a one element ArrayList whose single element is the NodeList from before.
      So now item gets bound to the entire Item NodeList.

      This seems like a bug to me.

        Activity

        Hide
        Bob Herrmann added a comment - - edited

        Attaching code that would demonstrate this problem would probably expidite it's getting fixed...

        for example....

        quickController.groovy
        class quickController {
           def index = {
                def xml = '<root><Items><Item>...</Item><Item>...</Item></Items></root>'
                def doc = ??? slurper ???
                for(item in doc.Items.item){
                     // assert item.class == NodeList
                     // I'm suprised that this is now an ArrayList
                     assert item.class == ArrayList
                }
           }
        }
        
        Show
        Bob Herrmann added a comment - - edited Attaching code that would demonstrate this problem would probably expidite it's getting fixed... for example.... quickController.groovy class quickController { def index = { def xml = '<root><Items><Item>...</Item><Item>...</Item></Items></root>' def doc = ??? slurper ??? for (item in doc.Items.item){ // assert item.class == NodeList // I'm suprised that this is now an ArrayList assert item.class == ArrayList } } }
        Hide
        Andrew Ressler added a comment - - edited

        This is working code.

        def testgroovy1 = {
        def xmlData = "<root><Items><Item>data1</Item><Item>data2</Item></Items></root>"
        def doc = new XmlParser().parseText(xmlData)
        def items = doc.Items.Item
        // I'm suprised that "items" is now an ArrayList. It used to be a NodeList. Isn't this a bug?
        assert items.class == ArrayList
        for (item in items)

        { // now instead of item being one of the actual item nodes it is a nodelist // of the actual nodes. System.out.println item.class.toString() }

        }

        Show
        Andrew Ressler added a comment - - edited This is working code. def testgroovy1 = { def xmlData = "<root><Items><Item>data1</Item><Item>data2</Item></Items></root>" def doc = new XmlParser().parseText(xmlData) def items = doc.Items.Item // I'm suprised that "items" is now an ArrayList. It used to be a NodeList. Isn't this a bug? assert items.class == ArrayList for (item in items) { // now instead of item being one of the actual item nodes it is a nodelist // of the actual nodes. System.out.println item.class.toString() } }
        Hide
        Roshan Dawrani added a comment -

        I tried on 1.6.7 / 1.6.8/1.7.1 snapshots and I don't see this issue.

        I see groovy.util.NodeList / groovy.util.Node classes and not ArrayList

        def xmlData = "<root><Items><Item>data1</Item><Item>data2</Item></Items></root>"
        def doc = new XmlParser().parseText(xmlData)
        def items = doc.Items.Item
        
        println items.getClass() // is groovy.util.NodeList
        
        for (item in items)
        { 
        	// now instead of item being one of the actual item nodes it is a nodelist 
        	// of the actual nodes. 
        	println item.getClass() // is groovy.util.Node
        }
        

        Can you check the groovy version being used using GroovySystem.version?

        Show
        Roshan Dawrani added a comment - I tried on 1.6.7 / 1.6.8/1.7.1 snapshots and I don't see this issue. I see groovy.util.NodeList / groovy.util.Node classes and not ArrayList def xmlData = "<root><Items><Item>data1</Item><Item>data2</Item></Items></root>" def doc = new XmlParser().parseText(xmlData) def items = doc.Items.Item println items.getClass() // is groovy.util.NodeList for (item in items) { // now instead of item being one of the actual item nodes it is a nodelist // of the actual nodes. println item.getClass() // is groovy.util.Node } Can you check the groovy version being used using GroovySystem.version?
        Hide
        Guillaume Laforge added a comment -

        Could you be a bit more precise in your descriptiong of the problem?
        Could you show us some more code, so we understand exactly what's going on?

        Show
        Guillaume Laforge added a comment - Could you be a bit more precise in your descriptiong of the problem? Could you show us some more code, so we understand exactly what's going on?
        Hide
        Andrew Ressler added a comment -

        This was on the version of Groovy 1.6.3 that came with Grails 1.2. So perhaps it has been fixed?

        Show
        Andrew Ressler added a comment - This was on the version of Groovy 1.6.3 that came with Grails 1.2. So perhaps it has been fixed?
        Hide
        Michael Smolyak added a comment -

        We are in the process of upgrading from Grails 1.1.1 to 1.2 and are experiencing the same problem.

        The application processes this XML

        <Page number="1" width="612" height="792">
        <Content granularity="word" font="true" geometry="true">
        <Para>
        <Word>
        <Text>CLARKSVILLE</Text>
        <Box llx="45.00" lly="704.52" urx="139.69" ury="718.08">
        ...

        using this code

        def insertLineElements(pageXml)
        {
        pageXml.Content.Para.each()

        { insertLineIntoParent(it) }
        ...
        }

        This code used to work correctly: pageXml.Content.Para used to be a list (NodeList) of <Para> elements. The debugger now shows that it is an ArrayList of NodeLists. The ArrayList has a single element. So for the program to work, I had to change it to pageXml.Content.Para[0].each() { insertLineIntoParent(it) }

        (The program still does not work because there are many other places in the code with a similar problem - the parser went completely wild).

        It appears this problem is specific to Grails. We tried to replicate it by writing a similar Groovy program (using 1.6.7 version of Groovy used by Grails 1.2) and could not replicate it.

        Michael

        Show
        Michael Smolyak added a comment - We are in the process of upgrading from Grails 1.1.1 to 1.2 and are experiencing the same problem. The application processes this XML <Page number="1" width="612" height="792"> <Content granularity="word" font="true" geometry="true"> <Para> <Word> <Text>CLARKSVILLE</Text> <Box llx="45.00" lly="704.52" urx="139.69" ury="718.08"> ... using this code def insertLineElements(pageXml) { pageXml.Content.Para.each() { insertLineIntoParent(it) } ... } This code used to work correctly: pageXml.Content.Para used to be a list (NodeList) of <Para> elements. The debugger now shows that it is an ArrayList of NodeLists. The ArrayList has a single element. So for the program to work, I had to change it to pageXml.Content.Para [0] .each() { insertLineIntoParent(it) } (The program still does not work because there are many other places in the code with a similar problem - the parser went completely wild). It appears this problem is specific to Grails. We tried to replicate it by writing a similar Groovy program (using 1.6.7 version of Groovy used by Grails 1.2) and could not replicate it. Michael
        Hide
        Andrew Ressler added a comment -

        Hello Michael,

        Here's a little helper file I wrote. I wrap getNodeList around every call that has the problem.

        
        class XmlHelper {
        
        
          // Use this when using xpath to get around bug in grails 1.2
          static def getNodeList (nodelist)
          {
            if (nodelist.class == ArrayList)
            {
              return nodelist[0]
            }
            else
            {
              return nodelist
            }
          }
        
        
          static def getNodeName (node)
          {
            def n = node.name()
            if (n instanceof groovy.xml.QName)
            {
              return n.getLocalPart()
            }
            else
            {
              return n.toString()
            }
          }
        
        }
        
        Show
        Andrew Ressler added a comment - Hello Michael, Here's a little helper file I wrote. I wrap getNodeList around every call that has the problem. class XmlHelper { // Use this when using xpath to get around bug in grails 1.2 static def getNodeList (nodelist) { if (nodelist.class == ArrayList) { return nodelist[0] } else { return nodelist } } static def getNodeName (node) { def n = node.name() if (n instanceof groovy.xml.QName) { return n.getLocalPart() } else { return n.toString() } } }
        Hide
        Michael Smolyak added a comment -

        Andrew, thank you for your help. Changing the code to make it work correctly was not the a big problem. XmlParser gives you dozens of ways of accomplishing the same goal, so I managed to rewrite the code that it works.

        What concern me was that the upgrade to Grails 1.2 required me to make all those changes. I would like to understand why XmlParser is behaving differently in Grails 1.2 than it did in 1.1.1 from which I upgraded. Was my code incorrect and XmlParser is stricter about following certain conventions or was a bug introduced into the ecosystem?

        The issue I have mentioned was not the only one that required me to change the code. For example, where before I could write

        box.@llx

        had to be rewritten as

        box.'@llx'

        Were there changes in Groovy language that would cause such change in XmlParser behavior?

        Michael

        Show
        Michael Smolyak added a comment - Andrew, thank you for your help. Changing the code to make it work correctly was not the a big problem. XmlParser gives you dozens of ways of accomplishing the same goal, so I managed to rewrite the code that it works. What concern me was that the upgrade to Grails 1.2 required me to make all those changes. I would like to understand why XmlParser is behaving differently in Grails 1.2 than it did in 1.1.1 from which I upgraded. Was my code incorrect and XmlParser is stricter about following certain conventions or was a bug introduced into the ecosystem? The issue I have mentioned was not the only one that required me to change the code. For example, where before I could write box.@llx had to be rewritten as box.'@llx' Were there changes in Groovy language that would cause such change in XmlParser behavior? Michael
        Hide
        olivier FRESSE added a comment -

        This is a very strange issue

        I use XmlParser to update an XML file used within IzPack.
        basically, I have to update the src attribute of the following node,
        <res id="jlinkProe" src="./installer/shared_lib/xxxxx"/>

        I used the following code :

        The following examples can be tested in the grails console

        This code doesn't work
        
        
        xtext = """<?xml version="1.0" encoding="iso-8859-1" standalone="yes" ?>
        <!-- <!DOCTYPE installation SYSTEM "./installation.dtd"> -->
        
        <installation version="1.0">
        
        
            <resources>
                
                <res id="LicencePanel.licence" src="./installer/resources/Licence.txt"/>
                <res id="InfoPanel.info" src="./installer/resources/Readme.txt"/>
                <res id="ProcessPanel.Spec.xml" src="./installer/scripts/ProcessPanel.Spec.xml"/>
                <res id="userInputSpec.xml" src="./installer/scripts/userInputSpec.xml"/>
                <res id="pworker.war" src="./installer/staging/pworker.war"/>
                <res id="DSL" src="./installer/staging/DSL.zip"/>
        
        
                <res id="jlinkWraper" src="./installer/shared_lib/jlinkWrapper.jar"/>
                <res id="jlinkProe" src="./installer/shared_lib/xxxxx"/>
            </resources>
        
        </installation>"""
        
        
        
           installation = (new XmlParser()).parseText(xtext)
           println "gne ? : ${installation.resources.res.class}"
           proeNode = installation.resources.res.find({
           println "cherchenode ${it.'@id'}"
           println it.class
            it.'@id' == 'jlinkProe'
             })
             
          proeNode.'@src' = "./installer/shared_lib/tatayoyo"
           
          
           proeNode
           
        

        I made the following modifications to make it work.

        proeNode = installation.resources.res [0].find({

        proeNode. attributes['src'] = "./installer/shared_lib/tatayoyo"

        This is definitely a grails issue, as I can't reproduce it in pure groovy code.

        This code works
        
        
        xtext = """<?xml version="1.0" encoding="iso-8859-1" standalone="yes" ?>
        <!-- <!DOCTYPE installation SYSTEM "./installation.dtd"> -->
        
        <installation version="1.0">
        
        
            <resources>
                
                <res id="LicencePanel.licence" src="./installer/resources/Licence.txt"/>
                <res id="InfoPanel.info" src="./installer/resources/Readme.txt"/>
                <res id="ProcessPanel.Spec.xml" src="./installer/scripts/ProcessPanel.Spec.xml"/>
                <res id="userInputSpec.xml" src="./installer/scripts/userInputSpec.xml"/>
                <res id="pworker.war" src="./installer/staging/pworker.war"/>
                <res id="DSL" src="./installer/staging/DSL.zip"/>
        
        
                <res id="jlinkWraper" src="./installer/shared_lib/jlinkWrapper.jar"/>
                <res id="jlinkProe" src="./installer/shared_lib/xxxxx"/>
            </resources>
        
        </installation>"""
        
        
        
           installation = (new XmlParser()).parseText(xtext)
           println "class  : ${installation.resources.res.class}"
           proeNode = installation.resources.res[0].find({
           println "cherchenode ${it.'@id'}"
           println it.class
            it.'@id' == 'jlinkProe'
             })
             
          proeNode.attributes['src'] = "./installer/shared_lib/tatayoyo"
           
          
           proeNode
           
        
        Show
        olivier FRESSE added a comment - This is a very strange issue I use XmlParser to update an XML file used within IzPack. basically, I have to update the src attribute of the following node, <res id="jlinkProe" src="./installer/shared_lib/xxxxx"/> I used the following code : The following examples can be tested in the grails console This code doesn't work xtext = """<?xml version=" 1.0 " encoding=" iso-8859-1 " standalone=" yes" ?> <!-- <!DOCTYPE installation SYSTEM "./installation.dtd" > --> <installation version= "1.0" > <resources> <res id= "LicencePanel.licence" src= "./installer/resources/Licence.txt" /> <res id= "InfoPanel.info" src= "./installer/resources/Readme.txt" /> <res id= "ProcessPanel.Spec.xml" src= "./installer/scripts/ProcessPanel.Spec.xml" /> <res id= "userInputSpec.xml" src= "./installer/scripts/userInputSpec.xml" /> <res id= "pworker.war" src= "./installer/staging/pworker.war" /> <res id= "DSL" src= "./installer/staging/DSL.zip" /> <res id= "jlinkWraper" src= "./installer/shared_lib/jlinkWrapper.jar" /> <res id= "jlinkProe" src= "./installer/shared_lib/xxxxx" /> </resources> </installation>""" installation = ( new XmlParser()).parseText(xtext) println "gne ? : ${installation.resources.res.class}" proeNode = installation.resources.res.find({ println "cherchenode ${it.'@id'}" println it.class it.'@id' == 'jlinkProe' }) proeNode.'@src' = "./installer/shared_lib/tatayoyo" proeNode I made the following modifications to make it work. proeNode = installation.resources.res [0] .find({ proeNode. attributes ['src'] = "./installer/shared_lib/tatayoyo" This is definitely a grails issue, as I can't reproduce it in pure groovy code. This code works xtext = """<?xml version=" 1.0 " encoding=" iso-8859-1 " standalone=" yes" ?> <!-- <!DOCTYPE installation SYSTEM "./installation.dtd" > --> <installation version= "1.0" > <resources> <res id= "LicencePanel.licence" src= "./installer/resources/Licence.txt" /> <res id= "InfoPanel.info" src= "./installer/resources/Readme.txt" /> <res id= "ProcessPanel.Spec.xml" src= "./installer/scripts/ProcessPanel.Spec.xml" /> <res id= "userInputSpec.xml" src= "./installer/scripts/userInputSpec.xml" /> <res id= "pworker.war" src= "./installer/staging/pworker.war" /> <res id= "DSL" src= "./installer/staging/DSL.zip" /> <res id= "jlinkWraper" src= "./installer/shared_lib/jlinkWrapper.jar" /> <res id= "jlinkProe" src= "./installer/shared_lib/xxxxx" /> </resources> </installation>""" installation = ( new XmlParser()).parseText(xtext) println "class : ${installation.resources.res.class}" proeNode = installation.resources.res[0].find({ println "cherchenode ${it.'@id'}" println it.class it.'@id' == 'jlinkProe' }) proeNode.attributes['src'] = "./installer/shared_lib/tatayoyo" proeNode
        Hide
        Paul King added a comment -

        There doesn't seem to be a consensus as to whether this is a groovy or grails issue. In fact, it seems like we don't have a standalone groovy failing test case. I am thinking of closing the issue and we can re-open or create a new one if we can find an example of the problem again. Any objections?

        Show
        Paul King added a comment - There doesn't seem to be a consensus as to whether this is a groovy or grails issue. In fact, it seems like we don't have a standalone groovy failing test case. I am thinking of closing the issue and we can re-open or create a new one if we can find an example of the problem again. Any objections?
        Hide
        Paul King added a comment -

        No objections raised to closing. Please open a new issue with a standalone Groovy test case if you are still having problems.

        Show
        Paul King added a comment - No objections raised to closing. Please open a new issue with a standalone Groovy test case if you are still having problems.
        Paul King made changes -
        Field Original Value New Value
        Status Open [ 1 ] Closed [ 6 ]
        Assignee Paul King [ paulk ]
        Resolution Not A Bug [ 6 ]

          People

          • Assignee:
            Paul King
            Reporter:
            Andrew Ressler
          • Votes:
            2 Vote for this issue
            Watchers:
            5 Start watching this issue

            Dates

            • Created:
              Updated:
              Resolved: