Git Product home page Git Product logo

jackson-dataformat-xml's Introduction

Overview

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:

  • While XML serialization should ideally be similar to JAXB output, deviations are not automatically considered flaws (there are reasons for some differences)
  • What should be guaranteed is that any XML written using this module must be readable using module as well ("read what I wrote"): that is, we do aim for full round-trip support
  • From above: there are XML constructs that this module will not be able to handle; including some cases JAXB (and other Java XML libraries) support
  • This module also supports constructs and use cases JAXB does not handle: specifically, rich type and object id support of Jackson are supported.

Status

Type Status
Build (CI) Build (github)
Artifact Maven Central
OSS Sponsorship Tidelift
Javadocs Javadoc
Code coverage (2.18) codecov.io
OpenSSF Score OpenSSF  Scorecard
Fuzzing Fuzzing Status

Branches

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.18 is for developing the next minor 2.x version
  • 2.17 is for backported fixes to include in 2.17.x patch versions
  • 2.16 is for backported fixes to include in 2.16.x patch versions
  • 2.15 is for backported fixes to include in 2.15.x patch versions

Older branches are usually not changed but are available for historic reasons. All released versions have matching git tags (jackson-dataformat-xml-2.17.1).

License

All modules are licensed under Apache License 2.0.

Maven dependency

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.17.1</version>
</dependency>

Gradle:

dependencies {
    implementation 'com.fasterxml.jackson.dataformat:jackson-dataformat-xml:2.17.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'
}

Usage

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 JsonTokens 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.

Android quirks

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.

Serializing POJOs as XML

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)

Deserializing POJOs from XML

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);

Incremental/partial reading/writing (2.4+)

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();

Additional annotations

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.

Known Limitations

