当相同键的值的数据类型更改时,将JSON解析为POJO

时间:2019-03-28 16:22:09

标签: java json jackson json-deserialization

我在JSON以下

{      
    "date":{
        "year":2017,
        "month":3,
        "day":12
    },
    "name":"Jon",
    "message":{
        "product":"orange",
        "price":2000
    }
}

有时有时会如下JSON所示:

 {      
   "date":2017312,
    "name":"Jon",
    "message":{
        "product":"orange",
        "price":2000
    }
  }

请注意,日期可以是JSON objectlong的值。如何将其解析为POJO?我正在使用Jackson库。

2 个答案:

答案 0 :(得分:1)

执行此操作的一种方法是使用自定义解串器,您可以在其中逻辑检查字段。(例如https://dzone.com/articles/custom-json-deserialization-with-jackson

字段长度可以是此处用来将其解析为元素的逻辑。

答案 1 :(得分:1)

LocalDate

您需要实现自定义反序列化程序,并使用JsonDeserialize注释对给定的字段进行注释。对于以下POJO模型:

class Pojo {

    @JsonDeserialize(using = LocalDateJsonDeserializer.class)
    private LocalDate date;
    private String name;
    private Message message;

    // getters, setters
}

class Message {

    private String product;
    private int price;

    // getters, setters
}

简单的反序列化器实现如下所示:

class LocalDateJsonDeserializer extends JsonDeserializer<LocalDate> {

    @Override
    public LocalDate deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
        if (p.currentToken() == JsonToken.START_OBJECT) {
            MapType mapType = ctxt.getTypeFactory().constructMapType(Map.class, String.class, Integer.class);
            JsonDeserializer<Object> mapDeserializer = ctxt.findRootValueDeserializer(mapType);
            Map<String, Integer> date = (Map<String, Integer>) mapDeserializer.deserialize(p, ctxt);

            return LocalDate.of(date.get("year"), date.get("month"), date.get("day"));
        } else if (p.currentToken() == JsonToken.VALUE_NUMBER_INT) {
            // You need to be really careful here. Date format is ambiguous
            // You should test it for all months and days
            String date = p.getValueAsString();
            int year = Integer.parseInt(date.substring(0, 4));
            // much better to use always two digits for month: `03` instead `3`
            int month = Integer.parseInt(date.substring(4, 5));
            int day = Integer.parseInt(date.substring(5, 7));

            return LocalDate.of(year, month, day);
        } else if (p.currentToken() == JsonToken.VALUE_NULL) {
            return null;
        }

        return null;
    }
}

简单用法:

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import java.io.IOException;
import java.time.LocalDate;
import java.util.HashMap;
import java.util.Map;

public class Test {

    public static void main(String[] args) throws Exception {
        String json1 = "json 1 ...";
        String json2 = "json 2....";

        ObjectMapper mapper = new ObjectMapper();
        System.out.println(mapper.readValue(json1, Pojo.class));
        System.out.println(mapper.readValue(json2, Pojo.class));
    }
}

打印:

Pojo{date=2017-03-12, name='Jon', message=Message{product='orange', price=2000}}
Pojo{date=2017-03-12, name='Jon', message=Message{product='orange', price=2000}}

当然,您需要处理所有极端情况并改进错误处理,但是通常这就是我们应该如何处理可能具有以下不同格式的值的方法:JSON objectJSON arrayprimitives

LocalDateTime

对于LocalDateTime,它的外观应类似。我们需要创建POJO模型:

class Pojo {

    @JsonDeserialize(using = LocalDateTimeJsonDeserializer.class)
    private LocalDateTime time;
    private String name;
    private Message message;

    // getters, setters, toString
}

自定义反序列化器可能如下所示:

class LocalDateTimeJsonDeserializer extends JsonDeserializer<LocalDateTime> {

    @Override
    public LocalDateTime deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
        if (p.currentToken() == JsonToken.START_OBJECT) {
            MapType mapType = ctxt.getTypeFactory().constructMapType(Map.class, String.class, Object.class);
            JsonDeserializer<Object> mapDeserializer = ctxt.findRootValueDeserializer(mapType);
            Map<String, Object> node = (Map<String, Object>) mapDeserializer.deserialize(p, ctxt);
            Map<String, Integer> date = (Map<String, Integer>) node.get("date");
            Map<String, Integer> time = (Map<String, Integer>) node.get("time");

            LocalDate localDate = LocalDate.of(date.get("year"), date.get("month"), date.get("day"));
            LocalTime localTime = LocalTime.of(time.get("hour"), time.get("minute"), time.get("second"), time.get("nano"));

            return LocalDateTime.of(localDate, localTime);
        } else if (p.currentToken() == JsonToken.VALUE_NUMBER_INT) {
            // You need to be really careful here. Date format is ambiguous
            // You should test it for all months and days
            String date = p.getValueAsString();
            // parse date string
            // ...

            return LocalDateTime.now();
        } else if (p.currentToken() == JsonToken.VALUE_NULL) {
            return null;
        }

        return null;
    }
}

反序列化示例与LocalDate相同。对于JSON以下的有效负载:

{
  "time": {
    "date": {
      "year": 2019,
      "month": 3,
      "day": 29
    },
    "time": {
      "hour": 13,
      "minute": 21,
      "second": 20,
      "nano": 620000000
    }
  },
  "name": "Jon",
  "message": {
    "product": "orange",
    "price": 2000
  }
}

它应该打印:

Pojo{time=2019-03-29T13:21:20.620, name='Jon', message=Message{product='orange', price=2000}}

另外,看看涵盖所有java.time.*类的JavaTimeModule模块,并允许以标准方式进行序列化和反序列化,