Saturday, April 18, 2015

Parse json into multiple java pojos using Jackson annotations

Jackson is a suite of data-processing tools for Java (and JVM platform), including the flagship streaming JSON parser / generator library, matching data-binding library (POJOs to and from JSON). In real world programming we will encounter complex cases of JSON to POJO conversion. Here, is one such case where we want to parse a single JSON into multiple java POJOs.

Problem Statement

We receive a json input which is as below format.
{
  "myOrder": {
    "submitDate": "2015-04-16T02:52:01.406-04:00",
    "supplier": "Amazon",
    "orderName": "Wifi Router",
    "submittedBy": "Gaurav Varma",
    "price": {
      "value": "2000",
      "currency": "USD"
    }
  }
}

I have a pre-existing structure of POJOs as below.

Submitter.java
 - name
 - date
Order.java
 - supplier
 - name
Price.java
 - value
 - currency


These are thee individual POJOs. They have all the fields to contain attributes in json request, but the name is not same as in json, neither do they follow the same heirarchy as JSON. We'll use Jackson annotations to handle the same.

Pre-requisites

Jackson 2.x JAR Dependency

To serialize or unserialize JSON using Jackson 2.x requires following three jar files in classpath
  • Core – jackson-core-2.5.0.jar
  • Annotations – jackson-annotations-2.5.0.jar
  • Databind – jackson-databind-2.5.0.jar

Jackson 2.x JAR Download URL

Download Jackson 2.x Libraries

Jackson 2.x JAR Maven Dependency

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-core</artifactId>
    <version>2.5.0</version>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-annotations</artifactId>
    <version>2.5.0</version>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.5.0</version>
</dependency>

Implementation 

There are few key points to handle this approach :
  1. We use Jackson annotation - @JsonIgnoreProperties. This would make sure only those fields in Pojo are mapped to JSON attributes. So we read the json twice, once mapping to Order.java and then to Submitter.java. Both gets the correspondingly mapped fields.
  2. We use Jackson annotation - @JsonProperty. This lets us map the exact JSON attribute to a field in POJO. The annotation makes sure different named attributes in JSON and POJO are mapped.
  3. Jackson doesn't provide any annotation to perform @JsonWrapped (The vice-versa @JsonUnwrapped is available for serialization). Hence, we map Price as an attribute in Order.java.
The source code is as below (All classes in same package) :

JacksonDeserializer.java

import com.fasterxml.jackson.databind.ObjectMapper;

public class JacksonDeserializer {

    public static void main(String[] args) {
        try {
            // ObjectMapper provides functionality for data binding between
            ObjectMapper mapper = new ObjectMapper();

            String jsonString = "{\"submitDate\":\"2015-04-16\",\"submittedBy\":\"Gaurav Varma\",\"supplier\":\"Amazon\",\"orderName\":\"This is my order\"," 
                    + "\"price\": {\"value\": \"2000\",\"currency\": \"USD\"}"
                    + "}";
            System.out.println("JSON String: " + jsonString);
            
            // Deserialize JSON to java format and write to specific POJOs
            Submitter submitterObj = mapper.readValue(jsonString, Submitter.class);
            Order orderObj = mapper.readValue(jsonString, Order.class);
            Price priceObj = orderObj.getPrice();

            System.out.println("submitterObj: " + submitterObj);
            System.out.println("orderObj: " + orderObj);
            System.out.println("priceObj: " + priceObj);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

Order.java

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;

@JsonIgnoreProperties(ignoreUnknown = true)
public class Order {
    @JsonProperty(value = "supplier")
    String supplier;
    @JsonProperty(value = "orderName")
    String name;
    Price price;

    public String getSupplier() {
        return supplier;
    }

    public void setSupplier(String supplier) {
        this.supplier = supplier;
    }

    public String getName() {
        return name;
    }

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

    public Price getPrice() {
        return price;
    }

    public void setPrice(Price price) {
        this.price = price;
    }

    @Override
    public String toString() {
        return "Order [supplier=" + supplier + ", name=" + name + ", price="
                + price + "]";
    }

}

Submitter.java

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;

@JsonIgnoreProperties(ignoreUnknown = true)
public class Submitter {
    @JsonProperty(value = "submittedBy")
    String name;
    @JsonProperty(value = "submitDate")
    String date;

    public String getName() {
        return name;
    }

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

    public String getDate() {
        return date;
    }

    public void setDate(String date) {
        this.date = date;
    }

    @Override
    public String toString() {
        return "Submitter [name=" + name + ", date=" + date + "]";
    }
}

Price.java

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;

@JsonIgnoreProperties(ignoreUnknown = true)
public class Price {
    @JsonProperty(value = "value")
    String value;
    @JsonProperty(value = "currency")
    String currency;

    public String getValue() {
        return value;
    }

    public void setValue(String value) {
        this.value = value;
    }

    public String getCurrency() {
        return currency;
    }

    public void setCurrency(String currency) {
        this.currency = currency;
    }

    @Override
    public String toString() {
        return "Price [value=" + value + ", currency=" + currency + "]";
    }
}

Execution Output

submitterObj: Submitter [name=Gaurav Varma, date=2015-04-16]
orderObj: Order [supplier=Amazon, name=This is my order, price=Price [value=2000, currency=USD]]
priceObj: Price [value=2000, currency=USD]

Suggestions and comments are most welcome. Happy Coding :-)



1 comment:

  1. Hey, let's say if we have many Submitter but we want to print only Gaurav Varma , how to write classes for this ?

    ReplyDelete