Currently, following limitations exist beyond general Jackson (JSON) limitations:

  • Streaming model is only meant to be used through databinding: direct usage is possible but not supported
  • Tree Model (JsonNode, ObjectMapper.readTree()) is based on JSON content model and it does not match exactly with XML infoset
    • Mixed content (both textual content and elements as children of an element) not supported: text, if any, will be lost
    • Prior to 2.12, handling of repeated XML elements was problematic (it could only retain the last element read), but #403 improves handling
  • Root value should be a POJO (that is, a Java value expressed as a set of properties (key/value pairs)); and specifically following types can be serialized as properties but may not work as intended as root values
    • Primitive/Wrapper values (like java.lang.Integer)
    • Strings (and types that serialize as Strings such as Timestamps, Date/Time values)
    • Enums
    • Java arrays
    • java.util.Collection values (Lists, Sets)
    • Note: over time some level of support has been added, and Collections, for example, often work.
  • Lists and arrays are "wrapped" by default, when using Jackson annotations, but unwrapped when using JAXB annotations (if supported, see below)
    • @JacksonXmlElementWrapper.useWrapping can be set to 'false' to disable wrapping
    • JacksonXmlModule.setDefaultUseWrapper() can be used to specify whether "wrapped" or "unwrapped" setting is the default
  • Polymorphic Type Handling works, but only some inclusion mechanisms are supported (WRAPPER_ARRAY, for example is not supported due to problems with reference to mapping of XML, Arrays)
    • JAXB-style "compact" Type Id where property name is replaced with Type Id is not supported.
  • Mixed Content (elements and text in same element) is not supported in databinding: child content must be either text OR element(s) (attributes are fine)
  • While XML namespaces are recognized, and produced on serialization, namespace URIs are NOT verified when deserializing: only local names are matched
    • This also means that elements that only differ by namespace cannot be used.
  • Root name wrapping support is incomplete (SerializationFeature.WRAP_ROOT_VALUE / DeserializationFeature.UNWRAP_ROOT_VALUE, so that:
    • Serialization DOES NOT add wrapping (at least up to and including 2.13)
    • Deserialization DOES unwrap root element (except for Jackson 2.12.x that temporarily removed support altogether -- fixed in 2.13.0)

Support

Community support

Jackson components are supported by the Jackson community through mailing lists, Gitter forum, Github issues. See Participation, Contributing for full details.

Enterprise support

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.


See Also

jackson-dataformat-xml's People

Contributors

ahcodedthat avatar arthurscchan avatar arturdryomov avatar bpasson avatar cowtowncoder avatar daannijkamp avatar dependabot[bot] avatar elexx avatar goooler avatar gtrog avatar joohyukkim avatar lkorth avatar mbladel avatar mensinda avatar migwel avatar naveensrinivasan avatar nurkiewicz avatar philzen avatar pjfanning avatar prb avatar ptziegler avatar simoncockx avatar simonetripodi avatar splatch avatar tamersaadeh avatar tatu-at-salesforce avatar timrs2998 avatar valery1707 avatar vmi avatar yawkat avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

jackson-dataformat-xml's Issues

unnecessary xmlns="" in the root element

ObjectMapper xmlMapper = new XmlMapper();
String xml = xmlMapper.writeValueAsString(new Simple());

and with POJO like:

public class Simple {
public int x = 1;
public int y = 2;
}

you would get something like:

<Simple xmlns="">
  <x>1</x>
  <y>2</y>
</Simple>

There is a xmlns="" attribute at the root element, which was not expected.
Using version 2.0.4 (dataformat-xml) with 2.0.5 (core)

XmlMapper parse &<> Instead of the expected

i want to parse map to xml string

lib:
jackson-annotations-2.0.2.jar
jackson-core-2.0.2.jar
jackson-databind-2.0.2.jar
jackson-dataformat-xml-2.0.2.jar
stax2-api-3.1.1.jar

code:
public static void main(String[] args) throws Exception {
XmlMapper mapper = new XmlMapper();
Map xx = new HashMap();
xx.put("asd", "asd ##&## xx");
String xxx = mapper.writeValueAsString(xx);
System.out.println(xxx);
}

this map parse to xml i want: asd ##&##xx

but the actual: asd ##&## xx&asd ##&##xx

Support root level collection serialization

I have done some tests with 2.1.0-SNAPSHOT since on RESThub project (https://github.com/resthub/resthub-spring-stack) we are waiting XML serialization of unwrapped lists for a while.

We use generic SpringMVC controller with Jackson 2.x for serialization.

@RequestMapping(method = RequestMethod.GET, params="page=no")
@responsebody
public List findAllNotPaginated() {
return (List)repository.findAll();
}

For JSON we get this normal output :

[{"id":17,"name":"toto"},{"id":18,"name":"toto"}]

For XML we get this bogus output (the same than is 2.0 I think) :

<ArrayList xmlns=""><id>41</id><name>toto</name></ArrayList><zdef1166645736:ArrayList xmlns:zdef1166645736=""><zdef1166645736:id>42</zdef1166645736:id><zdef1166645736:name>toto</zdef1166645736:name></zdef1166645736:ArrayList>

Is there something to do in order to activate Jackson 2.1 XML unwrapped list support, or is it a bug ?

Current master failing?

It seems like the current master is failing compilation. (I did manually delete all 1.8.0 files from my repository before posting this, just in case I had some old versions lying around.) Here's what I see:

cwinters@abita:#..jackson-xml-databind-orig$ rm -rf target
cwinters@abita:#..jackson-xml-databind-orig$ mvn install
[INFO] Scanning for projects...
[INFO] ------------------------------------------------------------------------
[INFO] Building Jackson-XML-databind
[INFO]    task-segment: [install]
[INFO] ------------------------------------------------------------------------
Downloading: http://repo1.maven.org/maven2/org/codehaus/jackson/jackson-mapper-asl/1.8.0/jackson-mapper-asl-1.8.0.pom

Downloading: http://repo1.maven.org/maven2/org/codehaus/jackson/jackson-core-asl/1.8.0/jackson-core-asl-1.8.0.pom

Downloading: http://repo1.maven.org/maven2/org/codehaus/jackson/jackson-xc/1.8.0/jackson-xc-1.8.0.pom

Downloading: http://repo1.maven.org/maven2/org/codehaus/jackson/jackson-mapper-asl/1.8.0/jackson-mapper-asl-1.8.0.jar

Downloading: http://repo1.maven.org/maven2/org/codehaus/jackson/jackson-core-asl/1.8.0/jackson-core-asl-1.8.0.jar

Downloading: http://repo1.maven.org/maven2/org/codehaus/jackson/jackson-xc/1.8.0/jackson-xc-1.8.0.jar

[INFO] [enforcer:enforce {execution: enforce-maven}]
[INFO] [resources:resources {execution: default-resources}]
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] skip non existing resourceDirectory /opt/java/jackson/jackson-src/jackson-xml-databind-orig/src/main/resources
[INFO] [compiler:compile {execution: default-compile}]
[INFO] Compiling 16 source files to /opt/java/jackson/jackson-src/jackson-xml-databind-orig/target/classes
[INFO] ------------------------------------------------------------------------
[ERROR] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Compilation failure

/opt/java/jackson/jackson-src/jackson-xml-databind-orig/src/main/java/com/fasterxml/jackson/xml/XmlTypeResolverBuilder.java:[142,12] cannot find symbol
symbol  : constructor ClassNameIdResolver(org.codehaus.jackson.type.JavaType)
location: class org.codehaus.jackson.map.jsontype.impl.ClassNameIdResolver

/opt/java/jackson/jackson-src/jackson-xml-databind-orig/src/main/java/com/fasterxml/jackson/xml/XmlTypeResolverBuilder.java:[163,12] cannot find symbol
symbol  : constructor MinimalClassNameIdResolver(org.codehaus.jackson.type.JavaType)
location: class org.codehaus.jackson.map.jsontype.impl.MinimalClassNameIdResolver

/opt/java/jackson/jackson-src/jackson-xml-databind-orig/src/main/java/com/fasterxml/jackson/xml/ser/XmlBeanSerializer.java:[81,4] method does not override or implement a method from a supertype

[INFO] ------------------------------------------------------------------------
[INFO] For more information, run Maven with the -e switch
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 24 seconds
[INFO] Finished at: Fri May 06 15:04:04 EDT 2011
[INFO] Final Memory: 25M/171M
[INFO] ------------------------------------------------------------------------

I tried some very naive fixes (passing in 'null' for the missing TypeFactory but failed.

Support non-wrapped array serialization

Currently only "wrapped" arrays / collections are supported (meaning there is an element for array, and then one for each entry); but not "unwrapped" mode (only entries have xml element, not array itself). Since latter is the default for JAXB, it would be nice to support it.

Differences between ObjectMapper and XmlMapper when using custom serializer

I have a problem when trying to deserialize string produced by XmlMapper.
Simple Item and Foo class:

public class Item {
  public String name;
  public Foo obj;
  public Item(String name, Foo obj) {
    this.name = name;
    this.obj = obj;
  }
}

public class Foo {
  public String name;
  public Foo(String name) {
    this.name = name;
  }
}

My serializer looks as following:

public class ItemSerializer extends StdSerializer<Item> {
  public ItemSerializer() {
    super(Item.class);
  }

  public void serialize(Item value, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonGenerationException {
    jgen.writeStartObject();
    jgen.writeObjectField("obj", value.obj);
    jgen.writeStringField("name", value.name);
    jgen.writeEndObject();
  }
}

Deserializer:

public class ItemDeserializer extends StdDeserializer<Item> {
  public ItemDeserializer() {
    super(Item.class);
  }

  public Item deserialize(JsonParser, DeserializationContext ctxt) throws IOException, JsonProcessingException {
    ObjectCodec oc = jp.getCodec();
    JsonNode json = oc.readTree(jp);
    // other code
  }
}

Test:


public class Test {
  public static void main(String[] args) {
    ObjectMapper json = new ObjectMapper();
    XmlMapper xml = new XmlMapper();
    SimpleModule m = new SimpleModule("module", new Version(1,0,0,null,null,null));
    m.addSerializer(Item.class, new ItemSerializer());
    m.addDeserializer(Item.class, new ItemDeserializer());
    json.registerModule(m);
    xml.registerModule(m);

    Item value = new Item("itemName", new Foo("fooName"));
    String a = json.writeValueAsString(value);
    String b = xml.writeValueAsString(value);

    Item x = json.readValue(a, Item.class);
    Item y = xml.readValue(b, Item.class);
  }
}

Second deserialization (y variable) fails. The problem is in deserializer at last shown line: variable json has different values for XmlMapper and for ObjectMapper.

  1. value with ObjectMapper:
{"obj":{"name":"fooName"},"name":"testName"}
  1. value with XmlMapper:
{"Foo":{"name":"fooName"},"name":"testName"}

Shouldn't these two be same? Now my deserializer works only for json, not for xml.

ToXmlGenerator startWrappedValue / finishWrappedValue bypass pretty printer

startWrappedValue and finishWrappedValue both bypass pretty printer, so that wrapped values are badly written :

<previousField>
   ...
</previousField><wrapper>
   ...
   <wrapped>
      ...
   </wrapped></wrapper>
<nextField>

Pretty representation should be :

<previousField>
   ...
</previousField>
<wrapper>
   ...
   <wrapped>
      ...
   </wrapped>
</wrapper>
<nextField>

`ACCEPT_EMPTY_STRING_AS_NULL_OBJECT` not honored in xml module for attributes

I'm having some issues with the treatment of empty strings in the XML module.

Say I have this:

class A {
 @JacksonXmlProperty( isAttribute = true )
 private String stringA;
 private String stringB;
}

A a = new A();
a.setStringA("");
a.setStringB("");

In JSON this would give me:

{ "stringA": "", "stringB": ""}

In XML this gives me:

<A stringA="">
 <stringB />
</A>

Which means that the attribute is treated correctly (as according to
the defaults), but for the element, it shows up as null. And is
therefore deserialized back as a null string.

Custom @XmlJavaTypeAdapter ignored when applied to Map

We need() a XmlAdapter because the XML element name will use the map key (containing invalid characters(*))

@XmlJavaTypeAdapter(URLEncoderMapDataAdapter.class)
Map<String,String> map;
...

map.put("my/key","myvalue")
<my/key>my/value</my/key>)

() it's not a good idea to use schema-free attributes in a DTO, and JAXB solution looks ad-hoc and i'm not sure someone will have some real use case
(
*) one can take care (encode) before setting an URL as key

This simple test shows two different errors for

@XmlJavaTypeAdapter(URLEncoderMapDataAdapter.class)

or

@XmlJavaTypeAdapter(value = URLEncoderMapDataAdapter.class, type = MapData.class)
package com.fasterxml.jackson.dataformat.xml.jaxb;

import static java.util.Collections.singletonMap;

import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Map;

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

import com.fasterxml.jackson.databind.AnnotationIntrospector;
import com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector;
import com.fasterxml.jackson.dataformat.xml.XmlAnnotationIntrospector;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import com.fasterxml.jackson.dataformat.xml.XmlTestBase;

/**
 * Tring to use a custom XmlJavaTypeAdapter in order to modify how Maps are serialized
 */
public class TestXmlJavaTypeAdapter extends XmlTestBase
{
    /*
    /**********************************************************************
    /* Helper types
    /**********************************************************************
     */

    @XmlJavaTypeAdapter(URLEncoderMapDataAdapter.class)
    public static class MapData
    {
        public String key;

        public String value;

        public MapData()
        {

        }

        public MapData(String key, String value)
        {
            super();
            this.key = key;
            this.value = value;
        }
    }

    public static class URLEncoderMapDataAdapter extends XmlAdapter<MapData[], Map<String, String>>
    {
        public URLEncoderMapDataAdapter()
        {

        }

        public MapData[] marshal(Map<String, String> arg0) throws Exception
        {
            MapData[] mapElements = new MapData[arg0.size()];
            int i = 0;
            for (Map.Entry<String, String> entry : arg0.entrySet())
            {
                mapElements[i++] = new MapData(encodeKey(entry.getKey()), entry.getValue());
            }

            return mapElements;
        }

        public Map<String, String> unmarshal(MapData[] arg0) throws Exception
        {
            Map<String, String> r = new HashMap<String, String>();
            for (MapData mapelement : arg0)
            {
                r.put(decodeKey(mapelement.key), mapelement.value);
            }
            return r;
        }

        private final static String ENCODING = "UTF-8";

        private String encodeKey(String key) throws UnsupportedEncodingException
        {
            return URLEncoder.encode(key, ENCODING);
        }

        private String decodeKey(String key) throws UnsupportedEncodingException
        {
            return URLDecoder.decode(key, ENCODING);
        }
    }

    @XmlRootElement(name = "DocWithMapData")
    public static class DocWithMapData
    {
        @XmlJavaTypeAdapter(value = URLEncoderMapDataAdapter.class, type = MapData.class)
        public Map<String, String> mapDatas;
    }

    @XmlRootElement(name = "DocWithMapDataSimpleAnnotation")
    public static class DocWithMapDataSimpleAnnotation
    {
        @XmlJavaTypeAdapter(URLEncoderMapDataAdapter.class)
        public Map<String, String> mapDatas;
    }

    private Map<String, String> simpleMapData = singletonMap("key", "value");

    private Map<String, String> needEncodingMapData = singletonMap("my/key", "my/value");

    /*
     * /********************************************************************** /* Set up
     * /**********************************************************************
     */

    protected XmlMapper _jaxbMapper;

    protected XmlMapper _nonJaxbMapper;

    @Override
    public void setUp() throws Exception
    {
        super.setUp();
        _jaxbMapper = new XmlMapper();
        _nonJaxbMapper = new XmlMapper();
        // Use JAXB-then-Jackson annotation introspector
        AnnotationIntrospector intr =
            XmlAnnotationIntrospector.Pair.instance(new XmlJaxbAnnotationIntrospector(),
                new JacksonAnnotationIntrospector());
        _jaxbMapper.setAnnotationIntrospector(intr);
    }

    /*
     * /********************************************************************** /* Unit tests
     * /**********************************************************************
     */

    public void testSimpleKeyMap() throws Exception
    {
        DocWithMapData bean = new DocWithMapData();
        bean.mapDatas = simpleMapData;

        assertEquals("<DocWithMapData><mapDatas><key>value</key></mapDatas></DocWithMapData>",
            _jaxbMapper.writeValueAsString(bean));
    }

    public void testNeedEncodingKeyMap() throws Exception
    {
        DocWithMapData bean = new DocWithMapData();
        bean.mapDatas = needEncodingMapData;

        assertEquals(
            "<DocWithMapData><mapDatas><my%2Fkey>my/value</my%2Fkey></mapDatas></DocWithMapData>",
            _jaxbMapper.writeValueAsString(bean));
    }

    public void testSimpleKeyMapSimpleAnnotation() throws Exception
    {
        DocWithMapDataSimpleAnnotation bean = new DocWithMapDataSimpleAnnotation();
        bean.mapDatas = simpleMapData;

        assertEquals(
            "<DocWithMapDataSimpleAnnotation><mapDatas><key>value</key></mapDatas></DocWithMapDataSimpleAnnotation>",
            _jaxbMapper.writeValueAsString(bean));
    }

    public void testNeedEncodingKeyMapSimpleAnnotation() throws Exception
    {
        DocWithMapDataSimpleAnnotation bean = new DocWithMapDataSimpleAnnotation();
        bean.mapDatas = needEncodingMapData;

        assertEquals(
            "<DocWithMapDataSimpleAnnotation><mapDatas><my%2Fkey>my/value</my%2Fkey></mapDatas></DocWithMapDataSimpleAnnotation>",
            _jaxbMapper.writeValueAsString(bean));
    }

    public void testNeedEncodingKeyMap_nonJaxb() throws Exception
    {
        DocWithMapData bean = new DocWithMapData();
        bean.mapDatas = needEncodingMapData;

        assertEquals(
            "<DocWithMapData><mapDatas><my%2Fkey>my/value</my%2Fkey></mapDatas></DocWithMapData>",
            _nonJaxbMapper.writeValueAsString(bean));
    }

    public void testNeedEncodingKeyMapSimpleAnnotation_nonJaxb() throws Exception
    {
        DocWithMapDataSimpleAnnotation bean = new DocWithMapDataSimpleAnnotation();
        bean.mapDatas = needEncodingMapData;

        assertEquals(
            "<DocWithMapDataSimpleAnnotation><mapDatas><my%2Fkey>my/value</my%2Fkey></mapDatas></DocWithMapDataSimpleAnnotation>",
            _nonJaxbMapper.writeValueAsString(bean));
    }
}

Annotations being ignored

Hi!

I'm experiencing some issues using dataformat-xml module.

I've successfully managed to get jackson-jaxrs-json-provider working, and was trying with jackson-jaxrs-xml-provider too. I've tried two stax-api implementations:

  • woodstox-4.1.1, with the following error when serializing a collection:
    XMLStreamException occurred when processing request: Trying to output second root, . Stacktrace follows:
  • sjsxp-1.0.1: with this, collections are properly serialized.

In both cases, any kind of XML annotations are ignored (i.e.: @JacksonXmlProperty( isAttribute = true ), @JacksonXmlRootElement and @JacksonXmlElementWrapper.

I've tried with jersey implementations of both json and xml providers (jackson 1.8 and jaxb) and annotations works fine for them.

I don't know if this is an issue or a problem with stax-api implementation; could you help me, please?

Thanks in advance

Empty field name when attribute in parent node

new XmlMapper().readValue(
    "<order><person lang=\"en\">John Smith</person></order>",
    Map.class);

produces the following map:

person -> [
    "lang" -> "en"
    "" -> "John Smith"
    ]

Empty key is somehow created. However, if the attribute is removed we will simply get:

person  -> "John Smith"

Make XML text use virtual name of property annotated with @JacksonXmlText

Currently all CDATA segments use bogus name of "" (empty String), and this works when binding to String type. But there are JAXB use cases where text is bind to actual property.
To support this use case, we should probably use name of property to expose CDATA instead, if at all possible.

Alternatively, if not possible, maybe we should just add a feature to allow changing name used; since JAXB defaults to "value", this should be the usual override to use.

Problem with Enums, `@JsonTypeInfo` (and indirectly, `As.WRAPPER_ARRAY`)

(from Jackson user forum by Rich M)

Hi all. I am using jackson 1.7.1 and xml-databind 0.5.3, and ran into a minor issue. I have an attribute of type Object, which is unavoidable because its value can be a primitive or an Enum. I have tagged the attribute with @JsonTypeInfo. Everything works fine in JSON, however, the xml is erroneous when working with an Enum:

public class MyClass {  
    @JsonTypeInfo(use=JsonTypeInfo.Id.MINIMAL_CLASS, include=JsonTypeInfo.As.PROPERTY, property="__type")
    public Object value;
}

The JSON fragment for an Enum is:

"value" : [ "PersonBloodTypeCode", "ABMNUS" ]

The XML fragment does not nest properly. It looks like:

<value>PersonBloodTypeCode</value>
 <value>ABMNUS</value>

Which, when deserialized, results in a String instance "ABMNUS", rather than an enum.

How to gracefully handle non-Stax2 problem with indentation (writeRaw)

Since non-Stax2 implementations like SJSXP2 can not implement writeRaw properly, and since indentation requires that, we have a problem where indentation will crash on SJSXP.

Instead of this, one of two things shoiuld happen:

  1. Indentation is simply omitted, when it can't be made to work, or
  2. Exception is thrown once this problem is detected; and exception specifically explaining the issue.

Support JAX-RS provider

It'd be great to be able to just plug in Jackson XML databind as the default JAX-RS XML provider.
It is possible that standard Jackson provider could work; however, we need to verify this is the case.
Also, if not (or if re-config or sub-classing is needed), we could still add separate provider to use with XML.

(thanks to james strachan for suggesting this!)

Bad empty element with attribute representation with DefaultXmlPrettyPrinter

In version 2.0.4, empty element with zero, one or more attributes are written :

<element>
</element>

or

<element attribute="xxx">
</element>

It may be better to write :

<element />
<element attribute="xxx" />

With some investigation, I notice that empty / non empty switch is based on nrOfEntries in writeEndObject and that non empty element is always choosen.

I don't understand the real nrOfEntries purpose, but it should not be used for empty / non empty switch.

I implement my own PrettyPrinter, subclass of DefaultXmlPrettyPrinter and I switch between empty / non empty based on a boolean stack and it works.
I modify the boolean when pretty printer methods are called :

  • writeStartObject, writeStartArray : add a new FALSE boolean on the stack (FALSE because empty)
  • writeEndObject : remove an boolean element of the stack
  • writeLeaf* : replace last element of the stack with TRUE (TRUE because non empty)

Here is a link of my PrettyPrinter version : http://pastebin.com/UaTujcce

Unrecognized field "" when parsing attribute value

I got an exception from a basic xml structure.

Exception in thread "main"
com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException:
Unrecognized field "" (class com.test.stack.Slot), not marked as
ignorable (4 known properties: , "id", "name", "width", "height"])

at [Source: resources\buildFile; line: 41, column: 192](through reference chain: .....)

at com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException.from(UnrecognizedPropertyException.java:79)

at com.fasterxml.jackson.databind.DeserializationContext.reportUnknownProperty(DeserializationContext.java:568)

Here the interesting part

....
<com.test.stack name="stack1">
<com.test.stack.slot height="0" id="0" name="slot0" width="0">+/null/this is a long string</com.test.stack.slot>
</com.test.stack>

public class Stack {

public String name;

@JsonProperty("com.test.stack.slot")
public Slot slot;

}

//@JsonIgnoreProperties(ignoreUnknown = true) //Need to have that I'll get an exception
public class PieceSlot {
public String name;
public String id;
public String height;
public String width;

}

At this point, I didn't figure out yet how to retreive the value for "Slot" object.

Unable to deserialize String from list items

I'm trying to deserialize this xml with jackson-dataformat-xml 2.0...

<com.test.players>
  <entry>Player 1</entry>
  <entry>Player 2</entry>
</com.test.players>

into this classes:

public class Main {
  @JsonProperty("com.test.Players")
  public Players players;
}

public class Players {
  public List<string> entry;
}

I look into the source code and it look like Jackson xml-bind doesn't like when the List are String.

I have to put this feature ON, if I want the code to run.

ObjectMapper xmlMapper = new XmlMapper();
xmlMapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);

if I don't do that.. it will give the exception below

Exception in thread "main"
com.fasterxml.jackson.databind.JsonMappingException: Can not deserialize
instance of java.util.ArrayList out of VALUE_STRING token

If I use the feature at ON.. the code will works.. BUT the List entry, will contains onyl the last value of the list.

Anyone have a idea to fix that ?

Maybe I could put the entry's values into a object Entry with a String constructor ? (no idea how to do do, if it's possible)

