1

If I can get either of the following JSON bodies as responses:

{
   "error" : "some error",
   "code": 123
}

or

[
 {
    "name" : "name",
    "value" : "value"
 },
 {
    "name" : "name",
    "value" : "value"
 }
]

Is there a way to map either of these response to below Java POJO using annotations ?

public class Response {
    String error;
    int code;
    List<NameValuePair> nameValuePairs;
}
oneCoderToRuleThemAll
  • 834
  • 2
  • 12
  • 33

2 Answers2

0

Out of the box, only the error response will deserialise. The issue is they are fundamentally different types - an object vs an array.

You can make it work with a custom module for jackson, as described here

stringy05
  • 6,511
  • 32
  • 38
0

One possible response is a JSON Object and other is a JSON Array. In that case it is not possible to create one POJO class to handle it. Also, these two payloads means to different things: one is SUCCESS and other is ERROR payloads. In that case I would use Facade design pattern to create extra layer and hide this complex logic there. It could look like below:

class ResponseDeserialiserFacade {

    private final ObjectMapper mapper = new ObjectMapper();

    public List<NameValuePair> deserialisePairs(String json) {
        try {
            return mapper.readValue(json, new TypeReference<List<NameValuePair>>() {
            });
        } catch (IOException e) {
            try {
                Error error = mapper.readValue(json, Error.class);
                throw new RequestApiException(error, e);
            } catch (IOException e1) {
                throw new RequestApiException(Error.from("Can not parse: " + json), e1);
            }
        }
    }
}

As you noticed I introduced new exception:

class RequestApiException extends RuntimeException {
    private final Error error;

    RequestApiException(Error error, Exception base) {
        super(base);
        this.error = error;
    }

    public Error getError() {
        return error;
    }
}

with Error class:

class Error {

    private String error;
    private String code;

    public static Error from(String message) {
        Error e = new Error();
        e.error = message;

        return e;
    }

    // getters, setters, toString
}

Now we can test it for SUCCESS and ERROR payloads:

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;

import java.io.IOException;
import java.util.List;

public class JsonApp {

    public static void main(String[] args) {
        String success = "[\n" +
                "  {\n" +
                "    \"name\" : \"name\",\n" +
                "    \"value\" : \"value\"\n" +
                "  },\n" +
                "  {\n" +
                "    \"name\" : \"name\",\n" +
                "    \"value\" : \"value\"\n" +
                "  }\n" +
                "]\n";

        tryToParse(success);

        String error = "{\n" +
                "   \"error\" : \"some error\",\n" +
                "   \"code\": 123\n" +
                "}";
        tryToParse(error);
    }

    private static void tryToParse(String json) {
        ResponseDeserialiserFacade deserialiser = new ResponseDeserialiserFacade();
        try {
            List<NameValuePair> pairs = deserialiser.deserialisePairs(json);
            System.out.println("SUCCESS: " + pairs);
        } catch (RequestApiException e) {
            System.out.println("ERROR: " + e.getError());
        }
    }
}

class NameValuePair {

    private String name;
    private String value;

    // getters, setters, toString
}

Above code prints:

SUCCESS: [NameValuePair{name='name', value='value'}, NameValuePair{name='name', value='value'}]
ERROR: Error{error='some error', code='123'}

As you can see, we treated error message like as exception.

Michał Ziober
  • 37,175
  • 18
  • 99
  • 146