codd
codd

Reputation: 612

XPATH in Java: parent node missing

I have the following XML structure:

<map name="testmap">
    <definitions>
        <tile name="ground"> <!-- a normal tile that has no special obstacles -->
            <centralObstacle>ground</centralObstacle>
            <neighbourObstacles>
                <north></north>
                <east></east>
                <south></south>
                <west></west>
            </neighbourObstacles>
        </tile>
        <tile name="wallE"> <!-- a ground tile with a wall obstacle at the east-->
            <centralObstacle>ground</centralObstacle>
            <neighbourObstacles>
                <north></north>
                <east>wall</east>
                <south></south>
                <west></west>
            </neighbourObstacles>
        </tile>
    </definitions>
</map>

And I would like to query it with XPATH. What I would like to do is get all the tile Nodes and then iterate over them to get all their names and other relevant information (using different XPATH queries).

Because XPATH expressions are to be ran on a Document, I have used the following nodeListToDoc() function, provided in this answer to convert the result of an XPATH query (a NodeList) to a Document. This way I can first get all the Tiles, then iterate over them to get Tile specific information.

private Document nodeListToDoc(NodeList nodes) throws ParserConfigurationException  
{
    Document newXmlDocument = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
    Element root = newXmlDocument.createElement("root");
    newXmlDocument.appendChild(root);
    for (int i = 0; i < nodes.getLength(); i++) {
        Node node = nodes.item(i);
        Node copyNode = newXmlDocument.importNode(node, true);
        root.appendChild(copyNode);
    }
    return newXmlDocument;      
}

What I first do is parse the file to a Document and then run the query to get a NodeList that contains all my Tiles. When I run the query //definitions/tile I get a NodeList containing two Node items (I have verified this), which is correct. The result of applying nodeListToDoc() looks like this.

 <?xml version="1.0" encoding="UTF-16"?>
<root><tile name="ground"> <!-- a normal tile that has no special obstacles -->
            <centralObstacle>ground</centralObstacle>
            <neighbourObstacles>
                <north/>
                <east/>
                <south/>
                <west/>
            </neighbourObstacles>
        </tile><tile name="wallE"> <!-- a ground tile with a wall obstacle at the east-->
            <centralObstacle>ground</centralObstacle>
            <neighbourObstacles>
                <north/>
                <east>wall</east>
                <south/>
                <west/>
            </neighbourObstacles>
        </tile></root>

So far so good. Now things go bad. I want to iterate over the two nodes, make a NodeList of them, convert that NodeList to a Document and then run some queries over them. One of the queries is get the name of every tile. I thought the following code piece would do the trick:

  for (int i = 0; i < nodes.getLength(); i++) { // iterate over the two nodes
            NodeList tile = (NodeList) nodes.item(i); // create a nodelist containing only the first node
            Document attrdoc = nodeListToDoc(tile); // convert it to a document
}

However, when I print the resulting tree that attrdoc represents, I get, for the first iteration, the following result:

<?xml version="1.0" encoding="UTF-16"?>
<root> <!-- a normal tile that has no special obstacles -->
            <centralObstacle>ground</centralObstacle>
            <neighbourObstacles>
                <north/>
                <east/>
                <south/>
                <west/>
            </neighbourObstacles>
        </root>

This is incorrect. The child of the root element should be tile? Where did this element go?

Upvotes: 0

Views: 522

Answers (2)

Michael Kay
Michael Kay

Reputation: 163595

You don't really explain what you are trying to achieve, but your description really makes me wonder whether Java+XPath is the right tool for the job. Have you looked at doing it in XQuery or XSLT?

Upvotes: 1

David Gorsline
David Gorsline

Reputation: 5018

+1 to @Andy's comment. As I read your question, it doesn't seem to me that you really want to make a new document; rather, you're just using it as a means to extract information from the existing XML.

So your approach is to access the information directly from the nodes. For instance, in your passage where you iterate over the two nodes, you can do something like this:

for (int i = 0; i < nodes.getLength(); i++) { // iterate over the two nodes
    NodeList node = nodes.item(i);
    if (node.getNodeType() == ELEMENT_NODE) {
        Element element = (Element) node;
        //from here, you can access element.getNodeValue(), element.getChildNodes(), etc.
    }    
}

Furthermore, you can go back to newXmlDocument and apply multiple XPath queries to it. It's not one-and-done, as it would be if you were using a SAX parser.

Upvotes: 0

Related Questions