Add equivalent of JAXB @XmlRootElement to specify name of root XML element

(note: re-creation of [http://jira.codehaus.org/browse/JACKSON-452] by Chris Winters)

JAXB allows you to override the name of the root element (using @XmlRootElement when generating XML. For example, rendering this class to XML:

@JsonIgnoreProperties( {"meal", "notMeal", "mealNameDisplay"} )
public class MealEvent...

Generates XML like this:

lunch ...

But I'd like the root element to be 'meal' instead of 'MealEvent'.

FWIW, I tried the wild guess using @JsonTypeName( "meal" ) as well – it had no effect.

In this discussion Tatu suggested the annotation @JacksonXmlRootName.

Unable to find constructor for element key/value style.

I have a xml (trimed for this question)

<?xml version="1.0" encoding="UTF-8"?>
 <main>
  <com.test.options>
   <option name="icon1">/image1.png</option>
   <option name="icon2">/image2.png</option>
  </com.test.options>
</main>

I used this code :

ObjectMapper xmlMapper = new XmlMapper();
xmlMapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);

...

public class Main {

  @JsonProperty("com.test.Options")
  public Options options;
}

public class Options {
  public List<option> option;
}

public class Option {
  public String name;
  public String value;
}

So far, I never been able to find a solution to get rid of this exception

Exception in thread "main"
com.fasterxml.jackson.databind.JsonMappingException: No suitable constructor found for type [simple type, class com.test.Option]: can not instantiate from JSON object (need to add/enable type information?)
...
com.fasterxml.jackson.databind.JsonMappingException.from(JsonMappingException.java:163)

I try to replace List by this :

public List option = new ArrayList();

but now, I get that :

Exception in thread "main"
com.fasterxml.jackson.databind.JsonMappingException: (was
java.lang.NullPointerException) (through reference chain: .....) at
com.fasterxml.jackson.databind.JsonMappingException.wrapWithPath(JsonMappingException.java:218)
at com.fasterxml.jackson.databind.JsonMappingException.wrapWithPath(JsonMappingException.java:183)
at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.wrapAndThrow(BeanDeserializerBase.java:938)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:292)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:112)
at com.fasterxml.jackson.databind.deser.SettableBeanProperty.deserialize(SettableBeanProperty.java:338)
at com.fasterxml.jackson.databind.deser.impl.FieldProperty.deserializeAndSet(FieldProperty.java:87)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:290)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:112)
at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:2563)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:1759)</option>

