21

Using JAXB (2.2) and Jackson (1.9.13), I have trouble unmarshalling the following JSON object to my POJOs

{
   "userId": "foo",
   "group_id": "bar"
}

Note that the payload contains both a camelCase and an underscore field.

The POJO generated by xjc for my XML schema is as follows:

public class User {
    @XmlElement(required = true)
    protected String userId;
    @XmlElement(name = "group_id", required = true)
    protected String groupId;

    public String getUserId() { return userId; }       
    public void setUserId(String value) { this.userId = value; }
    public String getGroupId() { return groupId; }
    public void setGroupId(String value) { this.groupId = value; }
}

Jackson fails with the following exception:

org.codehaus.jackson.map.exc.UnrecognizedPropertyException: Unrecognized field "group_id"

What I tried so far and failed

1. Use JAXB binding underscoreBinding="asCharInWord"

Using the following JXB binding in my XML schema

<jxb:globalBindings underscoreBinding="asCharInWord"/>

generates the following POJO:

public class User {
    @XmlElement(required = true)
    protected String userId;
    @XmlElement(name = "group_id", required = true)
    protected String groupId;

    public String getUserId() { return userId; }
    public void setUserId(String value) { this.userId = value; }
    public String getGroup_Id() { return groupId; }
    public void setGroup_Id(String value) { this.groupId = value; }
}

Note that JAXB now generated setters/getters with underscore for group IDs but the group_id field is still converted to CamelCase. Jackson's object mapper seems to ignore the property getters/setter names and still can't map group_id to groupId.

2. Jackson property naming strategy

Using Jackson's PropertyNamingStrategy CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES I can convert group_id to groupId, but now the object mapper fails on the userId JSON property.

3. Add a JSONProperty annotation to groupId

Adding a JSONProperty to the vanilla JAXB generated POJOs actually works

public class User {
        /* ... */
        @XmlElement(name = "group_id", required = true)
        @JSONProperty("group_id")
        protected String groupId;
        /* ... */
   }

However, my XML schema is huge and manual instrumentation of the generated classes is not feasible, since we generate our classes often.

What should I do?

I see the following two remaining options to handle this problem:

  1. Implement a JAXB plugin that adds a JSONProperty annotation to each XMLElement with underscore names (my preferred next approach)
  2. Use a custom name generator for XJC as outlined in this Stackoverflow answer.

Have I missed the obvious? thanks for your thoughts.

Community
  • 1
  • 1
user1627943
  • 337
  • 2
  • 3
  • 11

2 Answers2

8

Have you tried annotate the groupId attribute in your user class with @JsonProperty("group_id"). This should work (tested & proven)!

If no such annotation is present on an attribute, Jackson mapper will just consider the attribute name as is, else the provided value will used for JSON deserialization. Here is how should look your User class:

public class User {

private String userId;

@JsonProperty("group_id")
private String groupId;

This should map to your JSON

{"userId": "foo","group_id": "bar"}

If you're using CXF (or any other Rest framework) based on Jackson, I suggest reading the following short blog post that details how to configure the JacksonJsonProvider in CXF to achieve serialization and de-serialization from/to Java from/to JSON Snake Case (underscore) formatted data

https://mahichir.wordpress.com/2015/07/08/cxf-configuration-to-produce-json-snake-case-underscore-case-formatted-data-using-jackson-json-library/

  • This qualifies as link-only" answer - perhaps you would make an edit to include most adequate code snippet/piece of text from linked resource? – bardzusny Jul 08 '15 at 12:09
0

I found a possible solution to your issue, from a similar case, on stackoverflow. How to extract ObjectMapper from JAX-RS Client?

Once you have access to Object Mapper you can set your own annotation provider. One that has been removed as a separate module now, since jackson 2.0 is jaxb module.

Here the answer, though I modified it so it complies with Jackson module Jaxb 2.4.4 and Jackson API 2.3.2:

public final static JacksonJsonProvider JACKSON_JSON_PROVIDER = new JacksonJaxbJsonProvider().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false).configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
public final static ObjectMapper OBJECT_MAPPER = JACKSON_JSON_PROVIDER.locateMapper(Object.class, MediaType.APPLICATION_JSON_TYPE);
public final static Client CLIENT = ClientBuilder.newClient().register(JACKSON_JSON_PROVIDER);

This requires the jar: jackson-module-jaxb-annotations-2.x.x.jar so jackson can load it behind the scene through the interface JacksonJaxbProvider.

For your version of Jackson, I think jaxb code is still in it. So you can refer to similar way using http://wiki.fasterxml.com/JacksonJAXBAnnotations

Community
  • 1
  • 1