1
votes

I'm using XPATH 1.0 to fetch some nodes from an XML document which uses a default namespace on the root and on nodes with attributes. The attributeFormDefault = "qualified" is set on the schema.

<Transaction xmlns="http://example.com/transaction">
    <someElement/>
    <collection>
        <value p0:Name="SomeName" xmlns:p0="http://example.com/transaction">some value</value>
        <value p1:Name="SomeOtherName" xmlns:p1="http://example.com/transaction">some other value</value>
    </collection>
    <differentCollection>
        <value p2:Name="SomeName" xmlns:p2="http://example.com/transaction">not this one</value>
    </differentCollection>
</Transaction>

My aim is to get the /Transaction/collection/value[@Name='SomeName'] node.

The only solution I've found so far seems a little broad:

//*[@*='SomeName']

I could also get the entire collection of <value> nodes by using:

//*[local-name()='value']

With the namespace restriction in mind, is there a cleaner, more precise way get the node I'm after? 3rd party javascript libraries are not an option.


EDIT

Thanks for the quick responses. I'll note here that by adding a default namespace prefix, solved the xpath query expressions:

<t:Transaction xmlns:t="http://example.com/transaction">

Now i can do:

/t:Transaction/t:collection/t:value[@t:Name='SomeName']
2

2 Answers

2
votes

You actually want to select the following:

/t:Transaction/t:collection/t:value[@t:Name = 'SomeName']

…given that you have declared t as the prefix for the "http://example.com/transaction" namespace prior to evaluating the XPath expression.

For the sample XML you show you could compress it to //t:value[@t:Name = 'SomeName'].

In any case, you do not get to ignore the namespaces in your input document. attributeFormDefault is a red herring in the issue.


How to define selection namespaces depends on the API you use to evaluate the XPath expression.

Browser-integrated APIs support a namespace resolver function that maps the prefixes used in the expression to the according namespace URIs. Further reading: MDN: Introduction to using XPath in JavaScript.

A simple resolver would look like this:

function nsResolver(prefix) {
    var ns = {
        t: "http://example.com/transaction",
    };
    return ns[prefix] || null;
}

Note that namespace prefixes in the XPath don't have to agree with namespace prefixes in the XML. The URIs they point to, however, must agree.

1
votes

Rather than attempting to skirt namespaces, follow best practices and bind a prefix to the namespace and then write your XPath using namespace prefixes:

  1. Bind p0 to http://example.com/transaction using the mechanism provided by your XPath processor.
  2. Then leverage the namespace prefix in the XPath expression:

    /p0:Transaction/p0:collection/p0:value[@p0:Name='SomeName']