Configuring XStream to convert Java objects to and from XML

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.

		<dependency>
			<groupId>com.thoughtworks.xstream</groupId>
			<artifactId>xstream</artifactId>
			<version>1.4.11.1</version>
		</dependency>

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.

package com.nullbeans.persistence.models;

public class BankAccount {

    private long id;

    private int version;

    private String accountNumber;

    private boolean isActive;

    public BankAccount() {
    }


    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    public int getVersion() {
        return version;
    }

    public void setVersion(int version) {
        this.version = version;
    }

    public String getAccountNumber() {
        return accountNumber;
    }

    public void setAccountNumber(String accountNumber) {
        this.accountNumber = accountNumber;
    }

    public boolean isActive() {
        return isActive;
    }

    public void setActive(boolean active) {
        isActive = active;
    }

}

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.

@RunWith(MockitoJUnitRunner.StrictStubs.class)
public class XStreamTest {

    private static final Logger log = LoggerFactory.getLogger(XStreamTest.class);

    @Test
    public void testSimpleXStream(){

        XStream xStream = new XStream();

        BankAccount bankAccount = new BankAccount();
        bankAccount.setActive(false);
        bankAccount.setAccountNumber("12345123132");
        bankAccount.setId(22);

        String xml = xStream.toXML(bankAccount);
        log.info("Output = \r\n "+xml);

    }

}

And let us run the test and check the results.

22:34:13.615 [main] INFO com.nullbeans.XStreamTest - Output = 
 <com.nullbeans.persistence.models.BankAccount>
  <id>22</id>
  <version>0</version>
  <accountNumber>12345123132</accountNumber>
  <isActive>false</isActive>
</com.nullbeans.persistence.models.BankAccount>


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.

    @Test
    public void testSimpleXStream(){

        XStream xStream = new XStream();

        BankAccount bankAccount = new BankAccount();
        bankAccount.setActive(false);
        bankAccount.setAccountNumber("12345123132");
        bankAccount.setId(22);

        String xml = xStream.toXML(bankAccount);
        log.info("Output = \r\n "+xml);

        xml = xml.replace("22", "24");
        Object desBankAccount = xStream.fromXML(xml);
        log.info("Deserialized output = \r\n "+desBankAccount);

    }

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

22:56:28.075 [main] INFO com.nullbeans.XStreamTest - Output = 
 <com.nullbeans.persistence.models.BankAccount>
  <id>22</id>
  <version>0</version>
  <accountNumber>12345123132</accountNumber>
  <isActive>false</isActive>
</com.nullbeans.persistence.models.BankAccount>
Security framework of XStream not initialized, XStream is probably vulnerable.
22:56:28.102 [main] INFO com.nullbeans.XStreamTest - Deserialized output = 
 BankAccount{id=24, version=0, accountNumber='12345123132', isActive=false}


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:

@XStreamAlias("BankAccount")
public class BankAccount {


    private long id;

    private int version;

.............................


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.

