Entities-Parts III: Serialization

Published June 03, 2014 by David Chen, posted by dave19067
Do you see issues with this article? Let us know.
Advertisement

Background

Download RPG Battle Example and Java version of Entities-Parts Framework
Download C++ version of Entities-Parts Framework

I. Game Objects
II. Interactions
III. Serialization (current)

The previous articles focused on entity structure and interaction. In the previous version of the RPG Battle Example, all of the code that defined the entity attributes was in the CharacterFactory. For example, it contained code to set the health of the meleer character to 200 and add spells to the support mage. We now want to move entity data out of the code and into data files. By storing the entity data in files, we can conveniently modify them without compiling the code. In addition, the data files could be reused if the code was ported to another language. This will be the last article in the series and covers serialization.

For the purposes of this article, I chose XML and JAXB. If you aren't familiar with these technologies, I recommend googling about them as the article revolves heavily around them. Note that JAXB library refers to conversion between objects and data as marshalling, but this article will use the term serialization.

The advantages of XML are that it is a popular way to store data and is human-readable. JAXB is a powerful XML serialization framework packaged with Java EE 6 that uses annotations to mark serializable classes and fields. Using the annotations as hints, JAXB automatically de/serializes class instances and does much of the grunt work for us. The main drawback of JAXB is that it is slower to serialize/deserialize data compared to binary serialization frameworks such as Kryo and Java Serialization. In 1,000,000 runs of a serialization perfomance test, JAXB unmarshalling (a.k.a deserialization) took 249972ms, while Kryo took only 2557ms (Performance Comparison).

There are many viable ways to serialize/deserialize entities so I decoupled serialization code from general-purpose classes such as Entity and Part. This makes the code easy to modify if you want to switch to another serialization framework. Even if you decide to use another serialization framework, I hope this article gives you an idea of what issues or general approaches are associated with data serialization.

RPG Battle Example (continued)

The top of the article contains the download link for the RPG Battle Example. The RPG Battle Example has been updated to use JAXB serialization to load entities from files. The serialized files of the character entities are stored in the relative project path "data/characters/". Through the help of a program I created called EntityMaker.java, I used the old character factory, now renamed to CharacterFactory_Old, to serialize the entities to XML files. The following is the "meleer.xml" file:

<?xml version="1.0" encoding="UTF-8"?>
<entity>
  <parts>
    <part xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="manaPart">
      <maxMana>0.0</maxMana>
      <mana>0.0</mana>
    </part>
    <part xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="restorePart">
      <healthRestoreRate>0.01</healthRestoreRate>
      <manaRestoreRate>0.03</manaRestoreRate>
    </part>
    <part xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="healthPart">
      <maxHealth>200.0</maxHealth>
    </part>
    <part xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="equipmentPart">
      <weapon>
        <name>Sword</name>
        <minDamage>25.0</minDamage>
        <maxDamage>50.0</maxDamage>
        <attackRange>CLOSE</attackRange>
      </weapon>
      <spells/>
    </part>
    <part xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="descriptionPart">
      <name></name>
    </part>
    <part xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="alliancePart">
      <alliance>MONSTERS</alliance>
    </part>
    <part xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="mentalityPart">
      <mentality>OFFENSIVE</mentality>
    </part>
  </parts>
</entity>

The XML contains elements that represent the entity and the individual parts. Notice that not all variables are stored. For example, the Entity class has variables isInitialized and isActive that don't appear in the file above. The values for these variables can be determined at runtime so they don't need to be stored.

The attributes xmlns:xsi and xsi:type are needed by JAXB to deserialize the data to the necessary type. As you might imagine, it is very convenient to edit entities on the fly without compiling the whole program again. The human-readable XML format allows us to easily change entity behavior by updating existing part elements or add new part elements, e.g. a FlyingPart element to the "meleer.xml" file. The CharacterFactory from part II has been refactored to contain only one method instead of several methods to create each character.

The path to the XML file containing the serialized Entity is passed into the createCharacter method which converts the file to an Entity. XmlUtils is a helper class I created that serializes/deserializes between XML and Java objects. I will describe what the arguments to the read method represent later on in the article.

public class CharacterFactory {
	
  /**
   * Creates an character entity from a file path.
   * @param path path to the serialized character definition
   * @param name
   * @param alliance
   * @return new character
   */
  public static Entity createCharacter(String path, String name, Alliance alliance) {
    Entity character = XmlUtils.read(Paths.CHARACTERS + path, new EntityAdapter(), Bindings.BOUND_CLASSES, "bindings.xml");
    character.get(DescriptionPart.class).setName(name);
    character.get(AlliancePart.class).setAlliance(alliance);
    return character;
  }
	
}