Problem with DEFAULT_VIEW_INCLUSION and lists of objects

I have a pair of classes: Foo and Bar. Foo contains an array of Bar objects. When I turn off DEFAULT_VIEW_INCLUSION, my list of Bar objects isn't rendered at all when I make a XmlMapper.writeString() call, although it is rendered properly as JSON with a simple ObjectMapper.writeString(). Code below.

public class Views
{
    public static class RestrictedView { };
    public static class UnrestrictedView extends RestrictedView { };
}

public class Foo
{
    @JsonView( Views.RestrictedView.class )
    @JsonProperty
    private String restrictedFooProperty;
    @JsonView( Views.UnrestrictedView.class )
    @JsonProperty
    private String unrestrictedFooProperty;
    @JsonView( Views.RestrictedView.class )
    @JsonProperty
    private Bar[] bars;
    ...
}

public class Bar
{
    @JsonView( Views.RestrictedView.class )
    @JsonProperty
    private int restrictedBarProperty;
    @JsonView( Views.UnrestrictedView.class )
    @JsonProperty
    private int unrestrictedBarProperty;
    ...
}

And my test:

    Foo foo = new Foo();
    foo.setRestrictedFooProperty( "test" );

    Bar bar1 = new Bar();
    bar1.setRestrictedBarProperty( 10 );
    bar1.setUnrestrictedBarProperty( 1 );

    Bar bar2 = new Bar();
    bar2.setRestrictedBarProperty( 11 );
    bar2.setUnrestrictedBarProperty( 2 );

    Bar[] bars = new Bar[] { bar1, bar2 };
    foo.setBars( bars );

    ObjectMapper jsonMapper = new ObjectMapper();
    ObjectMapper xmlMapper = new XmlMapper();

    jsonMapper.configure( SerializationConfig.Feature.AUTO_DETECT_FIELDS, false );
    jsonMapper.configure( SerializationConfig.Feature.AUTO_DETECT_GETTERS, false );
    jsonMapper.configure( SerializationConfig.Feature.AUTO_DETECT_IS_GETTERS,  false );
    jsonMapper.configure( SerializationConfig.Feature.DEFAULT_VIEW_INCLUSION, false );

    xmlMapper.configure( SerializationConfig.Feature.AUTO_DETECT_FIELDS, false );
    xmlMapper.configure( SerializationConfig.Feature.AUTO_DETECT_GETTERS, false );
    xmlMapper.configure( SerializationConfig.Feature.AUTO_DETECT_IS_GETTERS, false );
    xmlMapper.configure( SerializationConfig.Feature.DEFAULT_VIEW_INCLUSION, false );

    String json = jsonMapper.viewWriter( Views.RestrictedView.class ).writeValueAsString( foo );
    String xml = xmlMapper.viewWriter( Views.RestrictedView.class ).writeValueAsString( foo );

    System.out.println( "JSON: \n" + json + "\n" );
    System.out.println(" XML: \n" + xml + "\n" );

