2


NOTE: I've rewritten this question ask it was too vauge beforehand, and I am unaware of how to approach this problem.


I'm currently trying to deserialize data from a RESTful API into POJO classes. Each method of the API returns the same response structure, with the actual result having varying structures depending on the method. I want to be able to map each method's result to its own POJO class.

Two of the sample JSON responses are of this structure, one returns an array of objects where the other returns a series of properties.

In this case, each JSON object within 'result' should be deserialized into it's own POJO instance.

{
   "success" : true,
   "message" : "",
   "result" : [{
            "Frequency" : 0.25000
            "Range" : 15.6
        }, {
            "Frequency" : 0.12500
            "Range" : 25.1
        }, {
            "Frequency" : 0.00750
            "Range" : 56.0
        }
    ]
}

In this case, 'result' is the object and should map onto a single POJO instance.

{
   "success" : true,
   "message" : "",
   "result" : {
       "Bid" : 2.05670368,
       "Ask" : 3.35579531,
       "Last" : 3.35579531
    }
}

This will be a different class to previous example as it is a response from a different API method. I am just wanting to extract the result, the success and message aren't required for any response.

4
  • It is confusing on what exactly trying to achieve? Is creating a POJO class an option for you? Elaborate your question! Commented Feb 23, 2017 at 17:40
  • I want to populate the POJO instance with the data contained within the result array. The results can either contain one single object or an array of objects therefore the default object mapper struggles to achieve this. What annotations / methods are required to so? Commented Feb 23, 2017 at 17:47
  • @Coder I've re-written this question as it was too vauge for an accurate response Commented Feb 23, 2017 at 19:32
  • I have updated my response @Flemingjp Commented Feb 23, 2017 at 20:18

2 Answers 2

3

As your results response is dynamic I would suggest you not to include your results object in your deserialization. Include the below code in your POJO class which does the deserailization.

@JsonIgnore
private Map<String, Object> additionalProperties = new HashMap<String, Object>();

@JsonAnyGetter
public Map<String, Object> getAdditionalProperties() {
return this.additionalProperties;
}

@JsonAnySetter
public void setAdditionalProperty(String name, Object value) {
this.additionalProperties.put(name, value);
}

Now the Map with the name additionalProperties will contain the any additional objects that you hadn't tried to capture in the Parent object ( in this case it will have your results object). It stores either Object or list of Objects depending upon your response and you can access it with the key value results.

As suggested by FlemingJp using a generic type seems like a much feasible solution for this question so I am including that part as well in this answer.

public class Response<T> {

    private boolean success;
    private String message;
    private T result;

    public T getResult() {
        return result;
    }

    public String getMessage () {
       return message;
    }

    public boolean getSuccess() {
        return success;
    }
}

Example Usage

ObjectMapper objectMapper = new ObjectMapper();

// When the result is one object
Response<DistanceStat> res = objectMapper.readValue(data, new TypeReference<Response<DistanceStat>>() {});

// For a collection of objects under result
Response<Collection<DistanceStat>> res = objectMapper.readValue(data, new TypeReference<Response<Collection<DistanceStat>>>() {});

Edit:

You can deserialize in jackson as follows:

ObjectMapper mapper = new ObjectMapper();
String jsonInString //which contains your json data in string
ParentNode parentNode = mapper.readValue(jsonInString, ParentNode.class);

PraentNode being your parent class.

Edit 2:

Note: This is very specific to your situation.

These are the POJO classes you will be needing, the parent class to which you have to deserialize

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.fasterxml.jackson.annotation.JsonAnyGetter;
import com.fasterxml.jackson.annotation.JsonAnySetter;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;

@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonPropertyOrder({
"success",
"message"
})
public class Example {

@JsonProperty("success")
private Boolean success;
@JsonProperty("message")
private String message;

@JsonIgnore
private Map<String, Object> additionalProperties = new HashMap<String, Object>();

@JsonProperty("success")
public Boolean getSuccess() {
return success;
}

@JsonProperty("success")
public void setSuccess(Boolean success) {
this.success = success;
}

@JsonProperty("message")
public String getMessage() {
return message;
}

@JsonProperty("message")
public void setMessage(String message) {
this.message = message;
}

@JsonAnyGetter
public Map<String, Object> getAdditionalProperties() {
return this.additionalProperties;
}

@JsonAnySetter
public void setAdditionalProperty(String name, Object value) {
this.additionalProperties.put(name, value);
}

}

