XStream – Java to XML and Back

The Problem Statement

XStream can be used to convert a Java Object to XML and back. This tutorial aims to create a java representation of the BBC RSS. We will start with a Simple java class and gradually start adding complexity to it so that it can be converted to the BBC RSS. Note that if you are looking at a way to parse an XML, it would be a good idea to first build a java class that XStream can convert to the required XML. XStream can then use the same class to deserialize the XML. First, have a look at the BBC RSS. Your browser would have formatted it to html so look at the source to see the XML. The parent element is ‘rss’. It has a child element called ‘channel’. The ‘channel’ has some properties (title, link, image etc) and a list of news ‘item'(s). Each ‘item’ has properties set on it (title, description etc). The aim is to create a Java class that XStream can convert to the BBC RSS.

Creating an XStream Instance

The first step is to create an instance of com.thoughtworks.xstream.XStream. This class is a facade to the XStream API and provides all major functionalities. If this class does not solve your problem then you could directly call the API methods. After obtaining the instance of XStream use the fromXML method to convert the XML to a java object. Note that creating an XStream instance is an expensive operation. It is therefore advisable to create a proper instance once and then reuse it for multiple calls (even in parallel). The setup is not threadsafe but any subsequent marshalling/unmarshalling is threadsafe.

Aliases

XStream provides a very useful functionality called aliases. In our example we want to convert an instance of com.studytrails.xml.xstream.Rss to an rss element. We tell XStream that the element ‘rss’ is an ‘alias’ to ‘com.studytrails.xml.xstream.Rss’ so that XStream knows how to convert them. If we dont provide the alias while converting the object to XML then XStream would give the element the fully qualified name of the class. Aliases work for Classes as well as fields. The example below demonstrates the use of aliases.

Implicit Collection

The Channel element in BBC has a list of items. However, note that the XML stream does not have an &ltitems&gt element containing a list of &ltitem&gt elements. The XML stream just has a list of &ltitem&gt elements directly under the &ltChannel&gt element. We want to map them to the ‘items’ java.util.List of the Channel Object. To accomplish this we use a concept in XStream called Implicit Collection. we say that ‘items’ is an implicit collection. What that means is that there is actually no ‘items’ element in the XML, but put all XML elements with name ‘item’ (since the java property items is of type List&ltItem&gt) into the ‘items’ property of the java object. Look at the example to see how it works.

Attributes

Handling attributes is a little tricky. In this example we provide one way to handle attributes, however, we have a dedicated tutorial to explain how attributes can be handled using XStream.

Namespaces

Elements within a namespace can be handled by XStream by using aliases. In this example there is a link within the namespace atom. we just use an alias to read that

xstream.aliasField("atom:link", Channel.class, "a_link");

Example

Lets start building the java example. As a first step create a Java class for the root element i.e. RSS

package com.studytrails.xml.xstream;

import com.thoughtworks.xstream.XStream;

public class BBCRSSExample {
	public static void main(String[] args) {
		XStream xStream = new XStream();
		Rss bbcRss = new Rss();
		System.out.println(xStream.toXML(bbcRss));
	}
}

class Rss {
}

The output

<com.studytrails.xml.xstream.Rss/>

We dont want the fully qualified name of the class for Rss. Lets use alias to simplify the name. Add this line after creating the Rss instance

xStream.alias("rss", Rss.class);

The output now becomes

<rss/>

The rss elmenent contains a Channel element. Lets create a Channel class. The channel element is a child of rss element. Create a field in the Rss class to hold an object of type Channel. Add these lines to the main method

Channel channel = new Channel();
bbcRss.channel = channel;

Here’s how the Rss and Channel class looks now

class Rss {
	public Channel channel;
}
class Channel {
}

Here’s how the XML looks now

<rss>
  <channel/>
</rss>

Before we start adding fields to the classes lets look at an important field. The channel element contains this :

  

the link element belongs to a different namespace. There is a neat little trick to handle this.

Create a class called AtomLink.

class AtomLink {
	public String href;
	public String rel;
	public String type;
}

Create an instance of AtomLink in the main class and assign it to a field in the Channel class

AtomLink atomLink = new AtomLink();
atomLink.href = "http://feeds.bbci.co.uk/news/technology/rss.xml";
atomLink.rel = "self";
atomLink.type = "application/rss+xml";
channel.a_link = atomLink;

Use alias to give the element a namespace prefix

xStream.aliasField("atom:link", Channel.class, "a_link");

and now we want href, rel and type to be attributes instead of child elements, this is how we do that

xstream.aliasAttribute(AtomLink.class, "type", "type");
xstream.aliasAttribute(AtomLink.class, "href", "href");
xstream.aliasAttribute(AtomLink.class, "rel", "rel");