 @Test
    public void testSimpleXStream(){

        XStream xStream = new XStream();
        xStream.processAnnotations(BankAccount.class);
        BankAccount bankAccount = new BankAccount();
        bankAccount.setActive(false);

........Example test continues as before......

And the result would be:

08:24:03.611 [main] INFO com.nullbeans.XStreamTest - Output = 
 <BankAccount>
  <id>22</id>
  <version>0</version>
  <accountNumber>12345123132</accountNumber>
  <isActive>false</isActive>
</BankAccount>
Security framework of XStream not initialized, XStream is probably vulnerable.
08:24:03.633 [main] INFO com.nullbeans.XStreamTest - Deserialized output = 
 BankAccount{id=24, version=0, accountNumber='12345123132', isActive=false}


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.

    @Test
    public void testSimpleXStream(){

        XStream xStream = new XStream();
        xStream.processAnnotations(BankAccount.class);
        BankAccount bankAccount = new BankAccount();
        bankAccount.setActive(false);
        bankAccount.setAccountNumber("12345123132");
        bankAccount.setId(22);

        BankAccount bankAccount2 = new BankAccount();
        bankAccount.setActive(false);
        bankAccount.setAccountNumber("12345123132");
        bankAccount.setId(23);

        ArrayList<BankAccount> bankAccounts = new ArrayList<>();
        bankAccounts.add(bankAccount);
        bankAccounts.add(bankAccount2);

        String xml = xStream.toXML(bankAccounts);
        log.info("Output = \r\n "+xml);
        
    }

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

08:33:12.251 [main] INFO com.nullbeans.XStreamTest - Output = 
 <list>
  <BankAccount>
    <id>23</id>
    <version>0</version>
    <accountNumber>12345123132</accountNumber>
    <isActive>false</isActive>
  </BankAccount>
  <BankAccount>
    <id>0</id>
    <version>0</version>
    <isActive>false</isActive>
  </BankAccount>
</list>


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.

@XStreamAlias("BankAccounts")
public class BankAccounts{

    @XStreamImplicit
    private List<BankAccount> bankAccounts = new ArrayList<>();

    public List<BankAccount> getBankAccounts() {
        return bankAccounts;
    }

    public void setBankAccounts(List<BankAccount> bankAccounts) {
        this.bankAccounts = bankAccounts;
    }
}

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.

    @Test
    public void testSimpleXStream(){

        XStream xStream = new XStream();
        xStream.processAnnotations(BankAccount.class);
        xStream.processAnnotations(BankAccounts.class);

        BankAccount bankAccount = new BankAccount();
        bankAccount.setActive(false);
        bankAccount.setAccountNumber("12345123132");
        bankAccount.setId(22);

        BankAccount bankAccount2 = new BankAccount();
        bankAccount2.setActive(false);
        bankAccount2.setAccountNumber("12345123132");
        bankAccount2.setId(23);

        BankAccounts bankAccounts = new BankAccounts();
        bankAccounts.getBankAccounts().add(bankAccount);
        bankAccounts.getBankAccounts().add(bankAccount2);

        String xml = xStream.toXML(bankAccounts);
        log.info("Output = \r\n "+xml);

    }

And the result is:

08:53:42.256 [main] INFO com.nullbeans.XStreamTest - Output = 
 <BankAccounts>
  <BankAccount>
    <id>22</id>
    <version>0</version>
    <accountNumber>12345123132</accountNumber>
    <isActive>false</isActive>
  </BankAccount>
  <BankAccount>
    <id>23</id>
    <version>0</version>
    <accountNumber>12345123132</accountNumber>
    <isActive>false</isActive>
  </BankAccount>
</BankAccounts>

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.

@XStreamAlias("SmartObject")
public class SmartObject {

    @XStreamImplicit
    private List<BigInteger> bigIntegers = new ArrayList<>();

    public List<BigInteger> getBigIntegers() {
        return bigIntegers;
    }

    public void setBigIntegers(List<BigInteger> bigIntegers) {
        this.bigIntegers = bigIntegers;
    }
}

We will create a new test for our new example.

    @Test
    public void xstreamTest2(){
        XStream xStream = new XStream();
        xStream.processAnnotations(SmartObject.class);

        BigInteger int1 = new BigInteger("123923892822223");
        BigInteger int2 = new BigInteger("777777777777777");

        SmartObject smartObject = new SmartObject();
        smartObject.getBigIntegers().add(int1);
        smartObject.getBigIntegers().add(int2);

        String xml = xStream.toXML(smartObject);
        log.info("Output = \r\n "+xml);
    }

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

09:08:31.554 [main] INFO com.nullbeans.XStreamTest - Output = 
 <SmartObject>
  <big-int>123923892822223</big-int>
  <big-int>777777777777777</big-int>
</SmartObject>

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.

    @XStreamImplicit(itemFieldName = "myValue")
    private List<BigInteger> bigIntegers = new ArrayList<>();

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

09:12:14.231 [main] INFO com.nullbeans.XStreamTest - Output = 
 <SmartObject>
  <myValue>123923892822223</myValue>
  <myValue>777777777777777</myValue>
</SmartObject>


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.

@XStreamAlias("BankAccount")
public class BankAccount {

    @XStreamOmitField
    private long id;


............................

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

    @Test
    public void xstreamHideFieldTest(){
        XStream xStream = new XStream();
        xStream.processAnnotations(BankAccount.class);
        xStream.processAnnotations(BankAccounts.class);

        BankAccount bankAccount = new BankAccount();
        bankAccount.setActive(false);
        bankAccount.setAccountNumber("12345123132");
        bankAccount.setId(22);

        String xml = xStream.toXML(bankAccount);
        log.info("Output = \r\n "+xml);
    }

The produced XML will look like this.

09:21:13.319 [main] INFO com.nullbeans.XStreamTest - Output = 
 <BankAccount>
  <version>0</version>
  <accountNumber>12345123132</accountNumber>
  <isActive>false</isActive>
</BankAccount>


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:

 <BankAccount>
  <version>0</version>
......................
</BankAccount>

it will look like this:

 <BankAccount version="0">
  .............
</BankAccount>

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.

@XStreamAlias("BankAccount")
public class BankAccount {

    @XStreamOmitField
    private long id;

    @XStreamAsAttribute
    private int version;

...........................

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

09:23:57.096 [main] INFO com.nullbeans.XStreamTest - Output = 
 <BankAccount version="0">
  <accountNumber>12345123132</accountNumber>
  <isActive>false</isActive>
</BankAccount>


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”.

    @Test
    public void testXStreamSecurity(){

        XStream xStream = new XStream();

        //Clear out all permissions
        xStream.addPermission(NoTypePermission.NONE);

        //Add permission to deserialize the BankAccount class
        xStream.allowTypes(new Class[]{BankAccount.class});

        xStream.processAnnotations(BankAccount.class);

        BankAccount bankAccount = new BankAccount();
        bankAccount.setActive(false);
        bankAccount.setAccountNumber("12345123132");
        bankAccount.setId(22);

        String xml = xStream.toXML(bankAccount);
        log.info("Output = \r\n "+xml);

        xml = xml.replace("22", "23");

        BankAccount bankAccount1 = (BankAccount)xStream.fromXML(xml);
        log.info("Deserialized object = \r\n "+bankAccount1);


    }


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.

09:51:19.261 [main] INFO com.nullbeans.XStreamTest - Output = 
 <BankAccount>
  <id>22</id>
  <version>0</version>
  <accountNumber>12345123132</accountNumber>
  <isActive>false</isActive>
</BankAccount>
09:51:19.291 [main] INFO com.nullbeans.XStreamTest - Deserialized object = 
 BankAccount{id=23, version=0, accountNumber='12345123132', isActive=false}

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

    @Test
    public void testXStreamSecurity2() throws IOException {

        String xml =
                "<java.io.BufferedWriter>" +
                        "/<java.io.BufferedWriter>";

        XStream xStream = new XStream();
        xStream.addPermission(NoTypePermission.NONE);
        xStream.allowTypes(new Class[]{BankAccount.class});

        //We should get an exception here
        Object bufferedWriter = xStream.fromXML(xml);


    }

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

com.thoughtworks.xstream.security.ForbiddenClassException: java.io.BufferedWriter

	at com.thoughtworks.xstream.security.NoTypePermission.allows(NoTypePermission.java:26)
	at com.thoughtworks.xstream.mapper.SecurityMapper.realClass(SecurityMapper.java:74)
	at com.thoughtworks.xstream.mapper.MapperWrapper.realClass(MapperWrapper.java:125)
	at com.thoughtworks.xstream.mapper.CachingMapper.realClass(CachingMapper.java:47)
	at com.thoughtworks.xstream.core.util.HierarchicalStreams.readClassType(HierarchicalStreams.java:29)
	at com.thoughtworks.xstream.core.TreeUnmarshaller.start(TreeUnmarshaller.java:133)
	at com.thoughtworks.xstream.core.AbstractTreeMarshallingStrategy.unmarshal(AbstractTreeMarshallingStrategy.java:32)
	at com.thoughtworks.xstream.XStream.unmarshal(XStream.java:1487)
	at com.thoughtworks.xstream.XStream.unmarshal(XStream.java:1467)
	at com.thoughtworks.xstream.XStream.fromXML(XStream.java:1338)
	at com.thoughtworks.xstream.XStream.fromXML(XStream.java:1329)
	at com.nullbeans.XStreamTest.testXStreamSecurity2(XStreamTest.java:39)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
	at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
	at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
	at org.mockito.internal.runners.DefaultInternalRunner$1.run(DefaultInternalRunner.java:79)
	at org.mockito.internal.runners.DefaultInternalRunner.run(DefaultInternalRunner.java:85)
	at org.mockito.internal.runners.StrictRunner.run(StrictRunner.java:39)
	at org.mockito.junit.MockitoJUnitRunner.run(MockitoJUnitRunner.java:163)
	at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
	at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
	at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
	at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
	at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)


Process finished with exit code -1


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 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 🙂