Extension for Jackson JSON processor that adds support for serializing POJOs as XML (and deserializing from XML) as an alternative to JSON
This projects contains Jackson extension component for
reading and writing XML encoded data.
Further, the goal is to emulate how JAXB data-binding works
with “Code-first” approach (no support is added for “Schema-first” approach).
Support for JAXB annotations is provided by JAXB annotation module;
this module provides low-level abstractions (JsonParser
, JsonGenerator
, JsonFactory
) as well as a small number of higher level
overrides needed to make data-binding work.
It is worth noting, however, that the goal is NOT to be full JAXB clone; or to be a
general purpose XML toolkit.
Specifically:
Type | Status |
---|---|
Build (CI) | |
Artifact | |
OSS Sponsorship | |
Javadocs | |
Code coverage (2.19) | |
OpenSSF Score | |
Fuzzing |
master
branch is for developing the next major Jackson version – 3.0 – but there
are active maintenance branches in which much of development happens:
2.19
is for developing the next minor 2.x version2.18
is for backported fixes to include in 2.18.x patch versions2.17
is for backported fixes to include in 2.17.x patch versionsOlder branches are usually not changed but are available for historic reasons.
All released versions have matching git tags (jackson-dataformat-xml-2.17.1
).
All modules are licensed under Apache License 2.0.
To use Jackson 2.x compatible version of this extension on Maven-based projects, use following dependency:
Maven:
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
<version>2.18.1</version>
</dependency>
Gradle:
dependencies {
implementation 'com.fasterxml.jackson.dataformat:jackson-dataformat-xml:2.18.1'
}
(or whatever version is most up-to-date at the moment)
Also: you usually also want to make sure the XML library in use is Woodstox since it is not only faster than Stax implementation JDK provides, but also works better and avoids some known issues like adding unnecessary namespace prefixes.
You can do this by adding this in your pom.xml
:
Maven:
<dependency>
<groupId>com.fasterxml.woodstox</groupId>
<artifactId>woodstox-core</artifactId>
<version>6.5.0</version>
</dependency>
Gradle:
dependencies {
implementation 'com.fasterxml.woodstox:woodstox-core:6.5.0'
}
Although this module implements low-level (JsonFactory
/ JsonParser
/ JsonGenerator
) abstractions,
most usage is through data-binding level. This is because a small number of work-arounds have been added
at data-binding level, to work around XML peculiarities: that is, the stream of JsonToken
s that the parser
produces has idiosyncracies that need special handling.
Usually you either create XmlMapper
simply by:
XmlMapper mapper = new XmlMapper();
but in case you need to configure settings, you will want to use the Builder (added in
Jackson 2.10) style construction:
XmlMapper mapper = XmlMapper.builder()
.defaultUseWrapper(false)
// enable/disable Features, change AnnotationIntrospector
.build();
Alternatively, sometimes you may want/need to configure low-level XML processing details
controlled by underlying Stax library (Woodstox, Aalto or JDK-default Oracle implementation).
If so, you will need to construct XmlMapper
with properly configured underlying factories.
This usually looks something like:
XMLInputFactory ifactory = new WstxInputFactory(); // Woodstox XMLInputFactory impl
ifactory.setProperty(WstxInputProperties.P_MAX_ATTRIBUTE_SIZE, 32000);
// configure
XMLOutputFactory ofactory = new WstxOutputFactory(); // Woodstox XMLOutputfactory impl
ofactory.setProperty(WstxOutputProperties.P_OUTPUT_CDATA_AS_TEXT, true);
XmlFactory xf = XmlFactory.builder()
.xmlInputFactory(ifactory) // note: in 2.12 and before "inputFactory()"
.xmlOutputFactory(ofactory) // note: in 2.12 and before "outputFactory()"
.builder();
XmlMapper mapper = new XmlMapper(xf); // there are other overloads too
For configurable properties, you may want to check out
Configuring Woodstox XML parser
As the well as the Woodstox properties specified above, you can also call WstxInputFactory#getConfig()
and modify the ReaderConfig.
One useful setting is the maxElementDepth.
Usage of this library on Android is currently not supported. This is due to the fact that the Stax API is unavailable on the Android platform, and attempts to declare an explicit dependency on the Stax API library will result in errors at build time (since the inclusion of the javax.*
namespace in apps is restricted).
For more on the issues, see:
Note that as per above articles it MAY be possible to use the module on Android, but it unfortunately requires
various work-arounds and development team can not do much to alleviate these issues.
Suggestions for improvements would be welcome; discussions on
Jackson users list encouraged.
Serialization is done very similar to JSON serialization: all that needs to change is ObjectMapper
instance to use:
// Important: create XmlMapper; it will use proper factories, workarounds
ObjectMapper xmlMapper = new XmlMapper();
String xml = xmlMapper.writeValueAsString(new Simple());
// or
xmlMapper.writeValue(new File("/tmp/stuff.xml"), new Simple());
and with POJO like:
public class Simple {
public int x = 1;
public int y = 2;
}
you would get something like:
<Simple>
<x>1</x>
<y>2</y>
</Simple>
(except that by default output is not indented: you can enabled indentation using standard Jackson mechanisms)
Similar to serialization, deserialization is not very different from JSON deserialization:
ObjectMapper xmlMapper = new XmlMapper();
Simple value = xmlMapper.readValue("<Simple><x>1</x><y>2</y></Simple>", Simple.class);
It is also possible to do incremental writes. This is done by creating Stax
XMLInputFactory
separately (similar to how with JSON you would create JsonGenerator
), and then:
// First create Stax components we need
XMLInputFactory xmlInputFactory = XMLInputFactory.newFactory();
XMLOutputFactory xmlOutputFactory = XMLOutputFactory.newFactory();
StringWriter out = new StringWriter();
XMLStreamWriter sw = xmlOutputFactory.createXMLStreamWriter(out);
// then Jackson components
XmlMapper mapper = new XmlMapper(xmlInputFactory);
sw.writeStartDocument();
sw.writeStartElement("root");
// Write whatever content POJOs...
SomePojo value1 = ...;
OtherPojo value2 = ...;
mapper.writeValue(sw, value1);
mapper.writeValue(sw, value2);
// and/or regular Stax output
sw.writeComment("Some insightful commentary here");
sw.writeEndElement();
sw.writeEndDocument();
Similarly it is possible to read content, sub-tree by sub-tree; assuming similar XML content
we would use
XMLInputFactory f = XMLInputFactory.newFactory();
File inputFile = ...;
XMLStreamReader sr = f.createXMLStreamReader(new FileInputStream(inputFile));
XmlMapper mapper = new XmlMapper();
sr.next(); // to point to <root>
sr.next(); // to point to root-element under root
SomePojo value1 = mapper.readValue(sr, SomePojo.class);
// sr now points to matching END_ELEMENT, so move forward
sr.next(); // should verify it's either closing root or new start, left as exercise
OtherPojo value = mapper.readValue(sr, OtherPojo.class);
// and more, as needed, then
sr.close();
In addition to standard Jackson annotations and optional JAXB (javax.xml.bind.annotation
), this project also adds a couple of its own annotations for convenience, to support XML-specific details:
@JacksonXmlElementWrapper
allows specifying XML element to use for wrapping List
and Map
properties@JacksonXmlProperty
allows specifying XML namespace and local name for a property; as well as whether property is to be written as an XML element or attribute.@JacksonXmlRootElement
allows specifying XML element to use for wrapping the root element (default uses ‘simple name’ of the value class)@JacksonXmlText
allows specifying that value of one property is to be serialized as “unwrapped” text, and not in an element.@JacksonXmlCData
allows specifying that the value of a property is to be serialized within a CData tag.for a longer description, check out XML module annotations.
Currently, following limitations exist beyond general Jackson (JSON) limitations:
JsonNode
, ObjectMapper.readTree()
) is based on JSON content model and it does not match exactly with XML infoset
2.12
, handling of repeated XML elements was problematic (it could only retain the last element read), but #403 improves handlingjava.lang.Integer
)String
s (and types that serialize as Strings such as Timestamps, Date/Time values)Enum
sjava.util.Collection
values (Lists, Sets)Collection
s, for example, often work.@JacksonXmlElementWrapper.useWrapping
can be set to ‘false’ to disable wrappingJacksonXmlModule.setDefaultUseWrapper()
can be used to specify whether “wrapped” or “unwrapped” setting is the defaultWRAPPER_ARRAY
, for example is not supported due to problems with reference to mapping of XML, Arrays)
SerializationFeature.WRAP_ROOT_VALUE
/ DeserializationFeature.UNWRAP_ROOT_VALUE
, so that:
Jackson components are supported by the Jackson community through mailing lists, Gitter forum, Github issues. See Participation, Contributing for full details.
Available as part of the Tidelift Subscription.
The maintainers of jackson-dataformat-xml
and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source dependencies you use to build your applications. Save time, reduce risk, and improve code health, while paying the maintainers of the exact dependencies you use. Learn more.