And output:

 [java] JSON: 
 [java] {"restrictedFooProperty":"test","bars":[{"restrictedBarProperty":10}, {"restrictedBarProperty":11}]}
 [java] 
 [java]  XML: 
 [java] <Foo><restrictedFooProperty>test</restrictedFooProperty></Foo>

How do I get linefeeds from my XML to deserialize into my strings in POJO

I have an XML with line feeds in it and when I use the XMLMapper, the line feeds are gone.
What do I have to do to get the line feeds to stay?

i.e.

my xml
...
<comment>Automatic move to
In Progress due to
a rejected drawing</comment>
...

My Java Code.

        XmlMapper mapper = new XmlMapper();
                    MyObj obj = mapper.readValue(xmlCode, MyObj.class)

// I want obj.comment to have the line feeds in it

newbie questions about customizing XmlMapper

Greetings. I'm excited about using jackson to serialize xml as well as json. I've got an existing object model that currently uses jackson/json. I have a requirement to produce/consume xml from services that can't handle json.

A few newbie questions around tweaking the output.
I've looked at com.fasterxml.jackson.databind.SerializationConfig etc. but have been unable to unlock the tricks

testFooToXML() Produces

<Foo><i>1</i><f>1.1</f><d>1330459205157</d><s>s</s><n/></Foo>

How to suppress nulls ?
How to render date as iso 8601?

testArrayFooToXML() Produces

<Foo[]><i>1</i><f>1.1</f><d>1330459775907</d><s>s</s><n/></Foo[]>

Foo[] is illegal xml name?

public class TestSerialization {

@Test
public void testFooToXML() throws Exception  {
    ObjectMapper xmlMapper = new XmlMapper();
    Foo f = new Foo();
    String xml = xmlMapper.writeValueAsString(f);
    System.err.println(xml) ;
}
@Test
public void testArrayFooToXML() throws Exception  {
    ObjectMapper xmlMapper = new XmlMapper();
    Foo[] foos = new Foo[]{ new Foo() };
    String xml = xmlMapper.writeValueAsString(foos);
    System.err.println(xml) ;
} 

public class Foo {
    int i = 1 ;
    float f = 1.1f ;
    Date d = new Date() ;
    String s = "s" ;
    String n = null ;
    public Foo() {
        super();
    }
    public int getI() {
        return i;
    }
    public void setI(int i) {
        this.i = i;
    }
    public float getF() {
        return f;
    }
    public void setF(float f) {
        this.f = f;
    }
    public Date getD() {
        return d;
    }
    public void setD(Date d) {
        this.d = d;
    }
    public String getS() {
        return s;
    }
    public void setS(String s) {
        this.s = s;
    }
    public String getN() {
        return n;
    }
    public void setN(String n) {
        this.n = n;
    }

}

}

Add @JacksonXmlText annotation

XML allows one kind of structure that JSON does not, namely things like:

<element attr="xyz" attr2="abc">text</element>

which could ideally match a POJO like:

public class Element {
  public String attr, attr;
  public String text;

}

The problem here is that the only way to indicate that a value should be serialized to / deserialized from a String is @JsonValue.
But its use requires that annotated property is the only property to use. This makes sense for JSON, but with XML, limit can be loosened.

Since we can not change global semantics (as this would not work too well with JSON), we need to introduce a new annotation; such as @JacksonXmlText. It would indicate that:

  • Value of annotated property should be serialized as simple String in XML (and deserialized from String)
  • POJO can have other properties, but they all MUST be serialized as attributes

Limitation on other properties is required because allowing elements would lead to mixed content model, which is something we can not handle. This is also limitation that JAXB has.
It is worth noting that this annotation would be very similar to JAXB @XmlValue, and we should try to then support that annotation as well (as an alias)

For 2.1, should support 'firstAsId' for Object Id handling

