Доброе утро, ребята!
У меня есть строки 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, я все равно получу то же исключение.




Это довольно просто. Определите структуру своего ответа, используя композицию классов. К сожалению, использовать поля с заглавными буквами в 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
если вам нужен только первый, вы можете сделать pl.getResponseDatas().get(0).getX() и pl.getResponseDatas().get(0).getY()
Еще одна вещь: есть ли простой способ просто получить доступ к первым значениям X и Y? Я не совсем понимаю этот цикл ..