Whenever you encounter a special task in parsing XML into Java objects (un-marshaling) or Java objects into XML (marshaling) utilizing JAXB, you can get angry quite easily.

Examples are ...

  • no namespace in XML, may it be marshaling or un-marshaling
  • sanitized output into XML when marshaling and vice versa
  • formatted output, which is easier to read

Facing these issues many times, I decided to create a special Eclipse Java project for JAXB marshaling and un-marshaling, which I can reference in other Java projects.

This is much quicker anyhow than repetitive code in different places.

Source Code

Now let's have a look at the project. Of course it is a JAXB project, but it does not contain any classes derived from XML schemas. All hand made, baby!

The project holds only one package called de.consulting.bolte.xml with four classes. My plan is to use it also for other XML related things later on, but so far I am very happy with JAXB.

The classes are as follows.

Class Name Description
EntityMarshallingUtils A utility class for converting between Java objects and xml and vice versa.
NamespaceFilter extends XMLFilterImpl A utility class used to override the XML target namespace defined in
a Java package info created using JAXB. This is for example necessary
when the XML target namespace in a read XML differs from the target
namespace in the Java package info for JAXB generated classes.
Another use case is reading XML data that does not provide any target
namespace. Although this is considered bad style, it is often seen in
real life.
NoNameSpaceContext A utility class used to marshal Java objects into XML without providing any target namespace. Although this is considered bad practice you come across this in real life e.g. when a system expects an XML without any namespace.
XmlEscapeHandler This class is an implementation of the CharacterEscapeHandler interface provided by JAXB in order to allow customization on how to escape characters when marshalling Java objects into XML.

EntityMarshallingUtils

The only class I directly work with is the EntityMarshallingUtils. This class uses the other three classes to provide service in marshaling and unmarshaling.

  • introspect allows to parse sub nodes of an XML document, which are not flagged as root nodes in an XML document.
  • marshal parses a Java object into XML, but it uses the XML target namespace of the package-info.java of the objects class.
  • marshalNoNamespace parses a Java object into XML, ignoring any namespace.
  • transform will pretty print any given XML String.
  • unmarshal parses a provided XML String into a Java object, but it uses the target namespace of the package-info.java of the objects class.
  • unmarshal(Class<T> c, String xml, String targetNamespace) parses a provided XML into a Java object, using the provided XML target namespace.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
package de.consulting.bolte.xml;
 
import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import java.io.Writer;
 
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.JAXBIntrospector;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.UnmarshallerHandler;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
 
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLFilter;
import org.xml.sax.XMLReader;
 
import com.sun.xml.internal.bind.marshaller.CharacterEscapeHandler;
 
/**
 * A utility class for converting between jaxb annotated objects and xml.
 */
public class EntityMarshallingUtils {
	/**
	 * <p>
	 * Unmarshals / deserializes a provided XML String into Java objects of
	 * provided class. The difference to the methods <code>unmarshal</code> is
	 * that this method is capable of parsing sub nodes from a XML document
	 * which are not annotated as root nodes in a JAXB project.
	 * </p>
	 *
	 * @param targetClass
	 *            - the class of a target type for a serialization.
	 * @param factoryPackagePath
	 *            - the Java package path a target class resides in.
	 * @param xml
	 *            - an XML String to be serialized.
	 * @return a deserialization of the XML into an object of type T of class
	 *         Class<T>
	 * @throws JAXBException
	 */
	@SuppressWarnings("unchecked")
	public static <T> T introspect(Class<T> targetClass, String factoryPackagePath, String xml) throws JAXBException {
		T ret;
 
		/*
		 * In order to unmarshal / deserialize nodes in an XML file, which are
		 * not annotated as a root node in a JAXB project, we have to provide
		 * the package in which the ObjectFactory created by JAXB resides.
		 */
		JAXBContext ctx = JAXBContext.newInstance(factoryPackagePath);
		Unmarshaller marshaller = ctx.createUnmarshaller();
		ret = (T) JAXBIntrospector.getValue(marshaller.unmarshal(new StringReader(xml)));
 
		return ret;
	}
 