With Jackson 2.1, it will be possible to support "first-as-id" handling for Object Ids, with 'firstAsId' property of @JsonIdentityInfo.
But JAXB equivalent does not have such option, so we need to decide what should constitute defaults for this behavior.

Since JAXB seems to default to that behavior (as opposed to Jackson that does not), we could consider two ways to tackle this:

  1. Make JAXB annotation introspector indicate that 'firstAsId' is always set, when we find JAXB annotations
  2. Add a feature in XmlMapper, to override setting.

I am not 100% sure which way to go, at this point... but one or both should be done for Jackson 2.1

Namespaces alias should be removed when there is no need for it

SSIA

public class TestNamespaces
{
   @JacksonXmlRootElement( localName = "person", namespace =
"http://example.org/person" )
   static class Person
   {
       private String name;

       @JacksonXmlProperty( namespace = "http://example.org/person" )
       public String getName()
       {
           return name;
       }

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

   public static void main( String... args ) throws IOException
   {
       Person person = new Person();
       person.setName( "hello" );

       XmlMapper xmlMapper = new XmlMapper();
       xmlMapper.writeValue( System.err, person );
   }
}

That simple code gives me:

<wstxns1:person xmlns:wstxns1="http://example.org/person">
  <wstxns1:name>hello</wstxns1:name>
</wstxns1:person>

@JacksonXmlElementWrapper Conflicting getter/setter definitions for property

Hi, I'm using latest release 2.0.1 and I've got a problem with serializing/deserializing xml (everything works fine in JSON).
I'd like to have such a xml: namename

public class WrapperTest {

public static void main(String[] args) throws JsonGenerationException,
        JsonMappingException, IOException {
    ObjectMapper mapper = new XmlMapper();
    Bean bean = new Bean();
    BeanInfo beanInfo = new BeanInfo();
    beanInfo.setName("name");
    BeanInfo beanOther = new BeanInfo();
    beanOther.setName("name");
    bean.setBeanInfo(new BeanInfo[] { beanInfo });
    bean.setBeanOther(new BeanInfo[] { beanOther });
    String output = mapper.writeValueAsString(bean);
    System.out.println(output);
}

@JacksonXmlRootElement(localName = "output")
private static class Bean {
    private BeanInfo[] beanInfo;
    private BeanInfo[] beanOther;

    @JacksonXmlElementWrapper(localName = "beanInfo")
    @JacksonXmlProperty(localName = "item")
    public BeanInfo[] getBeanInfo() {
        return beanInfo;
    }

    @JacksonXmlElementWrapper(localName = "beanInfo")
    public void setBeanInfo(BeanInfo[] beanInfo) {
        this.beanInfo = beanInfo;
    }

    @JacksonXmlElementWrapper(localName = "beanOther")
    @JacksonXmlProperty(localName = "item")
    public BeanInfo[] getBeanOther() {
        return beanOther;
    }
    @JacksonXmlElementWrapper(localName = "beanOther")
    public void setBeanOther(BeanInfo[] beanOther) {
        this.beanOther = beanOther;
    }
}

private static class BeanInfo {
    private String name;

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

}

This example give getter definition conflict.

If I change

@JacksonXmlElementWrapper(localName = "beanOther")
        @JacksonXmlProperty(localName = "item2")
        public BeanInfo[] getBeanOther() {
}

then all work fine and I've got

<output><beanInfo><item><name>name</name></item></beanInfo><beanOther><item2><name>name</name></item2></beanOther></output>

but if I try to deserialize it then I've got setter definition conflict...

XML Unwrapping no longer working for wrapped elements

Hi,

Using Jackson 2.1.1 when unwrapping a list from XML I no longer get each list item as an object but instead it returns the text attribute of each item. Here's a test case to show what I mean:

    static class Name {
        @JacksonXmlProperty(isAttribute=true)
        public String language

        @JacksonXmlText
        public String text

        public String data

        public Name(String data) {
            this.data = data;
        }
    }

    @XmlRootElement
    static class RoomName {
        @JacksonXmlElementWrapper(localName = "names", useWrapping=true)
        public List<Name> names

    }