In order to make a class recognized by JAXB for serialization, we add annotations such as @XmlRootElement and @XmlElement to the class. For example, the following classes EquipmentPart and SummonSpell contain annotations:

@XmlRootElement
public class EquipmentPart extends Part {

  @XmlElement
  private Weapon weapon;
  @XmlElementWrapper
  @XmlElement(name = "spell")
  private List<Spell> spells;
    ...
    
@XmlRootElement
public class SummonSpell extends Spell {

  @XmlJavaTypeAdapter(EntityAdapter.class)
  @XmlElement
  private Entity summon;
  ...

In case you don't know already, here are what the annotations mean:

@XmlRootElement - Creates a root element for this class.

@XmlAccessorType(XmlAccessType.NONE) - Defines whether properties, fields, or neither should be automatically serialized. The XmlAccessType.NONE argument means that by default, variables and properties will not be serialized unless they have the @XmlElement annotation.

@XmlElement(name = "spell") - This annotation defines fields or properties that should be serialized. The argument name = "spell" says that each Spell object in the list of spells should be wrapped in the tags.

@XmlElementWrapper - This wraps all of the individual elements in a tags.

@XmlJavaTypeAdapter(EntityAdapter.class) - The Entity field will be serialized and deserialized using the specified XML adapter passed in as the argument.

Obstacles

Ideally, it'd be nice to add annotations to our classes and just let our serialization framework do the rest of the work without any more effort from us. But often there are obstacles with serialization, such as classes that we don't want to or can't add annotations to. The following sections describe solutions for these issues and may be a little confusing because it goes into more advanced usage of JAXB: XML Adapters and Bindings.

XML Adapters

Since the classes Entity and Part can be reused in multiple games, we want to avoid adding JAXB annotations to these classes or modifying them to fit a specific purpose such as serialization. However, de/serializing unmodifiable classes requires some workarounds which I'll describe.

The first step to making Entity serializable is creating an XmlAdapter to convert Entity to a serializable class. We add two new classes, the serializable class EntityAdapted and the adapter EntityAdapter which is derived from the JAXB class XmlAdapter.

The EntityAdapted class contains the fields from Entity that need to be serialized such as parts and contains JAXB annotations. The EntityAdapter class converts between the unserializable form, Entity, and the serializable form, EntityAdapted. EntityAdapter is referenced in SummonSpell because SummonSpell contains a reference to an Entity and is also used in the CharacterFactory.createCharacter method.

@XmlRootElement(name = "entity")
public class EntityAdapted {

  @XmlElementWrapper
  @XmlElement(name = "part")
  private List<Part> parts;

  public EntityAdapted() {
  }

  public EntityAdapted(List<Part> parts) {
    this.parts = parts;
  }

  public List<Part> getParts() {
    return new ArrayList<Part>(parts);
  }
	
}
public class EntityAdapter extends XmlAdapter<EntityAdapted, Entity> {

  @Override
  public EntityAdapted marshal(Entity entity) throws Exception {
    EntityAdapted entityAdapted = new EntityAdapted(entity.getAll());
    return entityAdapted;
  }

  @Override
  public Entity unmarshal(EntityAdapted entityAdapted) throws Exception {
    Entity entity = new Entity();
    for (Part part : entityAdapted.getParts()) {
      entity.attach(part);
    }
    return entity;
  }

}

Bindings

We would like to add the @XmlTransient annotation to Part because we don't want to store any fields in that class. There is a way to add JAXB annotations to a class without modifying the class. If you noticed, "eclipselink.jar" was added to the project. This is a 3rd party library that allows JAXB annotations to be added to unmodifiable classes by defining the annotations in an XML file. This is what the bindings.xml file looks like and you'll notice that it contains an element to make Part xml-transient.

<?xml version="1.0"?>
<xml-bindings xmlns="http://www.eclipse.org/eclipselink/xsds/persistence/oxm" package-name="entitypart.epf">
    <java-types>
      <java-type name="Part" xml-transient="true"/>
    </java-types>
</xml-bindings>

When serializing a list of an abstract type, e.g. the parts in the EntityAdapted class, the serializer needs to know what subtypes of Part could exist in the list. As you saw in the createCharacter method of the CharacterFactory, you'll see that Bindings.BOUND_CLASSES is passed in as an argument to XmlUtils.read. This static list contains the classes that JAXB needs to know in order to serialize the list of parts with the data in the subclasses of Part.

public class Bindings {

