Nachiket Nachiket - 4 months ago 25
JSON Question

Injecting json property based on condition using Jackson

I have a json format which I am converting into Java Object Model using

Jackson API
. I am using
Jaxsonxml
2.1.5 parser. The json response is as shown below.

{
"response": {
"name": "states",
"total-records": "1",
"content": {
"data": {
"name": "OK",
"details": {
"id": "1234",
"name": "Oklahoma"
}
}
}
}
}


Now json response format has changed. If the
total-records
is
1
the
details
will be an object with
id
and
name
attributes. But if the
total-records
is more than
1
then the
details
will be an array of object like below:

{
"response": {
"name": "states",
"total-records": "4",
"content": {
"data": {
"name": "OK",
"details": [
{
"id": "1234",
"name": "Oklahoma"
},
{
"id": "1235",
"name": "Utah"
},
{
"id": "1236",
"name": "Texas"
},
{
"id": "1237",
"name": "Arizona"
}
]
}
}
}
}


My Java Mapper class looks like below with earlier
json
response.

@JsonIgnoreProperties(ignoreUnknown = true)
public class MapModelResponseList {

@JsonProperty("name")
private String name;

@JsonProperty("total-records")
private String records;

@JsonProperty(content")
private Model model;

public Model getModelResponse() {
return model;
}

public void setModel(Model model) {
this.model = model;
}
}


Now based on the
total-records
how to handle to mapping to either a
Model
or list of
Model
Object. Please let me know.

Answer

You need a custom deserializer. The idea is to mix and match object processing with tree processing. Parse objects where possible but use the tree (JSONNode) for custom handling.

On the MapModelResponseList, remove the records property and add a List<Data> array where Data is just a holder class for the id/name pairs. You can get the total records by returning the size of this list.

In the deserializer, do the following:

public final class MapModelDeserializer extends BeanDeserializer {
  protected void handleUnknownProperty(JsonParser jp, DeserializationContext ctxt, Object beanOrClass, String propName) throws IOException, JsonProcessingException {
    if ("content".equals(propName)) {
      MapModelResponseList response = (MapModelResponseList) beanOrClass;

      // this probably needs null checks!
      JsonNode details = (JsonNode) jp.getCodec().readTree(jp).get("data").get("details");

      // read as array and create a Data object for each element
      if (details.isArray()) {
        List<Data> data = new java.util.ArrayList<Data>(details.size());

        for (int i = 0; i < details.size(); i++) {
           Data d = jp.getCodec().treeToValue(details.get(i), Data.class);
           data.add(d);
        }

        response.setData(data);
      }
      // read a single object
      else {
         Data d = jp.getCodec().treeToValue(details, Data.class);
         response.setData(java.util.Collections.singletonList(d));
      }

    super.handleUnknownProperty(jp, ctxt, beanOrClass, propName);
}   

Note that you do not implement deserialize() - the default implementation is used to create the MapModelResponseList as normal. handleUknownProperty() is used to deal with the content element. Other data you don't care about is ignored due to @JsonIgnoreProperties(ignoreUnknown = true) in the super call.