Skeen
Skeen

Reputation: 4722

Issues with xpath in java

I'm currently having an issue with my xpath expressions in java. I'm trying to get a list of shopNames!

I got the following XML;

<?xml version="1.0" encoding="UTF-8"?>
<w:shops xmlns:w="namespace">
    <w:shop>
        <w:shopID>1</w:shopID>
        <w:shopName>ShopName</w:shopName>
        <w:shopURL>ShopUrl</w:shopURL>
    </w:shop>
    <w:shop>
        <w:shopID>2</w:shopID>
        <w:shopName>ShopNames</w:shopName>
        <w:shopURL>ShopUrl</w:shopURL>
    </w:shop>
</w:shops>

And I'm feeding this in a Document to a function alike this:

List<String> getShops(Document d)
    throws Exception
{
    List<String> shopnames = new ArrayList<String>();

    XPath xpath = XPathFactory.newInstance().newXPath();

    XPathExpression expr = xpath.compile("/descendant::w:shop/descendant::w:shopName");
    NodeList nodes = (NodeList) expr.evaluate(d, XPathConstants.NODESET);

    for(int x=0; x<nodes.getLength(); x++)
    {
        shopnames.add("" + nodes.item(x).getNodeValue());
    }
    return shopnames;
}

However the issue is that it simply returns an empty list, I'm suspecting it to be my xpath expression, but I'm not sure about it.

Anyone see the issue here?

Upvotes: 2

Views: 3544

Answers (4)

Daniel Kec
Daniel Kec

Reputation: 549

You don't have to specificate nscontext, your XPath expr will be little longer but will say everything alone:

/*[namespace-uri()='namespace'  and local-name()='shops']/*[namespace-uri()='namespace'  and local-name()='shop']/*[namespace-uri()='namespace'  and local-name()='shopName']

so in Java:

        XPathFactory factory = XPathFactory.newInstance();
        XPath xp = factory.newXPath();
        String xpath = 
        "/*[namespace-uri()='namespace'  and local-name()='shops']/*[namespace-uri()='namespace'  and local-name()='shop']/*[namespace-uri()='namespace'  and local-name()='shopName']";
        XPathExpression expr = xp.compile(xpath);
        NodeList nlist = (NodeList) expr.evaluate(e, XPathConstants.NODESET);
        ArrayList<String> shopNamesList = new ArrayList<String>();
        for (int i = 0; i < nlist.getLength(); i++) {
            shopNamesList.add(((Element) nlist.item(i)).getNodeValue());
        }

This should work. Regards

Upvotes: 1

Andreas Dolk
Andreas Dolk

Reputation: 114757

The root Element is not shop but shops. I think, you have to compile this expression:

xpath.compile("/descendant::w:shops/descendant::w:shop/descendant::w:shopName");

You may have to set a namespace context:

xpath.setNamespaceContext(new NamespaceContext() {

   public String getNamespaceURI(String prefix) {
    if (prefix.equals("w")) return "namespace";
    else return XMLConstants.NULL_NS_URI;
   }

   public String getPrefix(String namespace) {
    if (namespace.equals("namespace")) return "w";
    else return null;
   }

   public Iterator getPrefixes(String namespace) {return null;}

});

and parse so that the document is aware of namespaces

DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);  // <----
DocumentBuilder db = dbf.newDocumentBuilder();
Document xmlDom = db.parse("./shops.xml");

Upvotes: 4

proactif
proactif

Reputation: 11711

Don't you need to set a NamespaceContext on your XPath instance ? I think you have to so your 'w' ns is recognized.

Upvotes: 2

Augusto
Augusto

Reputation: 29827

This one also works: //w:shopName/text() is not as "selective", but I think it's more readable. And returns a list of strings, rather than a list of nodes, which might be better or not, depending what you need.

Upvotes: 2

Related Questions