	/**
	 * <p>
	 * Marshals / serializes a provided instance of a provided class into XML
	 * regarding to the XML target namespace maintained in the package-info.java
	 * of the package of the provided class.
	 * </p>
	 * <p>
	 * The XML output is formatted using the
	 * <code>Marshaller.JAXB_FORMATTED_OUTPUT</code> option.
	 * </p>
	 * <p>
	 * Uses the non API / Java internal class
	 * <code>CharacterEscapeHandler</code> in order to escape special characters
	 * causing trouble in XML. The code in question references the
	 * CharacterEscapeHandler class to set the property to a custom character
	 * escape handler. I did not find the proper way, without using the non API
	 * class to overwrite the Unmarshallers property for the escape handler to
	 * use during unmarshalling. Below the code in question.
	 * </p>
	 * <p>
	 * <code>
	 * marshaller.setProperty(CharacterEscapeHandler.class.getName(),
				new XmlEscapeHandler());
	 * </code>
	 * </p>
	 *
	 * @param c
	 *            - the class of the type to serialize.
	 * @param o
	 *            - the instance holding the data to serialize.
	 * @return a string representation of the data provided in handed instance.
	 * @throws Exception
	 */
	public static <T> String marshal(Class<T> c, Object o) throws Exception {
		JAXBContext ctx = JAXBContext.newInstance(c);
		Marshaller marshaller = ctx.createMarshaller();
		StringWriter entityXml = new StringWriter();
		/*
		 * Format the output in the XML file for readability.
		 */
		marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
		/*
		 * Switch the escape handler to a custom implementation for escaped XML
		 * code.
		 */
		marshaller.setProperty(CharacterEscapeHandler.class.getName(), new XmlEscapeHandler());
		marshaller.marshal(o, entityXml);
		String entityString = entityXml.toString();
 
		return entityString;
	}
 
	/**
	 * <p>
	 * Marshals / serializes a provided instance of a provided class into XML
	 * <b>ignoring</b> the XML target namespace maintained in the
	 * package-info.java of the package of the provided class.
	 * </p>
	 * <p>
	 * The XML output is formatted using the method transform in this class
	 * relying on a JDK implementation of Transformer.
	 * <code>Marshaller.JAXB_FORMATTED_OUTPUT</code> option is switched on, but
	 * does not work since the StringWriter is passed to a XMLStreamWriter,
	 * which simply ignores indentation and line breaks. The JDK implementation
	 * of XMLStreamWriter simply writes the XML in one line, which is why we
	 * need to rely on the Transformer to pretty print our XML. Annoying!
	 * </p>
	 * <p>
	 * Uses the non API / Java internal class
	 * <code>CharacterEscapeHandler</code> in order to escape special characters
	 * causing trouble in XML. The code in question references the
	 * CharacterEscapeHandler class to set the property to a custom character
	 * escape handler. I did not find the proper way, without using the non API
	 * class to overwrite the Unmarshallers property for the escape handler to
	 * use during unmarshalling. Below the code in question.
	 * </p>
	 * <code>
	 * marshaller.setProperty(CharacterEscapeHandler.class.getName(),
				new XmlEscapeHandler());
	 * </code>
	 *
	 * @param c
	 *            - the class of a type to serialize into XML.
	 * @param o
	 *            - the instance containing the data to serialize into XML.
	 * @return a XML representation of the data provided in handed instance o
	 *         without any namespace reference in the result String.
	 * @throws Exception
	 */
	public static <T> String marshalNoNamespace(Class<T> c, Object o) throws Exception {
		JAXBContext ctx = JAXBContext.newInstance(c);
		Marshaller marshaller = ctx.createMarshaller();
		StringWriter entityXml = new StringWriter();
		XMLStreamWriter xml = XMLOutputFactory.newFactory().createXMLStreamWriter(entityXml);
		String xmlText = null;
		/*
		 * Create a new instance of a custom implementation of the interface
		 * NamespaceContext.
		 */
		NoNameSpaceContext nsContext = new NoNameSpaceContext();
		xml.setNamespaceContext(nsContext);
 
		/*
		 * Format the output in the marshaled XML for readability.
		 */
		marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
		/*
		 * Switch the escape handler to a custom implementation for escaped XML
		 * code.
		 */
		marshaller.setProperty(CharacterEscapeHandler.class.getName(), new XmlEscapeHandler());
		/*
		 * Marshal and write the XML data from a provided instance into a
		 * XMLStreamWriter as well as a registered Writer.
		 */
		marshaller.marshal(o, xml);
		/*
		 * Ensure that the returned XML is pretty printed / has correct
		 * indentation.
		 */
		xmlText = transform(entityXml.toString());
		/*
		 * Return the output from a registered Writer as String (XML).
		 */
		return xmlText;
	}
 
