Разбор JSON в POJO, когда тип данных значения для того же ключа изменяется - PullRequest
1 голос
/ 28 марта 2019

у меня ниже 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 object или longзначение.Как я могу разобрать это на POJO?Я использую библиотеку Jackson.

Ответы [ 2 ]

1 голос
/ 29 марта 2019

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 object, JSON array, primitives.

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

Также обратите внимание на модуль JavaTimeModule, который охватывает все классы java.time.* и позволяет сериализовать и десериализовать стандартным образом,

1 голос
/ 28 марта 2019

Один из способов сделать это - использовать пользовательский десриализатор, где вы можете логически проверить поле (например, https://dzone.com/articles/custom-json-deserialization-with-jackson)

. Возможно, длина поля может быть логикой, используемой для ее анализа здесь.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...