  /**
   * Required for serializing list of base types to derived types, e.g. when a list of parts is serialized, binding 
   * the health part class to the serialization will allow health parts in the list to be serialized correctly.
   */
  public static Class<?>[] BOUND_CLASSES = new Class<?>[] {
    HealSpell.class, 
    SummonSpell.class, 
    AlliancePart.class, 
    DescriptionPart.class, 
    EquipmentPart.class, 
    FlyingPart.class, 
    HealthPart.class, 
    ManaPart.class, 
    MentalityPart.class, 
    RestorePart.class, 
    TimedDeathPart.class
  };

}

In the entityparts.parts package, there is a file called "jaxb.properties". This file must be added to a package of any class included in BOUND_CLASSES above. See JAXBContext for more information.

Final Notes

The article described the basics of using JAXB to serialize entities and parts. Also, some of the more advanced features of JAXB such as XMLAdapter were used to overcome obstacles such as unmodifiable classes.

In addition to JAXB, I recommend taking a look at these serialization frameworks:

SimpleXML (Java) - An easy-to-use, lightweight alternative to JAXB. If you're developing an Android app, I recommend this over JAXB. Otherwise, you need to include the 9 megabyte JAXB .jar with your app (see JAXB and Android Issue). The SimpleXML .jar file is much smaller, weighing in at less than 400kb.

I haven't used any of these libraries, but they are the most recommended from what I've researched:

JSONP (Java) - JSON is a human-readable format that also holds some advantages over XML such as having leaner syntax. There is currently no native JSON support in Java EE 6, but this library will be included in Java EE 7.

Kryo (Java) - According to the performance comparison (Performance Comparison), it is much faster than JAXB. I'll probably use it in a future project. The downside is it doesn't produce human-readable files, so you can't edit them in a text editor.

Protobuffer (C++) - A highly recommended serialization framework for C++ developed by Google.

Article Update Log

25 May 2014: Initial draft.

Cancel Save
0 Likes 6 Comments

Comments

Tjitte de Visscher

Hi, first of all, good article! :)

One thing though:
I really wonder why the choice for XML has been made?

Wouldn't JSON be better in this particular situation, because you are describing objects while XML is more useful for layout purposes? I'm not criticizing but just wondering...

May 25, 2014 06:44 PM
dave19067

Hi DeVisscha,

I was actually considering JSON for this article. JSON syntax is leaner and would fit this case well. The only reasons I didn't go with JSON is because I have little experience with it and would require importing another 3rd party library because there isn't any support for it in Java EE 6.

May 25, 2014 10:52 PM
rogue6

I agree DeVisscha, XML should die a slow death. JSON is a much better format for a number of reasons. Primarily, I believe it is more readable and more easily editable, due mainly to the lack of need of a terminating tag. Secondly, JSON has a simpler structure (just arrays and dictionaries), whereas XML can store information in two places: attributes and in between tags. This can lead to inconsistencies in data representation design. Finally, JSON is a LOT easier to process and parse.

May 27, 2014 11:24 AM
swiftcoder

Let's keep format wars out of the article discussion, yes? It doesn't really have a strong bearing on the topic of the article itself (XML or JSON both work similarly as far as serialisation is concerned).

June 02, 2014 01:00 PM
Ravyne

I agree DeVisscha, XML should die a slow death. JSON is a much better format ...

I won't contribute to the format war, but I want to point out that XML and JSON/YAML are not interchangeable. For simple serialization like this, JSON is a valid choice but understand its limitations. XML is a truly structured document, with an ecosystem of tools to define, validate and transform those structures. JSON or YAML are not structured in any real sense of the word, the content and "shape" of a JSON format is defined and implemented by convention alone, and loosely validated only by a reference implementation, if one exists. Sometimes a looser structure is a benefit, other times it is a hindrance. It depends entirely on your needs.

I would also disagree that the presence of both attributes and tags in XML is inherently flawed -- but I would concede that incorrectly choosing one instead of the other is a source of problems and confusion. Usually this is the case when people don't understand the differences and simply choose the option that they think looks best in their text editor without understanding the consequences. That's like a programmer choosing between composition and inheritance based solely on their stylistic whims. The problem is the programmer, not the tool.

June 02, 2014 08:14 PM
nonchiedercilaparola

hi, it's an old article but from my perspective is very interesting. Is there a way to see the full base code that google links is broken?

February 04, 2024 12:18 PM
You must log in to join the conversation.
Don't have a GameDev.net account? Sign up!

Building on the RPG Battle Example, this article will describe how to load entities from XML files using the JAXB serialization framework.

Advertisement

Other Tutorials by dave19067

Advertisement