	/**
	 * Uses the JDK implementation of <code>Transformer</code> in order to
	 * transform a provided XML String into pretty printed XML with correct
	 * indentation and line breaks.
	 *
	 * @param xml
	 *            - an XML without indentation or line breaks.
	 * @return an XML with correct indentation and line breaks.
	 * @throws XMLStreamException
	 * @throws TransformerException
	 */
	public static String transform(String xml) throws XMLStreamException, TransformerException {
		Transformer t = TransformerFactory.newInstance().newTransformer();
 
		t.setOutputProperty(OutputKeys.INDENT, "yes");
		// t.setOutputProperty("{http://xml.apache.org/xslt}indent-amount",
		// "2");
		Writer out = new StringWriter();
		t.transform(new StreamSource(new StringReader(xml)), new StreamResult(out));
 
		return out.toString();
	}
 
	/**
	 * <p>
	 * Unmarshals / deserializes a provided XML String into Java objects of
	 * provided class <b>regarding</b> the XML target namespace maintained in
	 * the package-info.java of the package of the provided class.
	 * </p>
	 *
	 * @param <T>
	 *            the type we want to convert XML into.
	 * @param c
	 *            the class of the parameterized type.
	 * @param xml
	 *            an instance XML description.
	 * @return a deserialization of the XML into an object of type T of class
	 *         Class<T>.
	 * @throws javax.xml.bind.JAXBException
	 */
	@SuppressWarnings("unchecked")
	public static <T> T unmarshal(Class<T> c, String xml) throws JAXBException {
		T res;
 
		if (c == xml.getClass()) {
			res = (T) xml;
		}
 
		else {
			JAXBContext ctx = JAXBContext.newInstance(c);
			Unmarshaller unmarshaller = ctx.createUnmarshaller();
			res = (T) unmarshaller.unmarshal(new StringReader(xml));
		}
 
		return res;
	}
 
	/**
	 * <p>
	 * Unmarshals / deserializes a provided XML String into Java objects of
	 * provided class <b>disregarding</b> the XML target namespace maintained in
	 * the package-info.java of the package of the provided class.
	 * </p>
	 * <p>
	 * Instead of the target namespcae maintained in a package-info
	 * corresponding provided class, the calling method can define the XML
	 * target namespace to be used.
	 * </p>
	 *
	 * @param <T>
	 *            the type we want to convert XML into.
	 * @param c
	 *            the class of the parameterized type.
	 * @param xml
	 *            an instance XML description.
	 * @return a deserialization of a provided XML into an object of type T of
	 *         provided class c.
	 * @throws javax.xml.bind.JAXBException
	 * @throws SAXException
	 * @throws ParserConfigurationException
	 * @throws IOException
	 */
	@SuppressWarnings("unchecked")
	public static <T> T unmarshal(Class<T> c, String xml, String targetNamespace)
			throws JAXBException, ParserConfigurationException, SAXException, IOException {
		T res;
 
		if (c == xml.getClass()) {
			res = (T) xml;
		} else {
			XMLFilter filter = new NamespaceFilter(targetNamespace);
 
			SAXParserFactory spf = SAXParserFactory.newInstance();
			SAXParser sp = spf.newSAXParser();
			XMLReader xr = sp.getXMLReader();
			filter.setParent(xr);
 
			JAXBContext ctx = JAXBContext.newInstance(c);
			Unmarshaller unMarshaller = ctx.createUnmarshaller();
			UnmarshallerHandler unmarshallerHandler = unMarshaller.getUnmarshallerHandler();
			filter.setContentHandler(unmarshallerHandler);
			InputSource data = new InputSource(new StringReader(xml));
			filter.parse(data);
			res = (T) unmarshallerHandler.getResult();
		}
 
		return res;
	}
 
}

 

NamespaceFilter

A utility class used to override the XML target namespace defined in a Java package info created using JAXB. This is for example necessary when the XML target namespace in a read XML differs from the target namespace in the Java package info for JAXB generated classes.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
package de.consulting.bolte.xml;
 
import org.xml.sax.SAXException;
import org.xml.sax.helpers.XMLFilterImpl;
 
/**
 * @author Alexander Bolte (Bolte Consulting)
 *         <p>
 *         A utility class used to override the XML target namespace defined in
 *         a Java package info created using JAXB. This is for example necessary
 *         when the XML target namespace in a read XML differs from the target
 *         namespace in the Java package info for JAXB generated classes.
 *         </p>
 *         <p>
 *         An XMLFilter sits as an extra, abstract layer between a real
 *         XMLReader and an application a reader delivers events and information
 *         to.
 *         </p>
 *         <p>
 *         This specific implementation of the interface XMLFilter simply
 *         ensures that JAXB is using a provided target namespace instead of the
 *         JAXB package info namespace. Simple but very useful.
 *         </p>
 */
