Reputation: 9757
I have a return result that can look like this:
{"rows":[{"ProfileID":"SLappE","CR_ProfileID":"BC1A"},{"ProfileID":"SCRA","CR_ProfileID":"BC15"},{"ProfileID":"SMOKE","CR_ProfileID":"BC15"},{"ProfileID":"Smokey","CR_ProfileID":"BC1F"},{"ProfileID":"WF-LN-OCR","CR_ProfileID":"BC1F"}]}
Or like this:
{"rows":[{"PARAM":"product_reference_nr","FIELDNAME":"account_nr","EXAMPLE-FIELD":"CHK","EXAMPLE-TABLE":"CHK_DOC","ID":"CHK-ACCT"},{"PARAM":"party_pd_nr","FIELDNAME":"front_image_object_id","EXAMPLE-FIELD":"CHK","EXAMPLE-TABLE":"BACK_CHK_DOC","ID":"CHK-BACK"},{"PARAM":"endDate","FIELDNAME":"document_dt","EXAMPLE-FIELD":"HR","EXAMPLE-TABLE":"SPLAPSTIC_DOC","ID":"HR-DT"},{"PARAM":"startDate","FIELDNAME":"document_dt","EXAMPLE-FIELD":"HR","EXAMPLE-TABLE":"SPLAPSTIC_DOC","ID":"HR-DT-STARTDT"},{"PARAM":"formCode","FIELDNAME":"form_category_cd","EXAMPLE-FIELD":"HR","EXAMPLE-TABLE":"SPLAPSTIC_DOC","ID":"HR-FORM”}]}
So I am calling a rest service and the return result will always start with rows: which is then followed by an array of what appear to be Dictionary or Map/HaspMap values. The size of that dictionary will vary. But in that List of Rows it appears to be key value pairs.
Now I try to Deserialize that json into this Data Structure:
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import javax.xml.bind.annotation.XmlRootElement;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@JsonInclude(JsonInclude.Include.NON_NULL)
@XmlRootElement
public class EResponse {
@JsonProperty("rows")
private final List<Row> rows = new ArrayList<>();
public List<Row> getRows() {
List<Row> retResult = rows;
return retResult;
}
}
and
import com.fasterxml.jackson.annotation.*;
import javax.xml.bind.annotation.XmlRootElement;
import java.util.Hashtable;
import java.util.Map;
@JsonInclude(JsonInclude.Include.NON_NULL)
@XmlRootElement
public class Row {
private Map<String,Object> column;
@JsonProperty
public Map<String, Object> getColumn(){
return column;
}
}
I call the above code like so:
result = mapper.readValue(response.readEntity(String.class), new TypeReference<EResponse>(){});
I have tried various configurations of the above and I can't seem to get it to return without throwing an Unrecognized field exception. Which would make sense as it the first element in the Key value is being seen as a field which the mapper can't map, cause it isn't defined anywhere in the object I am mapping it to.
What is the appropriate way To map this List> to Objects? Is there a simply way I can Cast the value to the appropriate type as well? I am using jackson-databind but could be persuaded to use GSON if there is an elegant solution to solve this.
Upvotes: 0
Views: 3250
Reputation: 9757
Essentially, I was counting on the Jackson library to be to smart. I needed to loop through the 'Tree structure' of the Json that was returned. Map it to my individual Response objects. A little like this:
ObjectMapper mapper = new ObjectMapper();
JsonNode rootNode = mapper.readTree(response.readEntity(String.class));
JsonNode rowsNode = rootNode.path("rows");
Iterator<JsonNode> elements = rowsNode.elements();
while (elements.hasNext()){
JsonNode columnNodes = elements.next();
System.out.println("All the Column Values: " + columnNodes.asText());
Map<String, Object> values = new HashMap<String, Object>();
values = mapper.treeToValue(columnNodes, HashMap.class);
ERow row = new ERow();
row.setColumn(values);
columns.add(row);
}
result.setRows(columns);
Don't worry, I wrap that in a try catch block.
Upvotes: 0
Reputation: 21124
Well, obviously it does not work since jackson can't map the key/value pairs to the given map. If you want jackson to map this, define the properties in json as class fields in your DTO. But that seems counter intuitive since you have different properties in two different payloads given, their structure is different. So, this is what you should do. Let's represent each object as a map of json key/value pairs against the offset value of the object in the array. So that will address the dynamic nature of your payload too. The code looks like this.
final String jsonArray = new String(Files.readAllBytes(Paths.get("./src/main/resources/payload.json")));
final TypeReference<Map<String, String>> typeRef = new TypeReference<Map<String, String>>() {
};
Iterator<Map<String, String>> it = new ObjectMapper().readerFor(typeRef)
.readValues(JsonPath.parse(jsonArray).read("rows").toString());
final AtomicInteger counter = new AtomicInteger();
Map<Integer, Map<String, String>> mapByObject = new HashMap<>();
it.forEachRemaining(keyValueMap -> {
mapByObject.putIfAbsent(counter.incrementAndGet(), keyValueMap);
});
System.out.println(mapByObject);
The output is like so:
{1={ProfileID=SLappE, CR_ProfileID=BC1A}, 2={ProfileID=SCRA, CR_ProfileID=BC15}, 3={ProfileID=SMOKE, CR_ProfileID=BC15}, 4={ProfileID=Smokey, CR_ProfileID=BC1F}, 5={ProfileID=WF-LN-OCR, CR_ProfileID=BC1F}}
For each object, key/value map is printed against the relative offset of that particular object in the array. You may try using the Gson
if needed, but this is simple enough IMO.
Upvotes: 1