Проблемы с сериализацией в JSON на Java с использованием Jackson

Доброе утро, ребята!

У меня есть строки JSON, которые выглядят так:

{
   "StatusCode":0,
   "Message":null,
   "ExecutionTime":0,
   "ResponseData":[
        {"Name":"name1","SiteId":"1234","Type":"Type1","X":"1234567","Y":"123456"},
        {"Name":"Name2","SiteId":"2134","Type":"Type2","X":"1234567","Y":"1234567"},
        {"Name":"Name3","SiteId":"3241","Type":"Type3","X":"1234567","Y":"1234567"},
        {"Name":"Name4","SiteId":"4123","Type":"Type4","X":"123456","Y":"123456"}
    ]
}

Я хочу создать объект, в котором я могу получить значения X и Y.

Я безуспешно пытался использовать Джексона для сериализации строки JSON. Я создал два дополнительных класса для использования Джексоном. Один класс для верхнего уровня, StatusCode, Message, ExecutionTime и ResponseData, который выглядит как

public class PL {
private Long statusCode;
private String executionTime;
private String message;
private ResponseData responseData;
public PL(){
}

public void setStatusCode(Long statusCode){
  this.statusCode = statusCode;
}

public Long getStatusCode(){
  return this.statusCode;
}

public void setExecutionTime(String executionTime){
  this.executionTime = executionTime;
}
public String getExecutionTime(){
  return this.executionTime;
}

public void setMessage(String message){
  this.message = message;
}
public String getMessage(){
  return this.message;
}

public void setResponseData(ResponseData responseData){
  this.responseData = responseData;
}
public ResponseData getResponseData(){
  return this.responseData;
}
}

Где ReponseData возвращается как объект, а затем у меня есть другой класс для сериализации ResponseData, который выглядит как

public class ResponseData {

private String name;
private String siteId;
private String type;
private String x;
private String y;

public ResponseData(){
}

public void setName(String name){
  this.name = name;
}
public String getName(){
  return this.name;
}

public void setSiteId(String siteId){
  this.siteId = siteId;
}
public String getSiteId(){
  return this.siteId;
}

public void setType(String type){
  this.type = type;
}
public String setType(){
  return this.type;
}

public void setX(String x){
  this.x = x;
}
public String getX(){
  return this.x;
}

public void setY(String y){
  this.y = y;
}
public String getY(){
  return this.y;
}
}

Затем я создаю ObjectMapper с

private final static ObjectMapper mapper = new ObjectMapper();

и попробуйте прочитать значения с помощью

ResponseData e = mapper.readValue(result.toString(), ResponseData.class);

и в конечном итоге с исключением

com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "StatusCode" (class MyClass.ResponseData), not marked as ignorable (5 known properties: "x", "y", "siteId", "name", "type"])

как будто он не может проанализировать первую запись, StatusMessage. Даже если я удалю второй класс и попытаюсь проанализировать только первые четыре записи, где я верну ResponseData как String, я все равно получу то же исключение.

Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
3
0
341
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Это довольно просто. Определите структуру своего ответа, используя композицию классов. К сожалению, использовать поля с заглавными буквами в JSON, которые по умолчанию требуют, чтобы имена полей в Java DTO начинались с заглавной буквы. Тем не менее, их можно легко сопоставить с обычными именами в нижнем регистре либо с помощью модификатора ACCEPT_CASE_INSENSITIVE_PROPERTIES на ObjectMapper, либо путем аннотирования полей соответствующими именами. Я предпочитаю свойство на ObjectMapper, поскольку оно сохраняет DTO независимо от кода сериализации, и этот метод используется в тесте ниже (тест зеленый):

import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.databind.ObjectMapper;

public class TestDeserialization50386188 {

    public static class Response {

        public static class ResponseDataType {
            public String name;
            public String siteId;
            public String type;
            public long x;
            public long y;
        }

        public int statusCode;
        public String message;
        public long executionTime;
        public List<ResponseDataType> ResponseData = new ArrayList<>();
    }

    private static final String data = "{\"StatusCode\":0,\"Message\":null,\"ExecutionTime\":0,\"ResponseData\":[{\"Name\":\"name1\",\"SiteId\":\"1234\",\"Type\":\"Type1\",\"X\":\"1234567\",\"Y\":\"123456\"},{\"Name\":\"Name2\",\"SiteId\":\"2134\",\"Type\":\"Type2\",\"X\":\"1234567\",\"Y\":\"1234567\"},{\"Name\":\"Name3\",\"SiteId\":\"3241\",\"Type\":\"Type3\",\"X\":\"1234567\",\"Y\":\"1234567\"},{\"Name\":\"Name4\",\"SiteId\":\"4123\",\"Type\":\"Type4\",\"X\":\"123456\",\"Y\":\"123456\"}]}";

    @Test
    public void deserialize_response_withJackson_ok() throws IOException {
        ObjectMapper mapper = new ObjectMapper()
          .configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true);

        Response response = mapper.readValue(data, Response.class);

        assertEquals(4, response.ResponseData.size());
        assertEquals(1234567, response.ResponseData.get(2).x);
        assertEquals(1234567, response.ResponseData.get(2).y);
    }
}

Заливаешь проект с исполняемым тестом на это специальное репозиторий GitHub.

Книга "Чистый код" от дяди Боба на самом деле не рекомендует чрезмерное использование геттеров и сеттеров, столь распространенных в Java для DTO, каковыми является класс Response. Тем не менее, вы можете заменить все общедоступные поля парами геттер / сеттер, если хотите, но ясность пострадает без очевидного улучшения качества.

Используйте список для получения массивов.

private Long statusCode;
private String executionTime;
private String message;
public List<ResponseDataType> ResponseData

и все будет делать автоматически.

Ответ принят как подходящий

Для начала, в PL у вас должен быть List<ResponseData>, а не простой атрибут ResponseData. Как видите, в JSON ResponseData представляет собой массив "ResponseData":[...], поэтому он будет десериализован как List. Каждый элемент списка будет объектом ResponseData, как вы его определили.

Тогда у вас есть проблема с регистром, у вас есть верхние регистры в JSON, которых нет в атрибутах вашего класса. Вы можете использовать аннотацию @JsonProperty (См. API) для решения проблемы следующим образом:

class PL {
    @JsonProperty("StatusCode")
    private Long statusCode;
    @JsonProperty("ExecutionTime")
    private String executionTime;
    @JsonProperty("Message")
    private String message;
    @JsonProperty("ResponseData")
    private List<ResponseData> responseDatas;

    public PL(){
    }

    // getters/Setters  

}


class ResponseData {

    @JsonProperty("Name")
    private String name;
    @JsonProperty("SiteId")
    private String siteId;
    @JsonProperty("Type")
    private String type;
    @JsonProperty("X")
    private String x;
    @JsonProperty("Y")
    private String y;

    public ResponseData(){
    }

    // getters/Setters  

}

Затем прочтите свой JSON как объект PL, например:

ObjectMapper mapper = new ObjectMapper();
PL pl = mapper.readValue(json, PL.class);
for(ResponseData rd : pl.getResponseDatas()) {
    System.out.println(rd.getX());
    System.out.println(rd.getY());
}

Это выводит:

1234567
123456
1234567
1234567
1234567
1234567
123456
123456

Еще одна вещь: есть ли простой способ просто получить доступ к первым значениям X и Y? Я не совсем понимаю этот цикл ..

t3ng1l 17.05.2018 11:01

если вам нужен только первый, вы можете сделать pl.getResponseDatas().get(0).getX() и pl.getResponseDatas().get(0).getY()

Bentaye 17.05.2018 11:26

Другие вопросы по теме