Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Umarshall JSON object with underscore and camelCase fields between Jackson and JAXB

Tags:

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.

like image 904
user1627943 Avatar asked Feb 24 '14 17:02

user1627943


People also ask

How does Jackson read nested JSON?

A JsonNode is Jackson's tree model for JSON and it can read JSON into a JsonNode instance and write a JsonNode out to JSON. To read JSON into a JsonNode with Jackson by creating ObjectMapper instance and call the readValue() method. We can access a field, array or nested object using the get() method of JsonNode class.

What is difference between JsonProperty and JsonAlias?

@JsonProperty can change the visibility of logical property using its access element during serialization and deserialization of JSON. @JsonAlias defines one or more alternative names for a property to be accepted during deserialization.

How do I ignore JsonProperty?

To ignore individual properties, use the [JsonIgnore] attribute. You can specify conditional exclusion by setting the [JsonIgnore] attribute's Condition property. The JsonIgnoreCondition enum provides the following options: Always - The property is always ignored.

What is JsonProperty annotation?

The @JsonProperty annotation is used to map property names with JSON keys during serialization and deserialization. By default, if you try to serialize a POJO, the generated JSON will have keys mapped to the fields of the POJO.


1 Answers

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/

like image 96
Mahieddine M. Ichir Avatar answered Sep 18 '22 14:09

Mahieddine M. Ichir