6

The namespace agnostic syntax I've seen around is confusing me.

Say I have:

<root>
  <parent attribute="A">A<child>A</child></parent>
  <parent attribute="B">B<child>B</child></parent>
</root>

So far I see how:

/root/parent/child/text()

translates to:

/*[local-name()='root']/*[local-name()='parent']/*[local-name()='child']/text()

but i'm struggling with things like this:

/root/parent[@attribute="A"]/child/text()

or:

/root/parent[text()="B"]/child/text()

or:

/root/parent[1]/child/text()

How do these translate?

Thanks,

EDIT: One More :-)

<root>
        <parent>
            <childName>serverName</childName>
            <childValue>MyServer</childValue>
        </parent>
        <parent>
            <childName>ServerLocation</childName>
            <childValue>Somewhere</childValue>
         </parent>
</root>

How does this translate?

/root/parent[childName="serverName"]/childValue/text()
pnuts
  • 58,317
  • 11
  • 87
  • 139
Andy Arismendi
  • 50,577
  • 16
  • 107
  • 124

2 Answers2

10

The namespace agnostic syntax I've seen around is confusing me.

First, I would advise you not to use this syntax, especially if it is confusing. It can also result in errors -- see the end of my answer for details.

The standard way to specify in an XPath expression names that are in a namespace is to register a namespace with your XPath engine (see the respective, vendor-specific documentation) and then to use the prefix bound to the registered namespace (say "x") with names like x:someName

There are plenty of good answers on this topic -- jus t use one of them.

Now, if due to some reason you still decide to use the confusing syntax, then:

but i'm struggling with things like this:

/root/parent[@attribute="A"]/child/text()

Use:

/*[local-name()='root']/*[local-name()='parent' and @attribute='A']

then:

or:

/root/parent[text()="B"]/child/text()

Use:

/*[local-name()='root']/*[local-name()='parent' and text()='B']
                                    /*[local-name()='child']/text()

then:

or:

/root/parent[1]/child/text()

Use:

/*[local-name()='root']/*[local-name()='parent'][1]
                                 /*[local-name()='child']/text()

then:

One More :-)

<root>
  <parent>
      <childName>serverName</childName>
      <childValue>MyServer</childValue>
  </parent>
  <parent>
      <childName>ServerLocation</childName>
      <childValue>Somewhere</childValue>
  </parent>
</root>

How does this translate?

/root/parent[childName="serverName"]/childValue/text()

Use:

/*[local-name()='root']
      /*[local-name()='parent'][*[local-name()='childName"]='serverName']
                                           /*[local-name()='childValue']/text()

Do note:

Such expressions may not select the wanted nodes if in the XML documents there are elements with the same local-name that belong to two different namespaces.

Andy Arismendi
  • 50,577
  • 16
  • 107
  • 124
Dimitre Novatchev
  • 240,661
  • 26
  • 293
  • 431
  • Thanks Dimitre, consider your warning heeded :-) Unfortunately i'm forced to use this type of xpath because I need to use it in InstallShield in the system search feature. Unfortunately the feature provides no way to define a namespace. The XML files I'm searching only have 1 namespace so I I'm safe. BTW they are Tomcat web.xml configuration files. Thanks! – Andy Arismendi Feb 10 '11 at 14:07
  • @antize: I am glad that my answer was helpful. Here at SO the established way of expressing gratitude is by accepting an answer (to do so, just click on the check-mark next to the answer) :). – Dimitre Novatchev Feb 10 '11 at 14:51
  • Ah sorry, didn't think I could check the answered check box for multiple answers. Dimitre, I made an edit to my original question asking about one more scenario. Could you help me out with that one? Thanks! – Andy Arismendi Feb 10 '11 at 16:08
  • @antize: You are welcome. I added to my answer the solution of your last translation request. – Dimitre Novatchev Feb 10 '11 at 16:33
4

I understand your question to mean, how do I make these XPath expressions namespace-agnostic? (It's not a special syntax, just a typical use of the local-name() function.)

/root/parent[@attribute="A"]/child/text()

would become

/*[local-name()='root']/*[local-name()='parent'][@attribute='A']/*[local-name()='child']/text()

(You could use double-quotes for the attribute value if you want, but that would make it harder to embed in XSLT or whatever your environment is.)

/root/parent[text()="B"]/child/text()

would become

/*[local-name()='root']/*[local-name()='parent'][text() = 'B']/*[local-name()='child']/text()

And

/root/parent[1]/child/text()

would become

/*[local-name()='root']/*[local-name()='parent'][1]/*[local-name()='child']/text()
LarsH
  • 27,481
  • 8
  • 94
  • 152
  • Long no see :) Where have you been? I think this might be interesting to you: http://dnovatchev.wordpress.com/2011/02/08/the-binary-search-tree-data-structurehaving-fun-with-xpath-3-0/ – Dimitre Novatchev Feb 10 '11 at 05:07
  • Thanks LarsH, that seems simple enough, since there were already square braces that filtered the results that came back from "*" I was unsure how an addition filter would look. Thanks, I hope this helps others as well! – Andy Arismendi Feb 10 '11 at 14:09
  • @antize: yep... although as @Dimitre showed, you can also filter the results by using `and ...` within the same predicate. – LarsH Feb 11 '11 at 22:05