Serializing Java objects to and from XML is a necessary operation in many software systems. For example, when communicating with other systems and back-ends, or simply when storing and loading data. In this tutorial, we will discuss how to use the XStream library to convert Java objects to and from XML.

What is XStream

XStream is an open source Java library that is used to serialize and deserialize Java objects to and from XML. XStream is a very powerful library as it allows us to get started with serialization/deserialization with little configuration, yet, it provides a wide array of tools to customize the serialization/deserialization process to our needs.

Getting started with XStream

Getting started with XStream in Java is very easy. We simply need to create a new instance of the XStream class. In order to do that, we will first need to add the required dependencies. If you are using a dependency management system, such as maven, then you will need to add this to your pom.xml file.

Please make sure that you are adding the latest version of the library that is compatible with your software. For that, you can check the XStream github page here and the maven repository.

Serializing objects to XML

Let us start by serializing an object. We will use the following example entity class.

Now, let us try to serialize an instance of the BankAccount class in a test. To serialize objects with xstream, we need to use the XStream.toXML(…) method.

And let us run the test and check the results.

Please notice the following points:

  • XStream was able to serialize the BankAccount object without any configuration. This is done through reflection.
  • Initializing an instance of XStream is an expensive operation. It should be done preferably once within the application, and reused everywhere. For example, if you are using Spring, then it would be a great idea to have XStream instantiated as a member of a singleton bean (check: singleton vs prototype beans) and then call XStream through that bean.

Notice also that the BankAccount XML tag contains the full class name. This can be changed to exclude the class name. We will explore this in the next section.

Deserializing XML to a Java Object

Now, let us try to extend our test by deserializing the generated XML into an instance of the BankAccount class. To deserialize XML to Java objects, we need to use the XStream.fromXML(…) method.

If we run our test now, this will be the output.

Notice that XStream was automatically able to create a new instance of BankAccount, with our modified id field. Also notice the security warning from XStream. We will explore this later.

Configuring and customizing XStream

In our previous section, we saw how easy it was to get started with XStream. However, our output can be customized to suit our needs. Let us explore a few customization options and more complicated scenarios that can be fulfilled with the framework.

One way to customize how XStream serializes your objects is by adding annotations to your object’s classes. The annotations will then modify the behavior of the library during serialization/deserialization. Let us discuss a few options.

Renaming classes using the @XStreamAlias annotation

In our previous example, XStream serialized the BankAccount to XML, by using the “com.nullbeans.persistence.models.BankAccount” tag in the XML output. This may not be desirable when producing XML for outside systems. Let us configure XStream to produce the XML tag with just “BankAccount”.

To do that, we will use the @XStreamAlias annotation. This annotation is added to the class who’s instances would be serialized and deserialized. The annotation provides an “Alias” to the class. This alias will be used in the XML tag when processing the class instances. For example, to configure the BankAccount class, we will add the annotation as follows:

The second step is to tell XStream that our class has special configurations that should be loaded. This is done by calling the XStream.processAnnotations(…) method. Again, this is a costly operation and should be done only once on application startup. Otherwise your application performance will not be optimal.

Let us modify our test and run it again.

And the result would be:

Serializing/Deserializing lists and the @XStreamImplicit annotation

Serializing and deserializing lists is an important operation when communicating with outside systems. For example, when obtaining or sending a list of results. Therefore, let us move to our next example.

Let us create a list of “BankAccounts” to convert to XML and let us check the results.

When we run this example, we will get the following results:

Notice that XStream calls the list of bank accounts “list” inside the XML output. What if we want to call it “BankAccounts” instead of list? The good news is that the solution is easy. The bad news is that it is a little tedious.

We will need to create a wrapper class, called “BankAccounts”, to wrap in the list of bank accounts into. In order to avoid XStream writing the name of the list class inside the XML output tag, we will use the @XStreamImplicit annotation. This annotation tells XStream that this is an implicitly inferred collection, and that an outer tag for the list elements is not required.

