Injecting json property based on condition using Jackson

别来无恙 提交于 2019-12-11 04:16:03

问题


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

Client Code

    package com.test.deserializer;

    import com.fasterxml.jackson.databind.DeserializationFeature;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import com..schema.model.Person;

    public class TestClient {

        public static void main(String[] args) {
            String response1="{\"id\":1234,\"name\":\"Pradeep\"}";
            TestClient client = new TestClient();
            try {
                Person response = client.readJSONResponse(response1, Person.class);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        public <T extends Object> T readJSONResponse(String response, Class<T> type) {
            ObjectMapper mapper = new ObjectMapper();
            mapper.enable(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT);
            T result = null;
            try {
                result = mapper.readValue(response, type);
            } catch (Exception e) {
                e.printStackTrace();
            }
            return (T) result;
        }

    }

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


回答1:


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 {
   public MapModelDeserializer(BeanDeserializerBase src) {
    super(src);
   }

  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.




回答2:


This is a late answer, but I solve it in a different way. It can work by catching it in Object like this:

    @JsonProperty("details")
    public void setDetails(Object details) {
        if (details instanceof List) {
            setDetails((List) details);
        } else if (details instanceof Map) {
            setDetails((Map) details);
        }
    }

    public void setDetails(List details) {
        // your list handler here
    }

    public void setDetails(Map details) {
       // your map handler here
    }


来源:https://stackoverflow.com/questions/38684201/injecting-json-property-based-on-condition-using-jackson

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!