DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports Events Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
Refcards
Trend Reports
Events
Zones
Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones AWS Cloud
by AWS Developer Relations
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones
AWS Cloud
by AWS Developer Relations
The Latest "Software Integration: The Intersection of APIs, Microservices, and Cloud-Based Systems" Trend Report
Get the report
  1. DZone
  2. Coding
  3. Languages
  4. Java and XML - Part 3 (JAXB)

Java and XML - Part 3 (JAXB)

Kosta Stojanovski user avatar by
Kosta Stojanovski
·
Nov. 13, 13 · Interview
Like (2)
Save
Tweet
Share
35.57K Views

Join the DZone community and get the full member experience.

Join For Free

[Editor's note: this article is part 3 of a series. The first two parts can be found here and here.]

Besides the JAXP API for handling XML files, which I mentioned in my previous article[1], there is another API in Java named JAXB[2] (Java Architecture for XML Binding[3]). JAXB is also used for persisting Java objects as XML files. Persisting this way they are more human readable than if they were to persist via Java serailization[4] and easier for manipulation from other applications. For example, if you need only a few properties from the persistent object as XML file they can be easily read via JAXP or similar parsers like JDOM2.

JAXB came to Java with Java 6. Before Java6, other 3rd party libraries were used for this kind of processing. Some of them are XStream from the firm Codehouse and XMLBeans from the Apache Software Foundation. Other libraries are: JiBX, TopLink, EclipseLink MOXy, Apache Common Betwixt, CookXML, Castor XML (I didn't know that there are so many other libraries for XML data binding, nor I can explain why so many different implementations exist. It seems every firm and organization wrote their own implementation).


The two main terms bound to JAXB are marshalling and unmarshalling.

  • Marshalling is the process when data from Java objects are written into XML.
  • Unmarshalling is the process when XML data is transformed into Java objects. 

Code Example Marshalling Handler.

JAXBContext jc = JAXBContext.newInstance(SomeClass.class);
Marshaller m = jc.createMarshaller();
// output pretty printed
m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
m.marshal(someClassObj, someFile);
m.marshal(someClassObj, System.out);
Code Example Unmarshalling Handler.
JAXBContext jc = JAXBContext.newInstance(SomeClass.class);
Unmarshaller u = jc.createUnmarshaller();
unmarshallesObject = u.unmarshal(file); 
After persisting the objects into XML files, they can be unmarshalled from any other Java application which has the class definition of the annotated JAXB classes on their classpath, which means that if you marshalled the obect with one JVM, it can be unmarshalled with other JVM as well.

In this article, I will manually create simple classes to demonstrate the most used JAXB cases. Another approach for doing this is to use XSD as parameter via the xjc tool for creating all annotated classes. For further and detailed explaination of the tool:

  • Java SE Documentation: Launching xjc [4]
  • GlassFish Metro Site: Launching xjc[5]
  • GlassFish Metro Site: xjc Specification 2.0[6]

There is also another tool with which you can create XSD files from the annotated classes known as schemagen[7]. Both tools can be found in the bin folder of the java install location.

You can even generate an XSD programmaticaly from the JAXB annotated classes like shown in this example from stackoverflow[8].

If you are an eclipse user, this tutorial[9] from "the Open Tutorials" can give you first impressions into handling JAXB projects or creating JAXB classes from XSD or vice versa.

For the examples following was used: eclipse luna as IDE, java 7.25

JAXB Clients

The JAXB clients used for the examples here looks like:

// marshaller
Marshaller m = jc.createMarshaller();
m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
			
//handling CDATA - is described in adapter section
XMLOutputFactory xof = XMLOutputFactory.newInstance();
XMLStreamWriter streamWriter = new CDataXMLStreamWriter(
	new IndentingXMLStreamWriter(
			xof.createXMLStreamWriter(new FileWriter(file))));

// unmarshaller
JAXBContext jc = JAXBContext.newInstance(Person.class,
		Market.class, OtherPerson.class, Key.class, Value.class);
Unmarshaller u = jc.createUnmarshaller();
outputObject = u.unmarshal(file);
Since the examples are too large to be added on one page, I have split them on 3 pages.


 Next pages

Simple-Sample-Example - Introduction to the common annotantion like @XmlRoot, @XmlAttribute, @XmlElementWrapper and @XmlType(propOrder = {"prop0", "prop1",  and soon  "}".
It's Adapter Time! - Using @XmlJavaTypeAdapter for maps and CDATA-Element.
 Collective Polymorphism - Presenting Inheritance case with @XMLSeeAlso({SomeClass.class})
 Everyday Situations and Résumé

Simple-Sample-Example

Empty constructor of every JAXB class is mandatory. By default all properties of one class are defined as if the where annotated with @XMLElement.

@XmlRoot

The root class needed to be marked with the annotation @XMLRoot.

@XmlAttribute

If you want to change the property to become an attribute then it should be annotated with @XmlAttribute.

@XmlValue

Somethimes all properties of a class are attributes and one is the "special" value of the property. This can be realized with @XMLValue. @XmlValue - can be only used if the class have no @XMLElements. @XMLAttributes can occur.

@XmlElementWrapper

@XmlElementWrapper can be used only over collections and is the first and easiest solution as wrapper. If you need more informations in the wrapper element like attributes then you need to create JAXB class for that.

@XmlType(propOrder = {"prop0", "prop1",  and soon  "}"

If you want to order the properties of the class @XmlType(propOrder = {"prop0", "prop1",  and soon  "}" should be used. The names of the properties are the same like the getter methods without the "get"-prefix and not like the property names.

Given are the following classes:
package name.stojanok.dzone.javaxml.situation1;

import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;

@XmlRootElement(namespace = "name.stojanok.dzone")
@XmlType(propOrder = { "id", "firstname", "lastname", "additionalInformation",
		"extraInformation" })
public class Person {

	private String id;
	private String firstname;
	private String lastname;
	private AdditionalInformation additionalInformation;
	private ExtraInformation extraInformation;

	public String getFirstname() {
		return firstname;
	}

	public void setFirstname(String firstname) {
		this.firstname = firstname;
	}

	public String getLastname() {
		return lastname;
	}

	public void setLastname(String lastname) {
		this.lastname = lastname;
	}

	@XmlAttribute
	public String getId() {
		return id;
	}

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

	@XmlElement(name = "addInfo")
	public AdditionalInformation getAdditionalInformation() {
		return additionalInformation;
	}

	public void setAdditionalInformation(
			AdditionalInformation additionalInformations) {
		this.additionalInformation = additionalInformations;
	}

	public ExtraInformation getExtraInformation() {
		return extraInformation;
	}

	public void setExtraInformation(ExtraInformation extraInformation) {
		this.extraInformation = extraInformation;
	}

}

package name.stojanok.dzone.javaxml.situation1;

import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlValue;

public class ExtraInformation {

	private String id;
	private String name;
	private String value;

	// for JAXB
	public ExtraInformation() {
		super();
	}

	public ExtraInformation(String id, String name, String value) {
		super();
		this.id = id;
		this.name = name;
		this.value = value;
	}

	@XmlAttribute
	public String getId() {
		return id;
	}

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

	@XmlAttribute
	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	@XmlValue
	public String getValue() {
		return value;
	}

	public void setValue(String value) {
		this.value = value;
	}

}

package name.stojanok.dzone.javaxml.situation1;

import java.util.ArrayList;

import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementWrapper;

public class AdditionalInformation {

	private boolean drivingLicense;
	private ArrayList<String> hobbies = new ArrayList<String>();

	public boolean isDrivingLicense() {
		return drivingLicense;
	}

	public void setDrivingLicense(boolean drivingLicense) {
		this.drivingLicense = drivingLicense;
	}

	@XmlElementWrapper(name = "hobbies")
	@XmlElement(name = "hobby")
	public ArrayList<String> getHobbies() {
		return hobbies;
	}

	public void setHobbies(ArrayList<String> hobbies) {
		this.hobbies = hobbies;
	}
}

The test methods:

	@Test
	public void testMarshallerSituation1() {
		Person person = new Person();
		person.setFirstname("John");
		person.setLastname("Doe");
		person.setId("123");
		AdditionalInformation additionalInformations = new AdditionalInformation();
		ArrayList<String> hobbies = new ArrayList<String>(Arrays.asList(
				"fishing", "techblog"));
		additionalInformations.setHobbies(hobbies);
		person.setAdditionalInformation(additionalInformations);
		ExtraInformation extraInformation = new ExtraInformation("987", "type",
				"info");
		person.setExtraInformation(extraInformation);
		jaxbClient.marshaller(person, file1);
	}

	@Test
	public void testUnmarshallerSituation1() {
		Object object = jaxbClient.unmarshaller(file1);
		if (object instanceof Person) {
			LOGGER.trace("id: " + ((Person) object).getId());
			LOGGER.trace("firstname: " + ((Person) object).getFirstname());
			LOGGER.trace("lastname: " + ((Person) object).getLastname());
		}
	}

The XML file:

<?xml version="1.0" ?>
<ns2:person xmlns:ns2="name.stojanok.dzone" id="123">
  <firstname>John</firstname>
  <lastname>Doe</lastname>
  <addInfo>
    <drivingLicense>false</drivingLicense>
    <hobbies>
      <hobby>fishing</hobby>
      <hobby>techblog</hobby>
    </hobbies>
  </addInfo>
  <extraInformation id="987" name="type">info</extraInformation>
</ns2:person>

It's Adapter Time!

In some cases the normal annations are not leading to the right solution. In that case, the adapter must be written for solving that structure problem. I will describe two kind of cases where this situation comes. 

Marshalling / Unmarshalling Maps

Searching in the web for this case I found some good solutions:

  • generic solution for this problem from the John Yearly's Blog[10]
  • solution for a class which wraps array from the Sushant's Blog[11]
  • solution for the given map (depend on the type of the key and value) written here on dzone by Blaise Doughan[12].

Here I will take the first generic and the last solution which is not generic but depends on the key, value types of the map.

@XmlJavaTypeAdapter(value=AdapterMap.class)

For the generic example I have used following unmodified classes from the John Yearly's Blog:

  • XmlGenericMapAdapter
  • MapType
  • MapEntryType

For the type depended solution I have modified the class which can be found behind the link[12] above.

package name.stojanok.dzone.javaxml.situation2;

import java.util.*;
import javax.xml.bind.annotation.adapters.XmlAdapter;

/**
 * taken from:
 * http://java.dzone.com/articles/jaxb-and-javautilmap
 * @author Blaise Doughan
 *  
 * modified for my example
 *
 */
public class DependMapAdapter extends
		XmlAdapter<DependMapAdapter.AdaptedMap, Map<String, String>> {

	public static class AdaptedMap {
		public List<Entry> entry = new ArrayList<Entry>();
	}

	public static class Entry {
		public String key;
		public String value;
	}

	@Override
	public Map<String, String> unmarshal(AdaptedMap adaptedMap)
			throws Exception {
		Map<String, String> map = new HashMap<String, String>();
		for (Entry entry : adaptedMap.entry) {
			map.put(entry.key, entry.value);
		}
		return map;
	}

	@Override
	public AdaptedMap marshal(Map<String, String> map) throws Exception {
		AdaptedMap adaptedMap = new AdaptedMap();
		for (Map.Entry<String, String> mapEntry : map.entrySet()) {
			Entry entry = new Entry();
			entry.key = mapEntry.getKey();
			entry.value = mapEntry.getValue();
			adaptedMap.entry.add(entry);
		}
		return adaptedMap;
	}

}

Given are the following classes:

package name.stojanok.dzone.javaxml.situation2;

public class Key {

	private String key;

	public String getKey() {
		return key;
	}

	public void setKey(String key) {
		this.key = key;
	}
}

package name.stojanok.dzone.javaxml.situation2;

public class Value {

	private String value;

	public String getValue() {
		return value;
	}

	public void setValue(String value) {
		this.value = value;
	}
}


package name.stojanok.dzone.javaxml.situation2;

import java.util.HashMap;
import java.util.Map;

import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;

@XmlRootElement
public class OtherPerson {

	private Map<String, String> map0 = new HashMap<String, String>();
	private Map<String, String> map1 = new HashMap<String, String>();
	private Map<Key, Value> map3 = new HashMap<Key, Value>();

	private String cdata;
	
	private String someData;
	
	@XmlJavaTypeAdapter(DependMapAdapter.class)
	public Map<String, String> getMap0() {
		return map0;
	}
	public void setMap0(Map<String, String> map0) {
		this.map0 = map0;
	}
	
	@XmlJavaTypeAdapter(XmlGenericMapAdapter.class)
	public Map<String, String> getMap1() {
		return map1;
	}
	public void setMap1(Map<String, String> map1) {
		this.map1 = map1;
	}	
	
	@XmlJavaTypeAdapter(XmlGenericMapAdapter.class)
	public Map<Key, Value> getMap3() {
		return map3;
	}
	public void setMap3(Map<Key, Value> map3) {
		this.map3 = map3;
	}
	
	@XmlJavaTypeAdapter(CdataAdapter.class)
	public String getCdata() {
		return cdata;
	}
	
	public void setCdata(String cdata) {
		this.cdata = cdata;
	}
	
	public String getSomeData() {
		return someData;
	}
	
	public void setSomeData(String someData) {
		this.someData = someData;
	}

}

Using CDATA

Annotation in JAXB for CDATA does not exist. One way to handle CDATA is using a selfmade Adapter. Some of the examples which I have found on internet are:

  • "the Open Tutorials" [13]
  • Oded Peer's Blog [14]
  • stackoverflow Q/A [15]

@XmlJavaTypeAdapter(value=CdataAdapter.class)

Using Java 6 and building the project with maven there was a problem with the restrictuon of the "com.sun.xml.internal.bind.characterEscapeHandler"-class. Workaround for that was to add the jaxb-impl jar to the classpath and to use the not internal "com.sun.xml.bind.characterEscapeHandler"-class.

Warning: Using this kind of modified "CharacterEscapeHandler" means that the signs which were escaped by default are not any more. This can lead to generating invalid XML files!!!

Better solution for CDATA

Other kind of solution for this problem can be done with implementing own XmlStream as described in the Michaels Blog[16].

With changing the pattern to CDATA, the custom XmlStream can be used for handling CDATA parts of the XMLStreams. One disadvantage is the flat format of the output. But that can be done by using the IndentingXMLStreamWriter. Because IndentingXMLStreamWriter belogs to the "internal" package it is recommended to use external jaxb-impl jar for building the project so you will not have any problems by building it with any build automation tool.

Both classes where used from the blog[16] where the first was not modified but the second is:

  • DelegatingXMLStreamWriter
  • CDataXMLStreamWriter

Given are the following classes:

package name.stojanok.dzone.javaxml.situation2;

import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;

public class CDataXMLStreamWriter extends DelegatingXMLStreamWriter {
	private static final String CDATA_END = "]]>";
	private static final String CDATA_BEGIN = "<![CDATA[";

	public CDataXMLStreamWriter(XMLStreamWriter del) {
		super(del);
	}

	@Override
	public void writeCharacters(String text) throws XMLStreamException {
		boolean useCData = false;
		if (text.startsWith(CDATA_BEGIN) && text.endsWith(CDATA_END)) {
			text = text.substring(CDATA_BEGIN.length(), text.length()
					- CDATA_END.length());
			useCData = true;
		}
		if (useCData) {
			super.writeCData(text);
		} else {
			super.writeCharacters(text);
		}
	}
}

package name.stojanok.dzone.javaxml.situation2;


import javax.xml.bind.annotation.adapters.XmlAdapter;







public class CdataAdapter extends XmlAdapter<String, String> {


   private static final String CDATA_END = "]]>";


   private static final String CDATA_BEGIN = "<![CDATA[";

   @Override


   public String marshal(String v) throws Exception {


     return CDATA_BEGIN + v + CDATA_END;


   }




   @Override


   public String unmarshal(String v) throws Exception {


     if (v.startsWith(CDATA_BEGIN) && v.endsWith(CDATA_END)) {


       v = v.substring(CDATA_BEGIN.length(), v.length() - CDATA_END.length());


     }


     return v;


   }



}

The test methods look this way:

   @Test



   public void testMarshallerSituation2() {


     OtherPerson otherPerson = new OtherPerson();


     Map<String, String> map = new HashMap<String, String>();


     map.put("property0", "value0");


     map.put("property1", "value1");


     otherPerson.setMap0(map);


     otherPerson.setMap1(map);

     Map<Key, Value> map0 = new HashMap<Key, Value>();
     Key key = new Key(); Value value = new Value();
     key.setKey("key0"); value.setValue("value0");
     map0.put(key, value);
     Key key0 = new Key(); Value value0 = new Value();
     key0.setKey("key1"); value0.setValue("value2");
     map0.put(key0, value0);
     otherPerson.setMap3(map0);
     otherPerson.setCdata("cdata conent <html></html>");
     otherPerson.setSomeData("a & b");
     jaxbClient.marshaller(otherPerson, file2);
   }




   @Test
   public void testUnmarshallerSituation2() {
     Object object = jaxbClient.unmarshaller(file2);
     if (object instanceof OtherPerson) {
       OtherPerson otherPerson = (OtherPerson)object;
       for (Entry<String, String> entry : otherPerson.getMap0().entrySet()) {
         LOGGER.trace(entry);
       }



       for (Entry<String, String> entry : otherPerson.getMap1().entrySet()) {
         LOGGER.trace(entry);
       }
       LOGGER.trace(otherPerson.getCdata());
     }
   }

The XML file:

<?xml version="1.0" ?>
<otherPerson xmlns:ns2="name.stojanok.dzone">
  <cdata><![CDATA[cdata conent <html></html>]]></cdata>
  <map0>
    <entry>
      <key>property1</key>
      <value>value1</value>
    </entry>
    <entry>
      <key>property0</key>
      <value>value0</value>
    </entry>
  </map0>
  <map1>
    <entry>
      <key xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">prperty1</key>
      <value xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">value1</value>
    </entry>
    <entry>
      <key xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">prperty0</key>
      <value xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">value0</value>
    </entry>
  </map1>
  <map3>
    <entry>
      <key xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="key">
        <key>key1</key>
      </key>
      <value xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="value">
        <value>value2</value>
      </value>
    </entry>
    <entry>
      <key xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="key">
        <key>key0</key>
      </key>
      <value xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="value">
        <value>value0</value>
      </value>
    </entry>
  </map3>
  <someData>a & b</someData>
</otherPerson>

Collective Polymorphism

Using different objects in a collection which are in an inheritance relationship can be very tricky if you don't know the right annotation. Here I want to show you an example where this situation is implemented.

@XMLSeeAlso({SomeClass.class})

Given are the following classes:

package name.stojanok.dzone.javaxml.situation3;

import java.util.List;

import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementWrapper;
import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement
public class Market {

	private List<Fruit> fruits;

	@XmlElementWrapper(name = "fruits")
	@XmlElement(name = "fruit")
	public List<Fruit> getFruits() {
		return fruits;
	}

	public void setFruits(List<Fruit> fruits) {
		this.fruits = fruits;
	}
}

package name.stojanok.dzone.javaxml.situation3;

import javax.xml.bind.annotation.XmlSeeAlso;

@XmlSeeAlso({Apple.class, Pear.class})
public class Fruit {

	private String color;
	private String type;
	
	public String getColor() {
		return color;
	}

	public void setColor(String color) {
		this.color = color;
	}

	public String getType() {
		return type;
	}

	public void setType(String type) {
		this.type = type;
	}
}

package name.stojanok.dzone.javaxml.situation3;

public class Apple extends Fruit {

	private String appleProperty;

	public String getAppleProperty() {
		return appleProperty;
	}

	public void setAppleProperty(String appleProperty) {
		this.appleProperty = appleProperty;
	}
}

package name.stojanok.dzone.javaxml.situation3;

public class Pear extends Fruit {

	private String pearProperty;

	public String getPearProperty() {
		return pearProperty;
	}

	public void setPearProperty(String pearProperty) {
		this.pearProperty = pearProperty;
	}
}

The test methods:

@Test
public void testMarshallerSituation3() {
	Market market = new Market();
	List<Fruit> fruits = new ArrayList<Fruit>();
	Apple apple = new Apple(); apple.setColor("red"); apple.setType("type1"); apple.setAppleProperty("some");
	fruits.add(apple);
	Pear pear = new Pear(); pear.setColor("yellow"); pear.setType("type2"); pear.setPearProperty("some");
	fruits.add(pear);
	market.setFruits(fruits);
	
	jaxbClient.marshaller(market, file3);		
}
	
@Test
public void testUnmarshallerSituation3() {
	Object object = jaxbClient.unmarshaller(file3);
	if (object instanceof Market) {
		Market market = ((Market)object);
		for (Fruit fruit : market.getFruits()) {
			if (fruit instanceof Apple) {
				LOGGER.trace("apple: " + fruit.getColor());
			} else if (fruit instanceof Pear) {
				LOGGER.trace("pear: " + fruit.getColor());
			}				
		}
	}		
}

The XML file:

<?xml version="1.0" ?>
<market xmlns:ns2="name.stojanok.dzone">
  <fruits>
    <fruit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="apple">
      <color>red</color>
      <type>type1</type>
      <appleProperty>some</appleProperty>
    </fruit>
    <fruit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="pear">
      <color>yellow</color>
      <type>type2</type>
      <pearProperty>some</pearProperty>
    </fruit>
  </fruits>
</market>

Everyday Situations


Hint

One of my tasks was to create an XML file and to convert it via unmarshalling to a Java object. Doing this, I got a situation where some of the data were not found into the object. Searching for the cause (i.e forgetting @XmlAttribute, typo of the propety and other cases) can be difficult and confusing. Another approach to find the cause is to create the XML from the annotated class with marshalling. With that "other direction" way, you can see where the differences between the generated (output) XML file and the expected (input) XML are.

Unit-Test's are good practice for testing every change of the JAXB clasess.

Problems

I had a situation while the JAXB part of the application had a problem, more precisely, exception was thrown only by executing it with the JVM of Java 7. In Java 6 everything was fine. It was primitive boolean property of a class which was not initialized explicitly. Doing that solved the problem for that case.

Another problem caused the implementation of the adapter class for the Map. In OpenJDK JVM exception was thrown by one method. Implementing as a method that has overloaded the original method solved the problem here.

You can see that there are still some differences between the JVM where there shouldn't be. But with searching the internet for bug issues and exception messages, and/or creating workarounds for those strange situations, the problems were solved.

Résumé

JAXB is a nice technology for handling XML files. Unmarshalling an XML file creates an object with all data restored from the file only by using the unmarshaller handler. This way you dont need to search for the content explicitly like doing it with the parsers of the JAXP API.

Using only Unmarshaing 

If you are using this processing method only for unmarshaling than you should use the hint mentioned above in this article. For this use case for a complex XML structure many annotated classes will be needed and at first this will look unnesessery oversized and disadvantaging.

Using only Marshalling 

If the XML structure is derived from the model of the application, then you are not creating some extra resources and waste time for it. Other is the situation when the structure of the model doesn't match the XML output. Then you do need to create extra classes only to make the XML structure like it should be.

 Marshalling and Unmarshalling

Best case is when you need less extra work in your project for this kind of processing. Then using the handlers for both JAXB methods are needed. Somethimes you need only part of global structure like persisting the object deeper in the class structure like some object reference. That is also possible, but then you get the same class via unmarshalling back. More work is needed when the persistence structure is not the same like the model of the application. Then extra work is needed for creating the classes to make the XML structure like specified.

Last word

Writing this article, I have noticed that this kind of processing needs more complex explanation than the JAXP API, because the approach of this kind of processing is bound to the model classes which can be used as the annotated classes for the JAXB, but don't have to be. The question is, does the class need to be used for this aspect or not? I think this question is used always when you need to use a class for other aspects like binding them to JAXB, JPA and other persistence output or other boundary, framework, or whatever else library or application.

Searching on the web for information for this article I found a link about EclipseLink JAXB (MOXy)'s External Mapping Document[17]. This way you can define the mapping without annotations. It was used in cases where the classes which are used for XML bindings are forbidden to be changed. Knowing this option, you have one more choice if you want to use annotation on your model classes or external mapping definition or not.

I hope I could give you summary about JAXB with the most useful annotations. Also some hints and problems were mentioned and use cases discussed. If I come to new situations and have enough motivation to write, I will write new article about that.

GitHub

The sources for this example can be checked out from: https://github.com/kstojanovski/java-and-xml. Use the java-and-xml-jaxb folder and start the test method of the TestJaxb.java file from the test folder.

XML Java (programming language)

Opinions expressed by DZone contributors are their own.

Popular on DZone

  • Chaos Engineering Tutorial: Comprehensive Guide With Best Practices
  • How To Choose the Right Streaming Database
  • Solving the Kubernetes Security Puzzle
  • A Gentle Introduction to Kubernetes

Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 600 Park Offices Drive
  • Suite 300
  • Durham, NC 27709
  • support@dzone.com
  • +1 (919) 678-0300

Let's be friends: