【问题标题】:Injecting json property based on condition using Jackson使用杰克逊根据条件注入 json 属性
【发布时间】:2016-07-31 12:56:39
【问题描述】:

我有一个 json 格式,我正在使用 Jackson API 将其转换为 Java 对象模型。我正在使用Jaxsonxml 2.1.5 解析器。 json响应如下图。

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

现在 json 响应格式已更改。如果total-records1,则details 将是一个具有idname 属性的对象。但如果total-records 大于1,则details 将是一个对象数组,如下所示:

    {
      "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"
              }
            ]
          }
        }
      }
    }

我的 Java Mapper 类与之前的 json 响应如下所示。

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

客户代码

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

    }

现在基于total-records 如何处理映射到ModelModel 对象列表。请告诉我。

【问题讨论】:

    标签: java json fasterxml


    【解决方案1】:

    您需要一个自定义反序列化器。这个想法是混合和匹配对象处理与树处理。尽可能解析对象,但使用树 (JSONNode) 进行自定义处理。

    MapModelResponseList 上,删除records 属性并添加List&lt;Data&gt; 数组,其中Data 只是id/name 对的持有者类。您可以通过返回此列表的大小来获取总记录。

    在反序列化器中,执行以下操作:

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

    请注意,您没有实现deserialize() - 默认实现用于正常创建MapModelResponseListhandleUknownProperty() 用于处理content 元素。由于超级调用中的@JsonIgnoreProperties(ignoreUnknown = true),您不关心的其他数据将被忽略。

    【讨论】:

    • 在使用上述方法时,我收到一条错误消息,指出 com.fasterxml.jackson.databind.JsonMappingException: Class com.test.deserializer.CustomDeserializer has no default (no arg) constructor
    • 我试过你的方法。但仍然没有运气。让我知道我的客户代码是否正确。我仍然遇到同样的异常com.fasterxml.jackson.databind.JsonMappingException: Class com.test.deserializer.CustomDeserializer has no default (no arg) constructor
    • 您使用的是哪个版本的 jaxson。我总是遇到同样的错误com.fasterxml.jackson.databind.JsonMappingException: Class com.test.deserializer.CustomDeserializer has no default (no arg) constructor
    • 如果您使用的是@JsonDeserialize,我看不到创建没有参数构造函数的 BeanDeserializerBase 的简单方法。您可能需要在自定义模块中创建反序列化器 - 请参阅 the answer to this
    【解决方案2】:

    这是一个迟到的答案,但我以不同的方式解决它。它可以通过在Object 中捕获它来工作,如下所示:

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

    【讨论】:

      猜你喜欢
      • 2018-04-03
      • 2017-09-03
      • 2016-08-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-02-08
      相关资源
      最近更新 更多