2

Details ---

One of my POJO SomeResponseObject for an api response has attribute

@JsonProperty("s_summary")
private Map<String, SummaryObject> summary 

which further has few more attributes. These are summed in json as follows :

{  
 "s_summary": {
    "rewardSubscription": {
      "accountId": "XYZ",
      "startDate": "2015-12-29T19:00:00+05:30",
      "endDate": "2017-06-21T00:00:00+05:30",
      "isActive": true,
      "entityId": "ID123",
      "status": "ACTIVE"
    }
  }
}

This POJO(json) is further modified by our service to return a RESPONSE as :

{  
  "rewardSubscription": {
    "accountId": "XYZ",
    "startDate": "2015-12-29T19:00:00+05:30",
    "endDate": "2017-06-21T00:00:00+05:30",
    "isActive": true,
    "entityId": "ID123",
    "status": "ACTIVE"
  }
}

Narrowing Down ---

Now when we are writing tests against this API call. We end up being unable to map the response to any specific POJOs(java response class). Test code -

JSONObject responseObject = new JSONObject(responseFromService.getResponseBody())
      .getJSONObject("RESPONSE");
ObjectMapper objectMapper = new ObjectMapper();
SomeResponseObject summaryResponse = objectMapper.getObjectMapper()
      .readValue(responseObject.toString(), SomeResponseObject.class); // And this wouldn't work.

Question --

Is there any way we can cast the current API response or wrap it somehow to be mapped to the actual POJO(SomeResponseObject.class)?

Thanks in advance.

Naman
  • 27,789
  • 26
  • 218
  • 353
  • as per my understading your question is parsing the json data to a dto object ? – xyz Aug 16 '16 at 12:17
  • there are many alternatives if you are parsing the client response in json format to an pojo class one of them is roo..It creates and aspect and using its api will transform it to : dto.toJson(); //internal code of aspect : return new JSONSerializer().exclude("*.class").serialize(this); – xyz Aug 16 '16 at 12:20
  • also there are many 3rd party libraries available like : jackson org.codehaus.jackson jackson-mapper-asl> – xyz Aug 16 '16 at 12:24
  • http://howtodoinjava.com/jackson/jackson-examples-convert-java-object-to-from-json/ is one of the tutorial...I like spring roo personally doing the stuff for me for an entity/pojo just by adding the specific annotations '@RooJson' – xyz Aug 16 '16 at 12:26
  • @xyz - not exactly what I am looking for. We have been using a mapper. The problem is to map a trimmed response to a POJO defined OR wrap the response to match the POJO. – Naman Aug 17 '16 at 03:14

1 Answers1

3

Problem

You receive an object with a rewardSubscription field, or, in your case, a map, with a rewardSubscription key. You can't convert a map to an object of SomeResponseObject type directly.

Solution

Option 1

Convert json to a map manually and set it to the SomeResponseObject instance:

JSONObject responseObject = new JSONObject(responseFromService.getResponseBody())
      .getJSONObject("RESPONSE");

ObjectMapper objectMapper = new ObjectMapper();
Map<String, SummaryObject> summaryMap = objectMapper.readValue(responseObject.toString(), new TypeReference<Map<String, SummaryObject>>() {});
SomeResponseObject response = new SomeResponseObject();
response.setSummaryMap(summaryMap);

Option 2

So as not to manually convert map each time, write a custom deserializer that will handle both cases. The deserialize method should be similar to this:

@Override
public SomeResponseObject deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException {
    ObjectMapper objectMapper = new ObjectMapper();
    JsonNode rootNode = jp.readValueAsTree();

    JsonNode sSummaryNode = rootNode.get("s_summary");
    if (sSummaryNode != null) {
        // Usual case.
        return objectMapper.treeToValue(sSummaryNode, SomeResponseObject.class);
    } else {
        // Special case - when received a map.
        Map<String, SummaryObject> summaryMap = objectMapper.readValue(rootNode.toString(), new TypeReference<Map<String, SummaryObject>>() {});
        SomeResponseObject response = new SomeResponseObject();
        response.setSummaryMap(summaryMap);
        return response;
    }
}

And then in the code you don't care:

ObjectMapper objectMapper = new ObjectMapper();
SomeResponseObject response = objectMapper.readValue(json, SomeResponseObject.class);
Artem Novikov
  • 4,087
  • 1
  • 27
  • 33