This configuration has two effects:

  • It removes the tag around the individual bank accounts. So no more <list> tags.
  • It adds a tag around the list items called “BankAccounts”, because this is the outer class name.

Let us modify our test again and run the example.

And the result is:

Renaming individual items in lists using @XStreamImplicit

In the previous example, we converted a list of “BankAccounts” to XML. Each item in the produced XML was called “BankAccount” because we were able to annotate the BankAccount class. But what if the list was made of an object, which is from a class that we cannot modify? Take the following class as an example.

We will create a new test for our new example.

If we run this example, we will get the following results.

Notice that XStream converted each BigInteger into an item with the <big-int> tag. This may not be a desirable behavior, specially if you need to format your data into a specific format for an external system. Luckily, this can be easily fixed by setting the itemFieldName property of the @XStreamImplicit annotation.

If we run our example again, XStream will convert each BigInteger into an item with the tag <myValue>.

Omitting fields from serialization/deserialization using @XStreamOmitField

Sometimes, you do not want to expose some data in the produced XML. For example, you may want to hide your database ids when sending data as XML to external systems. For this, the @XStreamOmitField annotation comes to the rescue. Let us revisit our BankAccount example, by using the annotation on the id field.

Now, let us try our new configuration in a new test.

The produced XML will look like this.

Including elements as a tag attributes using @XStreamAsAttribute

Sometimes, you need to include an inner element of a class as an XML attribute, instead of a child XML element. Let us try to add the version member of the BankAccount class as an attribute, so instead of having the version show up like this:

it will look like this:

For this, we will need to use the @XStreamAsAttribute annotation. Simply add the annotation to the class member you would like to show up as an attribute.

Now, if we rerun our the test from the previous section, we will get the following results.

What about the “Security framework of XStream not initialized, XStream is probably vulnerable” warning?

There comes a security risk when dealing with deserialization of data (converting from XML/JSON/etc to Java Objects). This is because we do not control the incoming data from external systems. Notice that in our examples, we only used our own defined classes. Also notice that XStream was able to decide the type of object to create by the incoming XML.

Since the framework automatically tries to decide the type of object that exist in the incoming data, this can be both an advantage but also a disadvantage. The disadvantage is that a malicious entity could send XML data that represents, for example, an MBean, a proxy object, or any other type of object that when initialized, could open a backdoor into your application, or even worse, perform some malicious operations such as collecting personal and financial data.

Therefore, XStream gives you a warning when you deserialize data. So what is the solution? The solution is to tell XStream which data types it is allowed to deserialize. Therefore, when only the data types that you specify will be converted to Java objects.

For our BankAccount example, we would need to allow XStream to deserialize data with the type “BankAccount”.

Please notice the first two statements after initializing XStream.

  • First, we clear out all permissions for XStream using the permission type NoTypePermission.NONE. This makes sure that XStream only deserializes the data types that we allow.
  • Second, we tell XStream that our BankAccount class data is authorized for processing. This is done using the XStream.allowTypes(…) method.

If we run our example now, we no longer get the warning.

So now, what happens when someone tries to add malicious data to the incoming XML? For example, an object with the type “BufferedWriter”.

If we run the example above, we will get the following error.

Summary and conclusions

XStream is a very powerful framework that allows you to quickly get up and running with interfaces that use the XML format. However, one has to be very careful when it comes to configuration, performance and security.

If you would like to improve your knowledge about Java and how to write high performance code like a pro, then take some time to invest in your skills by reading the book “Java Performance: The Definitive Guide: Getting the Most Out of Your Code”. You can check out more info about the book and pricing from our Amazon affiliate link: “Java Performance: The Definitive Guide: Getting the Most Out of Your Code”

If you liked this post, or if you have any questions or comments, then please let us know in the comments below, and we will be happy to answer you as soon as we can 🙂