2

Let's say I have the following XML at hand

<Envelope>
  <Body>
    <analyzeEffectOfReplaceOfferResponse>
      <productOfferings>
        <productOffering>
          <id>some value</id>
        </productOffering>
      </productOfferings>
    </analyzeEffectOfReplaceOfferResponse>
  </Body>
</Envelope>

I also have the input XML in form of GPathResult after retrieving the file and parsing it:

inputXML = new XmlSlurper().parse(inputFile)

when I try to find the node like this:

inputXML."Body"."analyzeEffectOfReplaceOfferResponse"."productOfferings"."productOffering".depthFirst().findAll {it.value}

I get the required child "id"

however if I use a string that holds this text:

"Body"."analyzeEffectOfReplaceOfferResponse"."productOfferings"."productOffering"

and use it like so:

inputXML."${xPath}".depthFirst().findAll {it.value}

It doesn't work... what am I doing wrong?

Yossi Even
  • 45
  • 8

3 Answers3

3

Possible to use Eval.me:

def inputXML = new XmlSlurper().parseText( '''<Envelope>
  <Body>
    <analyzeEffectOfReplaceOfferResponse>
      <productOfferings>
        <productOffering>
          <id>some value</id>
        </productOffering>
      </productOfferings>
    </analyzeEffectOfReplaceOfferResponse>
  </Body>
</Envelope>''')

println inputXML."Body"."analyzeEffectOfReplaceOfferResponse"."productOfferings"."productOffering".depthFirst().findAll {it.value}

//use Eval.me groovy code evaluation
def gpath = ' XML."Body"."analyzeEffectOfReplaceOfferResponse"."productOfferings"."productOffering" '
println Eval.me( 'XML', inputXML, gpath ).depthFirst().findAll {it.value}

//or even like this:
gpath = ' XML."Body"."analyzeEffectOfReplaceOfferResponse"."productOfferings"."productOffering".depthFirst().findAll {it.value} '
println Eval.me( 'XML', inputXML, gpath )
daggett
  • 26,404
  • 3
  • 40
  • 56
1

In your current attempt, Groovy is calling the getProperty method on the inputXML object with argument "Body"."analyzeEffectOfReplaceOfferResponse"."productOfferings"."productOffering".

Since there is no XML child element with that specific name, nothing is found.

Instead, your aim is to dynamically chain the calls along the lines of this:

inputXML.getProperty('Body')
        .getProperty('analyzeEffectOfReplaceOfferResponse')
        .getProperty('productOfferings')
        .getProperty('productOffering')
        .depthFirst().findAll {it.value}

You could do this by creating a method that recursively creates this chaining, e.g.

def xPath = '"Body"."analyzeEffectOfReplaceOfferResponse"."productOfferings"."productOffering"'

def getProperties(gpathResult, dotProp) {
    def props = dotProp?.split(/\./)
    props.length <= 1 ? gpathResult : getProperties(gpathResult[props.head() - '"' - '"'], (props.tail().join('.')))
}

getProperties(inputXML, xPath).depthFirst().findAll {it.value}

Or you could use a full-fledged XPath library.

bdkosher
  • 5,753
  • 2
  • 33
  • 40
  • I am curious, though - this gets me a collection of strings eventually. what if I want to get the actual nodes in the XML so I can update them? – Yossi Even Feb 26 '18 at 12:54
  • Your current code gets back a collection of `groovy.util.slurpersupport.NodeChildren` representing the productOffering node and its parents. – bdkosher Feb 26 '18 at 16:25
  • Interesting approach, but would only work with the simplest `gpath` expressions, i.e. it wouldn't work with e.g. `A.B[1].C` or similar, would it? – Tomislav Nakic-Alfirevic May 14 '20 at 20:27
0

Do you have to use a string or are you just looking for a way to store a path to an element so that you don't have to type it again later?

String xml = '<Envelope><Body><analyzeEffectOfReplaceOfferResponse><productOfferings><productOffering><id>some value</id></productOffering></productOfferings></analyzeEffectOfReplaceOfferResponse></Body></Envelope>'
def Envelope = new XmlSlurper().parseText(xml)
Closure takeMyPathFrom = {node -> node."Body"."analyzeEffectOfReplaceOfferResponse"."productOfferings"."productOffering"}
assert takeMyPathFrom(Envelope) == "some value"
Jeremy Hunt
  • 701
  • 3
  • 10