Lets see how our XML looks now


  
    
  

Hope you are enjoying the example. It is important that you understand what’s happened so far. Go back to the example if you have any doubt since we will start picking up pace now.

The next important part is the
item
. The channel contains a list of items but the items are not included in a parent
‘items’
element. XStream handles this using a concept called
Implicit Collection
. Lets see how it works, first lets create the Item class

class Item {
	public String title;
}

create a field in channel class to hold the list of items

class Channel {
	public AtomLink a_link;
	public List<Item> items;
}

In the main class we create an alias for Item and then add an implicit Collection for items. We add two items to the list and see how the XML looks.

xStream.alias("item", Item.class);
xStream.addImplicitCollection(Channel.class, "items");
channel.items = new ArrayList<Item>();
Item item = new Item();
item.title="Item1";
Item item2 = new Item();
item2.title="Item2";
channel.items.add(item);
channel.items.add(item2);

The resulting XML


  
    
    
      Item1
    
    
      Item2
    
  


Next we add class for the Thumbnail which is part of the item element. An item can have multiple thumbnails. Lets use an array now instead of a Collection just to demonstrate how an ImplicitArray works.

class Item {
	public String title;
	public MediaThumbnail[] media_thumbnails;
}

class MediaThumbnail {
	public int width;
	public int height;
	public String url;

}

The main method creates the array and adds thumbnails to it

xStream.addImplicitArray(Item.class, "media_thumbnails", "media:thumbnail");
xStream.aliasAttribute(MediaThumbnail.class, "width","width");
xStream.aliasAttribute(MediaThumbnail.class, "height","height");
xStream.aliasAttribute(MediaThumbnail.class, "url","url");
	
MediaThumbnail thumbnail = new MediaThumbnail();
thumbnail.height=100;
thumbnail.width=50;
thumbnail.url="thumbnail_link";
MediaThumbnail thumbnail2 = new MediaThumbnail();
thumbnail2.height=100;
thumbnail2.width=50;
thumbnail2.url="thumbnail_link2";

item.media_thumbnails = new MediaThumbnail[2];
item.media_thumbnails[0] = thumbnail;
item.media_thumbnails[1] = thumbnail2;

We have almost built up the whole class. We can further add the Image class that stores the image in a channel. After creating all classes we start adding the other fields to them. Once the Rss class is ready you can then use it to read the BBC RRS! Heres the complete class and the main method that does the reading.

package com.studytrails.xml.xstream;

import java.net.MalformedURLException;
import java.net.URL;

import com.thoughtworks.xstream.XStream;

public class XStreamDeserializerExample1 {

	private String bbcUrl = "http://feeds.bbci.co.uk/news/technology/rss.xml?edition=int";

	public static void main(String[] args) throws MalformedURLException {
		XStreamDeserializerExample1 serializer = new XStreamDeserializerExample1();
		serializer.basicSerialization();
	}

	private void basicSerialization() throws MalformedURLException {

		XStream xstream = new XStream();
		xstream.alias("rss", Rss.class);
		xstream.aliasField("atom:link", Channel.class, "a_link");
		xstream.alias("item", Item.class);
		
		xstream.aliasAttribute(AtomLink.class, "type", "type");
		xstream.aliasAttribute(AtomLink.class, "href", "href");
		xstream.aliasAttribute(AtomLink.class, "rel", "rel");
		
		xstream.aliasAttribute(MediaThumbnail.class, "width","width");
		xstream.aliasAttribute(MediaThumbnail.class, "height","height");
		xstream.aliasAttribute(MediaThumbnail.class, "url","url");
		
		xstream.addImplicitCollection(Channel.class, "items");
		xstream.addImplicitArray(Item.class, "media_thumbnails", "media:thumbnail");
		Rss bbcFeed = (Rss) xstream.fromXML(new URL(bbcUrl));
		// bbcFeed now contains the Java representation of the BBC RSS
	}
}

The classes

package com.studytrails.xml.xstream;

import java.util.List;

public class Rss {
	public Channel channel;
}

class Channel {
	public String title;
	public String link;
	public String description;
	public String language;
	public String lastBuildDate;
	public String copyright;
	public Image4 image;
	public String ttl;
	public AtomLink a_link;
	public List items;
}

class AtomLink {
	public String href;
	public String rel;
	public String type;
}

class Item {
	public String title;
	public String description;
	public String link;
	public String guid;
	public String pubDate;
	public MediaThumbnail[] media_thumbnails;
}

class MediaThumbnail {
	public int width;
	public int height;
	public String url;
}

class Image {
	public String url;
	public String title;
	public String link;
	public String width;
	public String height;
}


Leave a Comment