    @Test
    void jacksonUnwrappingTest() {
        String xmlData = "<roomName>" +
                    "<names>" +
                        "<name language=\"en\">SPECIAL</name>" +
                    "</names>" +
                "</roomName>";

        RoomName roomName= null;

        XmlMapper xmlMapper = new XmlMapper();
        xmlMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        roomName = xmlMapper.readValue(xmlData, RoomName.class);

        assert(roomName.names.get(0).data.equals("SPECIAL"));
    }

The assert is true when it shouldn't be. In jackson 2.0.6 that I was using previously this worked fine and returned:

<name language=\"en\">SPECIAL</name>

Which correctly mapped to the language and text (I didn't have the Name constructor before, that is only there for this test case).

Add Support for joda date time serialiazation/deserialization in xml

jackson-datatype-joda provides excellent support for converting joda's types in json ser/deser. However, JodaModule only supports json. Adding JodaModule to XmlMapper produces the following output:

    <effectiveDate>2013</effectiveDate>
    <effectiveDate>1</effectiveDate>
    <effectiveDate>1</effectiveDate>

instead of something like: <effectiveDate year="2013" month="1" day ="1' />

What's the best way to implement this as a joda ser/deser module for xml?

XmlMapper seems to ignore custom serializer

I tried to add custom serializer to my XmlMapper for XML serialization:

JacksonXmlModule module = new JacksonXmlModule();
module.addSerializer(Item.class, new ItemSerializer());
XmlMapper xml = new XmlMapper(module);

However, it seems like XmlMapper ignore my serializer and serialize code on its own.

I tried this same serializer with ObjectMapper for JSON serialization:

SimpleModule module = new SimpleModule("MyModule", new Version(1,0,0,null,null,null);
module.addSerializer(Item.class, new ItemSerializer());
ObjectMapper json = new ObjectMapper();
json.registerModule(module);

It works fine for JSON.

Can't parse more than one XML attribute

XmlMapper parses only the first XML attribute, ignoring subsequent ones if any. In the following XML code snippet only "max" attribute is deserialized:

<data max="7" offset="9" total="11"/>

I found and fixed the bug already:

nurkiewicz@9b51833

Serialization of List incorrect if property declared as non-Collection (Object)

(as reported by Paweł Kowol on Jackson users list)


If a List (or other Collection) is value of a non-Collection property, like so:

public class Bean {
  public Object list;
}

it will not be serialized correctly. This is most likely due to special handling of Collection types, which is triggered based on declared type of property (since it must be added statically when constructing bean serializers). Will need to see if this can be improved.

JsonFilter with XmlMapper ignores xml attributes

Hello, I have the case where I want to use a JsonFilter to exclude a property from being serialized to xml. Everything is ok when all the properties of the bean are to be serialized as xml child elements.
However, when there exists a bean property that needs to be serialised as attribute, then the "isAttribute" annotation gets ignored.

Please find a test case below.

Thank you in advance for your help.

import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;

import java.io.IOException;

import org.junit.Test;

import com.fasterxml.jackson.annotation.JsonFilter;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import com.fasterxml.jackson.core.JsonGenerationException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ser.FilterProvider;
import com.fasterxml.jackson.databind.ser.impl.SimpleBeanPropertyFilter;
import com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;

public class JsonFilterWithXmlAttributeTest {

    @JsonFilter("test-filter")
    @JsonPropertyOrder({ "id", "name"})
    abstract class TestFilterMixin{}

    @JsonPropertyOrder({ "id", "name", "jsonProp" })
    abstract class TestNoFilterMixin{}

    @JsonIgnoreProperties(ignoreUnknown=true)
    static class TestCls{

        private String id;
        private String name;
        private String jsonProp;

        public TestCls(){}

        public TestCls( String id, String name, String jsonProp ){
            this.id= id;
            this.name = name;
            this.jsonProp = jsonProp;
        }

        @JacksonXmlProperty(isAttribute=true)
        public String getId() {return id;}
        public void setId(String id) {this.id = id;}

        @JsonProperty("name")
        public String getName() {return name;}
        public void setName(String name) {this.name = name;}

        @JsonProperty("jsonProp")
        public String getJsonProp() {return jsonProp;}
        public void setJsonProp(String jsonProp) {this.jsonProp = jsonProp;}    

    }

    @Test
    public void testFilter() throws JsonGenerationException, JsonMappingException, IOException{
        XmlMapper mapper = new XmlMapper();

        mapper.addMixInAnnotations( TestCls.class, TestFilterMixin.class);

        FilterProvider filters = new SimpleFilterProvider().addFilter("test-filter",
                SimpleBeanPropertyFilter.serializeAllExcept("jsonProp"));
        mapper.setFilters(filters);

        TestCls test = new TestCls( "123", "tester", "jsonText");

        String xml = mapper.writeValueAsString(test);

        assertThat("xml", xml, is("<TestCls id=\"123\"><name>tester</name></TestCls>") );

    }

    @Test
    public void testNoFilter() throws JsonGenerationException, JsonMappingException, IOException{
        XmlMapper mapper = new XmlMapper();

        mapper.addMixInAnnotations( TestCls.class, TestNoFilterMixin.class);

        TestCls test = new TestCls( "123", "tester", "jsonText");

        String xml = mapper.writeValueAsString(test);

        assertThat("xml", xml, is("<TestCls id=\"123\"><name>tester</name><jsonProp>jsonText</jsonProp></TestCls>") );

    }
}

Strange behavior with namespace generation when using isAttribute = true

Given this class:

package test2;

import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;

@JacksonXmlRootElement( localName = "namespaceTest", namespace = Namespace.NAMESPACE )
public class NamespaceTest
{
    private boolean booleanA;

    protected String uid;

    public NamespaceTest()
    {

    }

    @JsonProperty
    @JacksonXmlProperty( isAttribute = true )
    public String getUid()
    {
        return uid;
    }

    public void setUid( String uid )
    {
        this.uid = uid;
    }

    @JsonProperty
    @JacksonXmlProperty( namespace = Namespace.NAMESPACE )
    public boolean isBooleanA()
    {
        return booleanA;
    }

    public void setBooleanA( boolean booleanA )
    {
        this.booleanA = booleanA;
    }
}

And this test program:

import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import com.fasterxml.jackson.dataformat.xml.ser.ToXmlGenerator;
import test2.NamespaceTest;

import java.io.IOException;
import java.util.UUID;

public class Main2
{
    public static void main( String... args ) throws IOException
    {
        XmlMapper xmlMapper = new XmlMapper();
        xmlMapper.configure( ToXmlGenerator.Feature.WRITE_XML_DECLARATION, true );
        xmlMapper.configure( MapperFeature.AUTO_DETECT_CREATORS, false );
        xmlMapper.configure( MapperFeature.AUTO_DETECT_FIELDS, false );
        xmlMapper.configure( MapperFeature.AUTO_DETECT_GETTERS, false );
        xmlMapper.configure( MapperFeature.AUTO_DETECT_IS_GETTERS, false );
        xmlMapper.configure( MapperFeature.AUTO_DETECT_SETTERS, false );

        NamespaceTest namespaceTest = new NamespaceTest();
        namespaceTest.setUid( UUID.randomUUID().toString() );
        namespaceTest.setBooleanA( true );

        xmlMapper.writeValue( System.out, namespaceTest );
    }
}

The output from running is:

<?xml version='1.0' encoding='UTF-8'?>
<wstxns1:namespaceTest xmlns:wstxns1="http://www.example.org" wstxns1:uid="8e7d9686-95a3-4c46-aeed-c9c1738a4f3e">
  <booleanA>true</booleanA>
</wstxns1:namespaceTest>

Even though I did not add any namespace to the uid field.. notice also that booleanA does not have a namespace associated with it.

If I change this:

    @JsonProperty
    @JacksonXmlProperty( isAttribute = true )
    public String getUid()
    {
        return uid;
    }

To

    @JsonProperty
    @JacksonXmlProperty
    public String getUid()
    {
        return uid;
    }

Then it is no longer an attribute (of course) but it also removes the namespace.. which is fine, but it should also not have this namespace when using isAttribute = true.

Regards,
Morten

First attribute of list ignored

The unit test below will reproduce the problem. The general problem is that when the XML is deserialized jackson skips the attributes in the first entry of a list. All subsequent entries have their attributes read, and using an element instead of an attribute will work, but the first item in the list has it's attributes ignored.

I tried stepping through the code a bit, and found that the JsonParser tokens seem to go straight from the "wrapper" field to the "a" field without ever stopping at the "id" field. I could be wrong about this though.


import java.io.StringReader;
import java.util.List;

import junit.framework.Assert;

import org.junit.Test;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;

public class TestParseMarketStat
{
    @Test
    public void testBrokenParser() throws Exception
    {
        String xml = "<response><wrapper><item id=\"1\"><a>x</a><b>y</b></item><item id=\"2\"><a>y</a><b>x</b></item></wrapper></response>";
        XmlMapper mapper = new XmlMapper();
        Response res = mapper.readValue(new StringReader(xml), Response.class);

        System.out.println(new ObjectMapper().writeValueAsString(res));
        Assert.assertNotNull(res.getItems().get(0).getId());
    }

    public static class Response
    {
        @JacksonXmlProperty(localName = "wrapper")
        List<Item> items;

        public List<Item> getItems()
        {
            return items;
        }

        public void setItems(List<Item> items)
        {
            this.items = items;
        }
    }

    public static class Item
    {
        protected String id;
        protected String a;
        protected String b;

        public String getId()
        {
            return id;
        }

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

        public String getA()
        {
            return a;
        }

        public void setA(String a)
        {
            this.a = a;
        }

        public String getB()
        {
            return b;
        }

        public void setB(String b)
        {
            this.b = b;
        }

    }
}

@JacksonXmlText Unrecognized field "" when parsing attribute value

here the test case : https://gist.github.com/2351109

I downloaded the trunk and run my unit test. Here the result

Exception in thread "main" com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "" (class com.test.stack.Slot), not marked as ignorable (5 known properties: , "value", "width", "height", "name", "id"])
at [Source: resources\emptyField.xml; line: 4, column: 115](through reference chain: com.test.stack.Main["com.test.stack"]->com.test.stack.Stack["com.test.stack.slot"]->com.test.stack.Slot[""])
at com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException.from(UnrecognizedPropertyException.java:79)
at com.fasterxml.jackson.databind.DeserializationContext.reportUnknownProperty(DeserializationContext.java:568)
at com.fasterxml.jackson.databind.deser.std.StdDeserializer.handleUnknownProperty(StdDeserializer.java:649)
at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.handleUnknownProperty(BeanDeserializerBase.java:830)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:310)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:112)
at com.fasterxml.jackson.databind.deser.SettableBeanProperty.deserialize(SettableBeanProperty.java:338)
at com.fasterxml.jackson.databind.deser.impl.FieldProperty.deserializeAndSet(FieldProperty.java:87)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:290)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:112)
at com.fasterxml.jackson.databind.deser.SettableBeanProperty.deserialize(SettableBeanProperty.java:338)
at com.fasterxml.jackson.databind.deser.impl.FieldProperty.deserializeAndSet(FieldProperty.java:87)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:290)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:112)
at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:2563)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:1759)
at com.test.stack.TestImporter.main(TestImporter.java:21)

