Nick
Nick

Reputation: 23

Java Jackson Deserialize Plain Json Array or Json Object into a Single Java Object (POJO)

I am retrieving data from an API endpoint that returns data in two different expected json formats. One of the responses is a success response and one of the responses is a failure response. The other endpoints in the API are similar and also have a success and failure format. For most of the endpoints I just deserialize both types of responses into a single Java object where some fields are just set to default/null values when not present. My problem with this endpoint is that the success response is a json array and the fail response is a json object. I have tried everything to have a simple way to deserialize both of these possible responses into a single java object.

Example success json.

[
    {
        "item1": "somevalue1",
        "item2": "somevalue2"
    },
    {
        "item1": "somevalue3",
        "item2": "somevalue4"
    },
    ..
    ..
]

Example fail json

{
    "success": false,
    "errorMessage": "something went wrong "
}

My Java class I am currently using works to deserialize the success json but not for the fail json.

public class ResponseObject {
    public final boolean success;
    public final String errorMessage;
    public final List<MyItem> items;

    @JsonCreator
    public ResponseObject(ArrayList<MyItem> items) {
       this.items = items;
    }

}

And I also have a constructor that works for the fail case but not for the success case

public class ResponseObject {
    public final boolean success;
    public final String errorMessage;
    public final List<MyItem> items;

    @JsonCreator
    public ResponseObject(@JsonProperty("success") Boolean success, @JsonProperty("errorMessage") String errorMessage){
        this.success = success == null;
        this.errorMessage = errorMessage;
        this.items = items;
    }

}

My deserialization code looks something like

ObjectReader objectReader = objectMapper.readerFor(ResponseObject.class);
Object json = objectReader.readValue(inputStream);

Whatever strategy I have tried to be able to deserialize both cases generically seems to fail. If I try to include the json array in the constructor with a @JsonProperty then I have no name/value to refer to the array of items, and the code throws an exception.

What I need is a way to deserialize both responses into the ResponseObject format that I outlined, when I get the success reponse the success field should be true, the errorMessage should be null and the items should contain a list of MyItem. When I get a fail response, the success should be false, the errorMessage should have a string message and the items list should be null.

How can I achieve this? Or, how else can I structure my code to handle multiple expected json formats? I know I could deserialize to a TreeMap, check the format and then convert again to the final object for example, but I would like to skip this intermediate step.

Thanks for any advice that anyone can give me on this one :)

Upvotes: 2

Views: 622

Answers (2)

pcoates
pcoates

Reputation: 2307

If you add a POJO for the error message, you could add a second JsonCreator to your Response Object.

public class ErrorResponse {
    private boolean success;
    private String errorMessage;
    public boolean isSuccess() {
        return success;
    }
    public void setSuccess(boolean success) {
        this.success = success;
    }
    public String getErrorMessage() {
        return errorMessage;
    }
    public void setErrorMessage(String errorMessage) {
        this.errorMessage = errorMessage;
    }
}

Modified ResponseObject with 2 JsonCreator

public class ResponseObject {
    public final boolean success;
    public final String errorMessage;
    public final List<MyItem> items;

    @JsonCreator
    public ResponseObject(List<MyItem> items) {
       this.success = true;
       this.errorMessage = null;
       this.items = items;
    }

    @JsonCreator
    public ResponseObject(ErrorResponse error){
        this.success = error.isSuccess();
        this.errorMessage = error.getErrorMessage();
        this.items = null;
    }

}

Upvotes: 0

SBylemans
SBylemans

Reputation: 1764

You can try to make a superclass

public class ResponseObject {

    public final boolean success;
    public final String errorMessage;
    public final List<MyItem> items;

    ...
}

and then organize the fail and success messages in a

public class SuccesResponseObject extends ResponseObject {...}

and

public class FailResponseObject extends ResponseObject {...}

with the corresponding constructors. The properties will be situated in the superclass. When using the classes, you should refer to them of being of the class ResponseObject.

Upvotes: 2

Related Questions