public class NamespaceFilter extends XMLFilterImpl {
	private String namespace = "";
 
	public NamespaceFilter(String namespace) {
		this.namespace = namespace;
	}
 
	@Override
	public void endElement(String uri, String localName, String qName) throws SAXException {
		super.endElement(namespace, localName, qName);
	}
 
	@Override
	public void startElement(String uri, String localName, String qName, org.xml.sax.Attributes atts)
			throws SAXException {
		super.startElement(namespace, localName, qName, atts);
	}
}

NoNameSpaceContext

A utility class used to marshal Java objects into XML without providing any target namespace.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
package de.consulting.bolte.xml;
 
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
 
import javax.xml.XMLConstants;
import javax.xml.namespace.NamespaceContext;
 
/**
 * @author Alexander Bolte
 *         <p>
 *         A utility class used to marshal Java objects into XML without
 *         providing any target namespace. Although this is considered bad
 *         practice you come across this in real life e.g. when a system expects
 *         an XML without any namespace.
 *         </p>
 */
public class NoNameSpaceContext implements NamespaceContext {
	private Map<String, String> urisByPrefix = new HashMap<String, String>();
 
	private Map<String, Set> prefixesByURI = new HashMap<String, Set>();
 
	public NoNameSpaceContext() {
		/*
		 * addNamespace(XMLConstants.XML_NS_PREFIX, XMLConstants.XML_NS_URI);
		 * addNamespace(XMLConstants.XMLNS_ATTRIBUTE,
		 * XMLConstants.XMLNS_ATTRIBUTE_NS_URI);
		 */
	}
 
	public synchronized void addNamespace(String prefix, String namespaceURI) {
		urisByPrefix.put(prefix, namespaceURI);
		if (prefixesByURI.containsKey(namespaceURI)) {
			(prefixesByURI.get(namespaceURI)).add(prefix);
		} else {
			Set<String> set = new HashSet<String>();
			set.add(prefix);
			prefixesByURI.put(namespaceURI, set);
		}
	}
 
	@Override
	public String getNamespaceURI(String prefix) {
		/*
		 * if (prefix == null) throw new IllegalArgumentException(
		 * "prefix cannot be null"); if (urisByPrefix.containsKey(prefix))
		 * return (String) urisByPrefix.get(prefix); else
		 */
		return XMLConstants.NULL_NS_URI;
	}
 
	@Override
	public String getPrefix(String namespaceURI) {
		if (!getPrefixes(namespaceURI).hasNext()) {
			return "";
		} else {
			return (String) getPrefixes(namespaceURI).next();
		}
 
	}
 
	@Override
	public Iterator getPrefixes(String namespaceURI) {
		/*
		 * if (namespaceURI == null) throw new IllegalArgumentException(
		 * "namespaceURI cannot be null"); if
		 * (prefixesByURI.containsKey(namespaceURI)) { return ((Set)
		 * prefixesByURI.get(namespaceURI)).iterator(); } else {
		 */
		return Collections.EMPTY_SET.iterator();
		// }
	}
}

XmlEscapeHandler

This class is an implementation of the CharacterEscapeHandler interface provided by JAXB in order to allow customization on how to escape characters when marshaling Java objects into XML.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
/**
 *
 */
package de.consulting.bolte.xml;
 
import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;
 
import com.sun.xml.internal.bind.marshaller.CharacterEscapeHandler;
 
/**
 * This class is an implementation of the CharacterEscapeHandler interface
 * provided by JAXB in order to allow customization on how to escape characters
 * when marshaling Java objects into XML.
 *
 * @author Alexander Bolte (Bolte Consulting)
 */
public class XmlEscapeHandler implements CharacterEscapeHandler {
 
	/*
	 * (non-Javadoc)
	 *
	 * @see
	 * com.sun.xml.internal.bind.marshaller.CharacterEscapeHandler#escape(char
	 * [], int, int, boolean, java.io.Writer)
	 *
	 * @Remarks - this implementation of the interface CharacterEscapeHandler
	 * escapes special characters. This is useful, if your XML for example needs
	 * to support escaped XML code as values within XML tags.
	 */
	@Override
	public void escape(char[] buf, int start, int len, boolean isAttValue, Writer out) throws IOException {
		StringWriter buffer = new StringWriter();
 
		for (int i = start; i < start + len; i++) {
			buffer.write(buf[i]);
		}
 
		String st = buffer.toString();
 
		st = buffer.toString().replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;").replace("'", "&apos;")
				.replace("\"", "&quot;");
 
		out.write(st);
	}
}