Support for providing namespace to use for fields of a Class (unless overridden by field)

Currently in the xml-module you have to qualify each field with the wanted namespace. This in many ways are similar to JAXB, but JAXB provides @XmlSchema for adding namespaces at the package level.

It would be nice if one could provide a default namespace for a class, so that theres not need to qualify every single field.

Even better would be to provide this at the package level, but class level should be more than ok for now.

Provide or expose pretty-printing option(s)

If this exists, forgive me. I tried a json method and it threw some exception (which I don't have handy right now), and didn't see any docs about it. If/when this exists, please ensure that tabs/spaces(and how many) are choosable, somehow. XStream which I've fallen back to uses 2 spaces, and I'd much prefer tabs.

Respect field order during serialisation.

In order to match existing files from a human perspective, it'd be helpful if the order of elements matched the field order in the classes involved. Either that or provide some other way to choose the ordering, though that seems simplest.

Deserializing when using custom wrapper element

I'm having some issues deserializing a class that uses a @JacksonXmlElementWrapper.

This how my class looks:

@JacksonXmlRootElement( localName = "person", namespace ="http://example.org/person" )
public class Person
{
   @JacksonXmlProperty( isAttribute = true )
   private String id;

   private String name;

   private Integer age;

   @JacksonXmlElementWrapper(localName = "notes")
   @JacksonXmlProperty( localName = "note" )
   private List<String> notes = new ArrayList<String>();

  // getters, setters, etc
}

And this gets serialized fine with:

Person p = new Person( "Name", 30 );
p.getNotes().add( "This is note #1" );
p.getNotes().add( "This is note #2" );

ObjectMapper xmlMapper = new XmlMapper();
String xml = xmlMapper.writeValueAsString( p );

This gives me something like:

<zdef-1759520061:person xmlns=""
xmlns:zdef-1759520061="http://example.org/person"
id="af47ac8e-ee8c-4e43-a87c-dbff93f28d87">
 <name>Name</name>
 <age>30</age>
 <notes>
   <note>This is note #1</note>
   <note>This is note #2</note>
 </notes>
</zdef-1759520061:person>

But, when I now try and deserialize this using:

Person p = xmlMapper.readValue( xml, Person.class );

I get:

Exception in thread "main"
com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException:
Unrecognized field "notes" (Class Person), not marked as ignorable
 at [Source: java.io.StringReader@5c1fe88d; line: 1, column: 169]
(through reference chain: Person["notes"])
       at com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException.from(UnrecognizedPropertyException.java:51)
       at com.fasterxml.jackson.databind.DeserializationContext.reportUnknownProperty(DeserializationContext.java:526)
       at com.fasterxml.jackson.databind.deser.std.StdDeserializer.handleUnknownProperty(StdDeserializer.java:651)
       at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.handleUnknownProperty(BeanDeserializerBase.java:771)
       at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:291)
       at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:99)
       at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:2532)
       at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:1774)

Binary data does not get reset with deserialising from XML

Related jackson-dataformat-xml version 2.0.4

While reading more than one data into byte[] we see that we always get the value of first data deserialised.
This happens only with xml deserialisation and not with json deserialisation.
It seems as if the _binaryValue does not get reset, as for example it is done in com.fasterxml.jackson.core.json.ReaderBasedJsonParser.nextToken()

See test case below, where xmlReadTwoData fails while jsonReadTwoData succeeds.

import static org.hamcrest.CoreMatchers.is; import static org.junit.Assert.assertThat; import java.io.IOException; import org.apache.commons.codec.binary.Base64; import org.junit.Test; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.core.JsonParseException; import com.fasterxml.jackson.databind.JsonMappingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.dataformat.xml.XmlMapper;

public class XmlToMultipleByteTest{

public static class Data {

    @JsonProperty
    byte[] bytes;
}

public static class TwoData {
    @JsonProperty
    Data data1;

    @JsonProperty
    Data data2;
}

@Test
public void xmlReadTwoData() throws JsonParseException, JsonMappingException, IOException {
    String xml = 
        "<TwoData>" +
                "<data1><bytes>" + Base64.encodeBase64String("Hello".getBytes()) + "</bytes></data1>" +
                "<data2><bytes>" + Base64.encodeBase64String("World".getBytes()) + "</bytes></data2>" +
        "</TwoData>";

    TwoData two = new XmlMapper().readValue(xml, TwoData.class);
    assertThat( new String(two.data1.bytes), is("Hello"));
    assertThat( new String(two.data2.bytes), is("World"));
}

@Test
public void jsonReadTwoData() throws JsonParseException, JsonMappingException, IOException {
    String xml = 
        "{" +
            "\"data1\":{\"bytes\":\"" + Base64.encodeBase64String("Hello".getBytes()) + "\"}," +
            "\"data2\":{\"bytes\":\"" + Base64.encodeBase64String("World".getBytes()) + "\"}" +
        "}";

    TwoData two = new ObjectMapper().readValue(xml, TwoData.class);
    assertThat( new String(two.data1.bytes), is("Hello"));
    assertThat( new String(two.data2.bytes), is("World"));
}

}

Incorrectly parsing XML with duplicated tag names

Trying to parse the following XML document:

<data>
  <r><a>A</a></r>
  <r><b>B</b><c>C</c></r>
</data>

with:

new XmlMapper().readValue(xml, Map.class)

ignores the first "r" (r -> {a -> A}) node, overriding it with a second one (r -> {b -> B, c -> C}). It should generate a map with a single key and array value instead: r -> [{a -> A}, {b -> B, c -> C}]. The problem is here (last line of org.codehaus.jackson.map.deser.MapDeserializer#_readAndBind):

            /* !!! 23-Dec-2008, tatu: should there be an option to verify
             *   that there are no duplicate field names? (and/or what
             *   to do, keep-first or keep-last)
             */
            result.put(key, value);

Although this can be worked around by using special map implementation instead of Map.class, but if the duplicated tags appear deeper in XML document (not at top level), there is no easy workaround, see org.codehaus.jackson.map.deser.UntypedObjectDeserializer#mapObject class (LinkedHashMap creation).

Of course the root cause of this problem is the assumption that there are no duplicate properties in JSON. In XML such nodes should be treated as arrays.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.