I intentionally not included the Result class in here. Now whatever the Result response you get will be stored in the Map named additional properties as an object which can be a list<Result> or class Result

Now you have two structures for Result object. You can store them in same POJO. so let's construct two different POJO classes for each structure.

Result1.java

import java.util.HashMap;
import java.util.Map;
import com.fasterxml.jackson.annotation.JsonAnyGetter;
import com.fasterxml.jackson.annotation.JsonAnySetter;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;

@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonPropertyOrder({
"Frequency",
"Range"
})
public class Result1 {

@JsonProperty("Frequency")
private Double frequency;
@JsonProperty("Range")
private Double range;
@JsonIgnore
private Map<String, Object> additionalProperties = new HashMap<String, Object>();

@JsonProperty("Frequency")
public Double getFrequency() {
return frequency;
}

@JsonProperty("Frequency")
public void setFrequency(Double frequency) {
this.frequency = frequency;
}

@JsonProperty("Range")
public Double getRange() {
return range;
}

@JsonProperty("Range")
public void setRange(Double range) {
this.range = range;
}

@JsonAnyGetter
public Map<String, Object> getAdditionalProperties() {
return this.additionalProperties;
}

@JsonAnySetter
public void setAdditionalProperty(String name, Object value) {
this.additionalProperties.put(name, value);
}

}

Result2.java:

import java.util.HashMap;
import java.util.Map;
import com.fasterxml.jackson.annotation.JsonAnyGetter;
import com.fasterxml.jackson.annotation.JsonAnySetter;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;

@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonPropertyOrder({
"Bid",
"Ask",
"Last"
})
public class Result2 {

@JsonProperty("Bid")
private Double bid;
@JsonProperty("Ask")
private Double ask;
@JsonProperty("Last")
private Double last;
@JsonIgnore
private Map<String, Object> additionalProperties = new HashMap<String, Object>();

@JsonProperty("Bid")
public Double getBid() {
return bid;
}

@JsonProperty("Bid")
public void setBid(Double bid) {
this.bid = bid;
}

@JsonProperty("Ask")
public Double getAsk() {
return ask;
}

@JsonProperty("Ask")
public void setAsk(Double ask) {
this.ask = ask;
}

@JsonProperty("Last")
public Double getLast() {
return last;
}

@JsonProperty("Last")
public void setLast(Double last) {
this.last = last;
}

@JsonAnyGetter
public Map<String, Object> getAdditionalProperties() {
return this.additionalProperties;
}

@JsonAnySetter
public void setAdditionalProperty(String name, Object value) {
this.additionalProperties.put(name, value);
}

}

Now you deserialize the JSON using the following piece of code:

ObjectMapper mapper = new ObjectMapper();
    String jsonInString //which contains your json data in string
    Example example= mapper.readValue(jsonInString, Example.class);

Here comes the tricky part, now your map contains the Results response but we are not sure if it is a list of objects or object.

Now to get the Results we will first try to identify if it's a collection or not ( as list is a collection) which will help us identify.

Object resultObject = example.getAdditionalProperties().getKey("results");

if ( resultObject instanceof Collection<?>){

List<Result1> results=resultObject;
}

else{

Result2 results=resultObject;

}

Hope this solves your issue!!

Let me know if you need clarification with anything!!

Sign up to request clarification or add additional context in comments.

6 Comments

The result property varys from different api methods not from request to request of the same method. I have tried to clarify this in my post. I will have different POJOs for each method but all have the same response structure that I want to deal with
so your just looking on how to deserialize using jackson?
I am trying to yes.
Yes.From a Http GET request
@Flemingjp solved the issue? are you looking for pojo classes as well?
|
1

I managed to solve my problem. I created a ParentNode as suggested by Coder with a generic type. This allows any type and even collections to be within result.


Response Class

public class Response<T> {

    private boolean success;
    private String message;
    private T result;

    public T getResult() {
        return result;
    }

    public String getMessage () {
       return message;
    }

    public boolean getSuccess() {
        return success;
    }
}


Example Usage

ObjectMapper objectMapper = new ObjectMapper();

// When the result is one object
Response<DistanceStat> res = objectMapper.readValue(data, new TypeReference<Response<DistanceStat>>() {});

// For a collection of objects under result
Response<Collection<DistanceStat>> res = objectMapper.readValue(data, new TypeReference<Response<Collection<DistanceStat>>>() {});

This is an close to an elegant solution I'm looking for now, would now be useful to avoid the triple type defintion in the second usage example.

Note: I'm accepting Coder's answer as it lead to this solution.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.