XML (Extensible Markup Language) is an universal formal language used for representing different data in a structural way. XML is platform independent which enables document exchange between various systems and made the language popular. XML became an unchallenged data exchange standard on the Web. XHTML language and documents can efficiently describe the contents of a web page; whereas XML allows us to describe any data which has a well-defined structure. In addition XML enables us to define custom dictionaries of tags for different domains, instead using a predefined set. This article’s purpose is not to present a detailed description of XML. The full specification can be found on-line and in books.
XML documents are widely used and have a wide range of applications. I, as a programmer, often receive an XML file with data, which should be stored in a database. There could be information about people, books, institutions etc. The document has to be parsed and objects obtained from that stored in a database. Data from XML documents can be transformed into an HTML document, a PDF file or any other text format using XSLT. So it is good to know how to create XML documents to store your data in order to generate a PDF file for instance. XML is used to create configuration files – this usage is for sure known to Java programmers.
I think that two most popular API’s to access XML from Java are SAX and DOM. They are used for syntax analysis of the documents. The first one processes the document and everytime a tag, comment, a piece of text or any other XML element occurs, it calls a piece of Java code to signal an event. Our code can perform an appropriate action then. Using this API we can access only the currently processed element. SAX interface is particularly useful for processing big files. On the other hand DOM provides a full representation of the document in memory, in a tree form. Even though these API’s are not subject of this article, I recommend knowing them.
A different approach is data mapping (binding). Instead of working with elements and attributes, we would use Java objects, which simplifies working with XML. These approach should sound familiar, if you have used Hibernate (which maps database tables into Java objects).
To mark the difference in using data binding, a different term is used – "marshalling" instead of "parsing" or "serialization" – converting XML elements into Java objects. A reverse process is called "unmarshalling" – turning Java objects into XML. XStream is a library using this approach. It uses reflection to identify the fields that have to be stored. It is very simple (does not require defining an XML schema) and simplifies greatly using XML in Java. More information can be found at http://xstream.codehaus.org/.
If we want to use XStream, we have to add it to our project. In order to do this we have to download the newest jar file with the library – during the writing of this article it was 1.3.1 version. If we are not going to develop XStream, only use it, I recommend downloading the binary version. After extracting the downloaded file, we can find the jar file in the lib subdirectory (xstream-1.3.1.jar file). Next the path to the extracted jar file should be added to the Java Build Path of our project. If Maven is used, the following dependency should be added to pom.xml:
<dependency> <groupId>com.thoughtworks.xstream</groupId> <artifactId>xstream</artifactId> <version>1.3.1</version> </dependency>
XStream library uses annotations therefore Java 1.5 or higher is needed.
To do a simple conversion, let us create a Java Bean:
public class Person { private String name; private String surname; private Date birthday; //getters and setters }
Now we will use the library to create XML:
public String person2Xml(Person person) { XStream mapping=new XStream(new DomDriver()); String xml=mapping.toXML(person); return xml; }
Let us create a Person object, populate its fields with custom data and call that method. The following result will be displayed on the screen:
<pl.marek.Person> <name>Marek</name> <surname>Kapowicki</surname> <birthday>1983-09-28 00:00:00.0 CET</birthday> </pl.marek.Person>
As you can see the mapping is done according to the names. XStream maps the fully qualified class name (together with the package name), which is not always needed. The date format is a little bit "strange" as well. Fortunately we can modify the mapping e.g. by defining aliases (using annotations). To convert the resulting XML back into a Java object we use:
public Person xml2Person(String xml){ XStream mapping=new XStream(new DomDriver()); return (Person) mapping.fromXML(xml); }
In order to show the capabilities of the library I have created a program which stores information about films. The following data is stored: title, description, genre, actors starring, date of production, director, cover and a link to a website. Application allows us to create an XML file from data provided in Java as well as to perform the reverse process – creating Java objects from a file.
Application is created using Maven. To run it, you have to type mvn install
from the command line. It consists of a few packages, which will be described briefly (more information in Javadoc):
pl.marek.beans
– package containing Java beans which will be mapped to XML:
Film
– represents a film,Person
– represents a person i.e. a director, an actor,Films
– this class contains the full information about films, owner etc. Objects of this class are converted into xml files – which are the result of running this program.All the beans inherit from an abstract class BaseXML, which makes it possible to map all files the same way (if we use annotations to configure mapping, not library methods).
pl.marek.enums
– enum with the movie genres,
pl.marek.xstream
– the main part of the application responsible for data mapping,
XMLMappingInterface
– interface used to map beans inheriting from BaseXML. It has four methods:
public String java2xml(BaseXML base)
– converts an object into a string with XML content,public void java2xmlFile(BaseXML base, String fileName)
– converts an object into an XML file.Methods creating XML are general and can be used to convert all classes descending from BaseXML. During conversion the type of the object is detected and annotations are loaded.
public BaseXML xml2java(String xml, Class<? extends BaseXML> className)
– converts a string (XML content) into an object,public BaseXML xmlFile2java(String fileName, Class<? extends BaseXML> className)
– converts an XML file into an object.Calling methods converting XML you have to pass the type of the object.
XMLMapping
– a class implementing the above interface,PersonXMLMapping
– used for mapping Person objects. Created to show that using XStream library methods instead of annotation we will have problems and cannot use one general mapping,PersonConverter
– converter used to manually map classes to XML files.The code that calls these methods is in the JUnit tests directory. After running the tests we should see two new files: author.xml and films_list.xml.
Looking at the bean code we can see that we can map Java primitive types, custom class objects e.g. director field in Film
class and collections – list of films in Films
class.
Using the XStream library we can change the mapping. It is best to use annotations, adding them to fields in the beans. We have to inform the converter (XStream class object) about that fact. In order to do this we call the method:
xStream.processAnnotations(Class className)
– annotations from className class will be loaded by the converter. If we use annotations when creating an XML file, we have to use annotations when converting the file into objects. Otherwise XStream will try to map XML according to names and the conversion will fail.
Useful annotations:
@XStreamAlias("name")
can be used both with classes and fields. It describes the name to which the field should be mapped. An example for that can be found in the Film
class,@XStreamImplicit(itemFieldName = "nazwa")
– used with collections mapping. Describes the name to which collections elements should be mapped. Example in Films
class,@XStreamOmitField
– fields annotated with this will not be mapped.If we do not want to use annotations, we can use library methods made available by the converter. While creating author.xml I have used xStream.alias("director", Person.class)
in order to define an alias (replacing Person
class name with string "director"). I think that this approach does not simplify using the library and makes the code unreadable.
If we do not want to use the standard conversion from the XStream library, we have to write a custom converter. In order to do so, we extend the com.thoughtworks.xstream.converters.Converter
class, which implements two methods:
public Boolean canConvert(Class clazz)
– checks which classes can be converted,public void marshall(Object value, HierarchicalStreamWriter writer, MarshallingContext context)
– method which is called when an objects is converted to XML:
This method is used in the PersonConverter
– used for manuall conversion of Person
objects.
DateFormat df=DateFormat.getDateInstance(DateFormat.MEDIUM); if(author.getBirthday()!=null) { writer.startNode("birthday"); writer.setValue(df.format(author.getBirthday())); writer.endNode(); }
In the method we check what field is currently processed and define the action to perform while converting that field. In the example we convert the date to the yyyy-mm-dd format.
public Object unmarshall(HierarchicalStreamReader reader, UnmarshallingContext context)
– method called when converting an xml file into Java objects.I hope I managed to encourage anyone to familiarize oneself with XStream library. It is a simple tool which makes using XML files from Java quite easy. The library should be a problem to use for anyone. Annotations and defining custom converters allow us to manage the mapping.
Translation: Paweł Cegła
Source: http://www.javaexpress.pl/article/show/XML_in_JAVA__XStream_Library