The XML parsing primer

XML stands for Extensible Markup Language, and it is a standard that defines a set of rules to encode information created by the World Wide Web Consortium (W3C). The major advantage of XML is that it is both human and machine readable, which contributes to its success not only on the Web but also in game development.

We assume you have basic knowledge of XML, but take a look at the W3C introduction tutorial if this is the first time you have come across the standard. It is available at http://www.w3schools.com/xml/xml_whatis.asp.

XML's flexibility and hierarchical tree-oriented nature made many game studios use it to represent their data, such as configuration files, level layout, animation, and so on. Keeping the code separated from data makes it more reusable, allows rapid iteration through hot reloading, and lets nontechnical people tweak aspects of the game.

As you might have expected, Libgdx features its own XML parsing utilities, and we will show you how to leverage them in this recipe.

Getting ready

Import the sample projects into your Eclipse workspace.

How to do it…

An example of how to proceed can be found in the XMLParsingSample class, which parses the data/credits.xml file. This document contains nothing more than the credits for this book. Here, we will provide you with an insight into how to read arbitrary XML documents:

<Credits>
  <Book year="2014" pages="300" >Libgdx Game Development Cookbook</Book>
  <Publisher>PACKT Publishing</Publisher>
  <Authors>
    <Author>David Saltares</Author>
    <Author>Alberto Cejas</Author>
  </Authors>
  <Reviewers>
    <Reviewer>Manuel Palomo</Reviewer>
    <Reviewer>Simon Fleming</Reviewer>
    …
  </Reviewers>
</Credits>

To open and parse an XML file, you will need an instance of the XMLReader class and to call its parse() method. In the following example, we pass FileHandle through to the parser; however, it can take an input stream, a reader, or just a string:

XmlReader reader = new XmlReader();
Element root = reader.parse(Gdx.files.internal("data/credits.xml"));

Note

You might want to be careful here because if anything goes wrong during the parsing process, the parse() method will throw an IOException. Watch out for missing files or malformed XML.

The reader will return a reference to the tree's root element, which you can use to get the information you want. Once you have an element, retrieving its name is simple, as follows:

String elementName = root.getName();

You can also get the text that an element encloses using the following code:

String elementText = root.getText();

Elements can have attributes, which you can retrieve using their names. The following methods return the attribute identified by the given name or the contents of a child node with that tag name. When nothing is found, defaultValue is returned. The second parameter is optional in every case:

String get(String name, String defaultValue)
float getFloat(String name, float defaultValue)
boolean getBoolean(String name, boolean defaultValue)
int getInt(String name, int defaultValue)

Let's take a look at the following XML element:

<Book year="2014" pages="300" >Libgdx Game Development Cookbook</Book>

The getName() method will return Book, while the getText() method will give us Libgdx Game Development Cookbook; easy peasy! To get the year, we will intuitively call getInt("year").

Note

Pay special attention to files with special characters and make sure they are encoded in the UTF-8 format, which Libgdx is ready to handle without any problems.

Iterating over the children of an element is a simple enough job:

int numChildren = element.getChildrenCount();

for (int i = 0; i < numChildren; ++i) {
  Element child = element.getChild(i);
}

Sometimes, you might be interested in a child with a specific name; do not iterate over all the children because Libgdx can do it for you if you ask using the following functions:

Element getChildByName(String name) 
Element getChildByNameRecursive(String name)

This will return the first occurrence of a child or descendant (in the recursive version) that matches the given name. Oh, you say you want them all? If so, then use the following versions, which return an array of Element references:

Array<Element> getChildrenByName(String name) 
Array<Element> getChildrenByNameRecursively(String name)

These mechanisms will give you enough power to traverse a tree, fetching the data you need.

What about modifying an existing tree? You can set the text of an element as well as add or modify existing attributes with the following methods:

void setAttribute(String name, String value) 
void setText(String text)

Once you are done modifying the document tree, you will probably want to save it off to persistent memory. Such a task is as simple as grabbing a new file handle pointing to the file we want and writing the text representation of the tree. To obtain it, just call the toString() method on the root node. Optionally, you can pass the indent string (either a few spaces or a tab character):

FileHandle handle = Gdx.files.external("data.xml");
handle.writeString(root.toString('  '));

Note

Make sure you can write to the selected file handle. As mentioned in the The first approach to file handling in Libgdx recipe, not all file types on all platforms are writeable.

How it works…

XMLReader supports a subset of the full XML specification so as to be able to provide lightweight and faster document processing. This subset includes elements, attributes, text, CDATA (text that should not be parsed), and so on. Namespaces are simply considered part of the element or attribute name. Doctypes and schema declarations are completely ignored.

When it comes to games, performance and simplicity are usually preferred over correctness or sticking to strict standards.

There's more…

We covered how to parse an XML file, modify it, and save it to disk, but what about creating a new tree from scratch? This is when the XMLWriter class comes into play. First and foremost, you need to instantiate it:

StringWriter writer = new StringWriter();
XmlWriter xml = new XmlWriter(writer);

All operations on an XmlWriter object return a reference to the same instance, which allows chaining (as seen in the following code). To add a new element to the tree, we call the element() method. Every subsequent operation will be performed on the element; whenever we want to go back up one level in the hierarchy, we have to call pop(). Attributes can be added with the attribute() method.

Imagine that we want to recreate the following document:

<Credits>
  <Book year="2014" pages="300" >Libgdx Game Development Cookbook</Book>
</Credits>

Here is the code snippet that will achieve it:

xml.element("Credits")
  .element("Book", "Libgdx Game Development Cookbook")
    .attribute("year", "2014")
    .attribute("pages", "300")
  .pop()
.pop();

Piece of cake! To finally save the document off to disk, we can do the following:

FileHandle handle = Gdx.files.external("credits.xml");
handle.writeString(writer.toString());

See also

  • Some people love XML for its hierarchical structure and readability when compared with binary files. However, XML can sometimes be slightly verbose, in which case, you might be interested to know about JSON. Read more about this format in the JSON serialization and deserialization recipe.
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.16.79.65