Serializing objects to XML – Java I/O: Context-Specific Deserialization Filters
126. Serializing objects to XML
Serializing/deserializing objects to XML via the JDK API can be accomplished via java.beans.XMLEncoder, respectively XMLDecoder. The XMLEncoder API relies on Java Reflection to discover the object’s fields and write them in XML format. This class can encode objects that respect the Java Beans contract (https://docs.oracle.com/javase/tutorial/javabeans/writing/index.html). Basically, the object’s class should contain a public no-arguments constructor and public getters and setters for private/protected fields/properties. Implementing Serializable is not mandatory for XMLEncoder/XMLDecoder, so we can serialize/deserialize objects that don’t implement Serializable. Here it is a helper method that encodes the given Object to XML:
public static String objectToXML(Object obj)
throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try ( XMLEncoder encoder = new XMLEncoder(
new BufferedOutputStream(baos))) {
encoder.writeObject(obj);
}
baos.close();
return new String(baos.toByteArray());
}
The reverse process (deserialization) uses XMLDecoder as follows:
public static Object XMLToObject(String xml)
throws IOException {
try ( InputStream is
= new ByteArrayInputStream(xml.getBytes());
XMLDecoder decoder = new XMLDecoder(is)) {
return decoder.readObject();
}
}
The XMLEncoder/XMLDecoder is much more flexible than the writeObject()/readObject() API. For instance, if a field/property is added/removed/renamed or its type has changed then the decoding process skips everything it cannot decode and tries to decode as much as possible without throwing an exception.Another common approach relies on the third-party library Jackson 2.x which comes with XmlMapper. This library should be added as a dependency (of course, if you don’t have it already present in your project):
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
<version>2.x</version>
</dependency>
Next, we create an instance of XmlMapper:
XmlMapper xmlMapper = new XmlMapper();
Via XmlMapper, we can serialize objects as XML as follows (there is no problem if the object’s class doesn’t implement Serializable and/or it doesn’t contain setters):
public static String objectToXMLJackson(Object obj)
throws JsonProcessingException {
XmlMapper xmlMapper = new XmlMapper();
if (xmlMapper.canSerialize(obj.getClass())) {
return xmlMapper.writeValueAsString(obj);
}
return “”;
}
Calling this method can be done as follows (melon is an instance of the Melon class):
String melonSer = Converters.objectToXMLJackson(melon);
The reverse process can rely on readValue() as follows:
public static <T> T XMLToObjectJackson(
String xml, Class<T> clazz)
throws JsonProcessingException {
XmlMapper xmlMapper = new XmlMapper();
return xmlMapper.readValue(xml, clazz);
}
Calling this method can be done as follows:
Melon melonDeser = Converters
.XMLToObjectJackson(melonSer, Melon.class);
Take your time to explore the XmlMapper API since it has a lot more to offer. For now, consider running the bundled code to see the XML produced by each of these two approaches.If you plan to serialize/deserialize an object to JSON then consider Java Coding Problems, First Edition, Problem 141 for a comprehensive set of examples based on Jsonb, Jackson, and Gson.