Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java XML: get nested element

Tags:

java

xml

I'm programming a simple Java game and I wanted to save to player data to an XML encoded save file. Currently I'm discovering the Java XML DOM API and I have a short question.

Suppose I have the following XML file (doesn't make sense I know, but it is just an example)

<?xml version="1.0"?>
<savedGame>
    <name></name>
    <player>
        <name>YOU</name>
        <map>
                        <health>2000</health>
            <name>map_test.map</name>
            <position>
                <x>0</x>
                <y>0</y>
            </position>
        </map>
        <stats>
            <health>1000</health>
            <xp>
                <melee></melee>
                <magic></magic>
                <ranged></ranged>
                <agility></agility>
            </xp>
        </stats>
        <items>
            <item id="0">
                <amount></amount>
            </item>
        </items>
    </player>
</savedGame>

Now I want to get the players health level (note that there is an other health element in the map section). This can be done with the following code:

String hp = ((Element) ((Element) savedGameDocument.getElementsByTagName("player").item(0)).getElementsByTagName("stats").item(0)).getElementsByTagName("health").item(0).getTextContent()

It works, yes. But it looks very nasty to me. Is this the usual way for getting those nested elements? And why isn't there an ElementList class? Now I have to cast every Node.

Thanks in advance, Jori.

like image 718
Jori Avatar asked May 10 '13 10:05

Jori


2 Answers

You can use the javax.xml.xpath APIs included on the JDK/JRE to easily get data from your do document.

import javax.xml.xpath.*;
import org.xml.sax.InputSource;

public class Demo {

    public static void main(String[] args) throws Exception {
        XPathFactory xpf = XPathFactory.newInstance();
        XPath xpath = xpf.newXPath();

        InputSource xml = new InputSource("src/forum16479976/input.xml");
        String health = (String) xpath.evaluate("/savedGame/player/stats/health", xml, XPathConstants.STRING);
        System.out.println(health);
    }

}

These APIs also work accessing data from a DOM

import javax.xml.parsers.*;
import javax.xml.xpath.*;
import org.w3c.dom.Document;

public class Demo {

    public static void main(String[] args) throws Exception {
        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        DocumentBuilder db = dbf.newDocumentBuilder();
        Document xml = db.parse("src/forum16479976/input.xml");

        XPathFactory xpf = XPathFactory.newInstance();
        XPath xpath = xpf.newXPath();
        String health = (String) xpath.evaluate("/savedGame/player/stats/health", xml, XPathConstants.STRING);
        System.out.println(health);
    }

}
like image 165
bdoughan Avatar answered Oct 17 '22 05:10

bdoughan


If you do this a lot, you can let a simple serializer handle this. You write Objects and read objects (like player etc), serializer takes care of the XML part. While you focus on game development.

As of manually parsing an XML from DOM, the logic you are already using can't be made smaller anymore. That is because, DOM provides very very basic methods of navigation : listing child nodes and picking one of them, repeat until reach the required node. This may be good for lightweight XML, but soon turns into a nightmare when you have parse intricate structures with many tag levels.

Another faster way is streaming, still, the more complicated XML structure gets , so does the parser code.

XPATH is also great if you just need to query the XML for just a particular node. But if you need full xml to objects transforms, use a serializer.

like image 31
S.D. Avatar answered Oct 17 '22 07:10

S.D.