Определите соответствующий класс на основе существования свойства при чтении JSON - PullRequest
1 голос
/ 29 января 2020

Предполагая, что у меня есть два типа ответов от службы - положительные и отрицательные. Они имеют формат JSON. Когда я использую Jackson API для создания соответствующих java объектов, я должен написать код, подобный следующему:

import com.fasterxml.jackson.databind.ObjectMapper;

private ObjectMapper objectMapper = new ObjectMapper();
String json = ... // JSON received from a service
ServiceResponse response = objectMapper.readValue(json, ServiceResponse.class);

// Analyze the response
if (response instanceof PositiveServiceResponse) {
...
}
...

Проблема заключается в том, что оба типа ответов не имеют ничего общего. Нет свойства типа. В противном случае я мог бы использовать Vehicle-Car-Truck пример из https://www.baeldung.com/jackson-inheritance

Пример положительного ответа:

{
  "city": "c",
  "street": "s" 
}

Отрицательный ответ:

{
  "errorMsg": "error",
  "errorCode": "572"
}

NegativeServiceResponse содержит errorCode свойство. Можно ли «сказать» API-интерфейсу Джексона о создании и экземпляре класса NegativeServiceResponse, если JSON содержит свойство, а экземпляре PositiveServiceResponse в противном случае? Конечно, я могу разобрать JSON, используя низкоуровневые механизмы (JsonNode et c.), Но есть ли более элегантное решение?

Ответы [ 2 ]

2 голосов
/ 29 января 2020

можно сделать. Вам необходимо создать класс Container для положительного и отрицательного ответа. См. Ниже, дайте мне знать, если это работает.

import com.fasterxml.jackson.databind.ObjectMapper;

import java.io.IOException;

class Solution {
    public static void main(String[] args) throws IOException {
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.enableDefaultTyping();

        Positive positive = new Positive(1);
        Negative negative = new Negative(-1);

        Response response = new Response(positive, negative);

        String json = objectMapper.writeValueAsString(response);

        Response output = objectMapper.readValue(json, Response.class);

        System.out.println(output.negative);
        System.out.println(output.positive);

    //practically, when you have only either positive or negative 
    Response response2 = new Response();
    response2.setPositive(positive);

    String json2 = objectMapper.writeValueAsString(response2);
    //or String json2 = "{\"positive\":{\"code\":1}};
    System.out.println(json2);
    Response output2 = objectMapper.readValue(json2, Response.class);

    System.out.println(output2.positive);
    System.out.println(output2.negative);

    }
}

Вывод:

Negative{otherCode=-1}
Positive{code=1}

{"positive":{"code":1},"negative":null}

Positive{code=1}
null

Классы ответов:

class Response {
    Positive positive;
    Negative negative;

    public Response() {
    }

    public Response(Positive positive, Negative negative) {
        this.positive = positive;
        this.negative = negative;
    }

    public boolean isPositive() {
        return positive != null;
    }
    public void setPositive(Positive positive) {
        this.positive = positive;
    }

    public void setNegative(Negative negative) {
        this.negative = negative;
    }

    public Positive getPositive() {
        return positive;
    }

    public Negative getNegative() {
        return negative;
    }
}

 class Positive {
    private int code;

     public Positive() {
     }

     public Positive(int code) {
        this.code = code;
    }

     public int getCode() {
         return code;
     }

     @Override
     public String toString() {
         return "Positive{" +
             "code=" + code +
             '}';
     }
}

 class Negative {
    private int otherCode;

     public Negative() {
     }

     public Negative(int otherCode) {
        this.otherCode = otherCode;
    }

     public int getOtherCode() {
         return otherCode;
     }

     @Override
     public String toString() {
         return "Negative{" +
             "otherCode=" + otherCode +
             '}';
     }
 }
1 голос
/ 29 января 2020

Предполагая, что у вас есть иерархия классов, подобная следующей

class ServiceResponse {}

class PositiveServiceResponse extends ServiceResponse { public String city; public String street; }

class NegativeServiceResponse extends ServiceResponse { public String errorMsg; public String errorCode; }

Затем вы создаете десериализатор:

import com.fasterxml.jackson.databind.deser.std.StdDeserializer;

class ServiceResponseDeserializer extends StdDeserializer<ServiceResponse> {

    protected ServiceResponseDeserializer() {
        super(ServiceResponse.class);
    }

    @Override
    public ServiceResponse deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
        ObjectMapper mapper = (ObjectMapper) p.getCodec();
        ObjectNode root = mapper.readTree(p);
        if (root.get("errorMsg") != null) { // found negative token
            NegativeServiceResponse result = new NegativeServiceResponse();
            result.errorCode = root.get("errorCode").asText();
            result.errorMsg = root.get("errorMsg").asText();
            return result;

            // OR (shorter but less efficient due to object-back-to-string conversion)

            return mapper.readValue(root.toString(), NegativeServiceResponse.class);
        } else {
            return mapper.readValue(root.toString(), PositiveServiceResponse.class);
        }
    }
}

и используете как следующие

@Test
public void testJackson() throws IOException {

    SimpleModule module = new SimpleModule("ServiceResponseModule")
            .addDeserializer(ServiceResponse.class, new ServiceResponseDeserializer());
    ObjectMapper mapper = new ObjectMapper();
    mapper.registerModule(module);

    ServiceResponse positive = mapper.readValue(
            "{ \"city\": \"c\", \"street\": \"s\" }", ServiceResponse.class);
    assertTrue(positive instanceof PositiveServiceResponse);

    ServiceResponse negative = mapper.readValue(
            "{ \"errorMsg\": \"error\", \"errorCode\": \"572\" }", ServiceResponse.class);
    assertTrue(negative instanceof NegativeServiceResponse);
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...