7

I have the following XML file:

<foo:a xmlns:foo=\"http://www.foo.com\">
    <foo:b foo:bar=\"zar\">
    </foo:b>
</foo:a>

To get the b nodes with an attribute bar having the value "zar" (all in the correct namespace), I can use the XPath expression:

/foo:a/foo:b[@foo:bar=\"zar\"]

(with "foo" properly bound to "http://www.foo.com" - see code at the end)

However, when I want to do the same in a namespace-unaware way, though I can remove the namespace from the elements by relying on the local-name() function, I am not able to remove them from the attribute.

This is the best I can come up with:

/*[local-name()='a']/*[local-name()='b' and @foo:bar=\"zar\"]

(where foo is, regrettably, used to qualify the bar attribute).

How can I write the above expression with namespaces completely removed?

Below code has been tested both with and without Saxon-HE-9.4.jar on the CLASSPATH and produces correct results, but I can't get the 'foo' namespace prefix out of the second XPath expression!

import java.io.*;
import java.util.*;
import javax.xml.xpath.*;
import javax.xml.parsers.*;
import org.w3c.dom.*;
import javax.xml.namespace.NamespaceContext;

public class FooMain {

    public static void main(String args[]) throws Exception {

        String xmlSample = "<foo:a xmlns:foo=\"http://www.foo.com\"><foo:b foo:bar=\"zar\"></foo:b></foo:a>";
        XPath xpath = namespaceAwareXpath("foo", "http://www.foo.com");
        {
            System.out.printf("[NS-aware           ] Number of 'b' nodes with foo:bar attribute having the value \"zar\" is: %d\n", 
                              ((NodeList) xpath.compile("/foo:a/foo:b[@foo:bar=\"zar\"]").evaluate(stringToXML(xmlSample, true),
                               XPathConstants.NODESET)).getLength());
        }
        {
            System.out.printf("[NS-aware but using local-name() ] Number of 'b' nodes with foo:bar attribute having the value \"zar\" is: %d\n", 
                              ((NodeList) xpath.compile("/*[local-name()='a']/*[local-name()='b' and @foo:bar=\"zar\"]").evaluate(stringToXML(xmlSample, true),
                               XPathConstants.NODESET)).getLength());
        }
    }


    public static XPath namespaceAwareXpath(final String prefix, final String nsURI) {
        XPathFactory xPathfactory = XPathFactory.newInstance();
        XPath xpath = xPathfactory.newXPath();
        NamespaceContext ctx = new NamespaceContext() {
                @Override
                public String getNamespaceURI(String aPrefix) {
                    if (aPrefix.equals(prefix))
                        return nsURI;
                    else
                        return null;
                }
                @Override
                public Iterator getPrefixes(String val) {
                    throw new UnsupportedOperationException();
                }
                @Override
                public String getPrefix(String uri) {
                    throw new UnsupportedOperationException();
                }
            };
        xpath.setNamespaceContext(ctx);
        return xpath;
    }    

    private static Document stringToXML(String s, boolean nsAware) throws Exception {
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        factory.setNamespaceAware(nsAware);
        DocumentBuilder builder = factory.newDocumentBuilder();
        return builder.parse(new ByteArrayInputStream(s.getBytes("UTF-8")));
    }


}
Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
Marcus Junius Brutus
  • 26,087
  • 41
  • 189
  • 331
  • @JensErat seen that, it's not exactly the same. It only selects based on attribute name, here I am trying to select based on the value of that attribute. Said attribute to be indicated in a namespace-unaware way. – Marcus Junius Brutus Jan 20 '14 at 20:37
  • You're trying to address an attribute an attribute in a namespace-unaware way, the other question does, too. All difference is between the other author is querying for existence, you will want to compare it with another value. – Jens Erat Jan 20 '14 at 22:55

1 Answers1

19
/*[local-name()='a']/*[local-name()='b' and @*[local-name() = 'bar']=\"zar\"]
Marcus Junius Brutus
  • 26,087
  • 41
  • 189
  • 331