So, I am accessing a third-party API and it's giving me this JSON object, but I'm having a whale of a time trying to find an elegant way to parse the resources sub-object using Jackson.
I'm assuming I have to write a custom deserializer, though I'm wondering if there's another way...
{
"somekey": "somevalue",
"resources": [
"list",
[
{
"#type": "com.yada.Yada",
"resource": {
"#type": "ServiceObjectReference",
"id": "emp1234",
"displayName": "Bob Smith"
},
"type": "TYPE_PERSON",
"resourceType": 200
},
{
"#type": "com.yada.Yada",
"resource": {
"#type": "ServiceObjectReference",
"id": "emp1235",
"displayName": "Sue Smith"
},
"type": "TYPE_PERSON",
"resourceType": 200
}
]
]
}
Since the value of resources is an array with different object type, probably you don't want to use POJO for the mapping. Not sure about your implementation of custom deserializer, but you might want to use ObjectMapper#readTree method, than read the node value based on its type
ObjectMapper mapper = new ObjectMapper();
JsonNode rootNode = mapper.readTree(your_json_input);
JsnoNode resourcesNode = rootNode.path("resources");
for (JsonNode resourceNode : resourcesNode ) {
if (resourceNode.isObject()) {
// the node is an object, you could do POJO mapping now or keep using path() method to go deeper
} else {
// simply get the String value
String list = resourceNode.getTextValue();
}
}
It looks like the producer of the json uses polymorphic type handling.
We can all agree that this is not something to use in a public api.
However, you could try to annotate your classes so that jackson can make use of this though.
The annotation #JsonTypeInfo would be your starting point
It would look something like this:
#JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, visible = true)
#JsonSubTypes({
#JsonSubTypes.Type(value = MyYada.class, name="com.yada.Yada")
})
public class MyYada
I did some more research and it looks like this is a "2-element JSON array". It can be parsed using JsonTypeInfo.As(WRAPPER_ARRAY)
http://jackson.codehaus.org/1.9.9/javadoc/org/codehaus/jackson/annotate/JsonTypeInfo.As.html#WRAPPER_ARRAY
Related
I need to send following JSON in API BODY POST request:
{
"name": "",
"type": "TEMP",
"shared": false,
"search": {
},
"order": [
]
}
In my MainBody.java, declared
private String name;
private String type;
private boolean shared;
private JSON search;
private Object order;
and defined getters and setters.
In Payload.java,
MainBody mb = new MainBody();
mb.setName("");
mb.setType("TEMP");
mb.setShared(false);
mb.setSearch(null);
mb.setOrder(new ArrayList<>());
ObjectMapper om = new ObjectMapper();
String myData = om.writerWithDefaultPrettyPrinter().writeValueAsString(mb);
System.out.println(myData);
results
{
"name" : "",
"type" : "TEMP",
"shared" : false,
"search" : null,
"order" : [ ]
}
Please assist with how search as { } can be achieved as per expected JSON instead of null.
TIA.
Instead of setting search to null, you need to set it to an empty object. I'm not sure which JSON library you are using, but there should be an object constructor like new JsonObject(). Depending on what the allowed values for search are, you may also want to consider representing it in your class as Map<String, String> or something like that.
I would try something like this:
mb.setSearch(new JSON());
This way you create empty object and there should be only {}. It also depends on which JSON library do you use.
Issue resolved after using JSONMapper.
I have a rather large JSON object (this is a subset) I'm trying to parse:
{
"items": [
{
"Name": "Wallet",
"tags": [
"wallet",
"cardholder"
],
"features": {
"material": {
"location": "in-house"
},
"stitching": {
"location": "in-house"
}
},
"color": null,
"store": {
"address": "123 Main Street"
}
}
],
"jItem": 0
}
I have Java POJO's for all JSON objects except for the features object, which contains objects where the key is a dynamic value. My current only-POJO's code does this:
...
itemsJson = doGet(url);
ObjectMapper objMapper = new ObjectMapper();
Items items = objMapper.readValue(itemsJson, Items.class);
...
This gives me a hierarchy of Java POJO's representing my data. The one hitch is features. How can I parse the features data, with the keys as values, within this larger object? I've looked at other SO posts:
Deserialize JSON in Jackson where key is a value
Deserializing jackson dynamic key value
Jackson JSON key as value in Java
but none of these solutions have 1. an object where the key is the value and 2. an object where the key is the value is contained within an object. I do have parsing just the features working using this "just features JSON":
{
"features": {
"material": {
"location": "in-house"
},
"stitching": {
"location": "in-house"
}
}
}
with this code:
...
JsonNode jsonNodeRecord = objectMapper.readTree(App.class.getResourceAsStream("/data.json"));
List<JsonNode> recordNodes = jsonNodeRecord.findValues("features");
...
which gives me JsonNode's. This isn't ideal because I don't have my features data in a POJO.
The Question:
It's not clear to me how to integrate parsing the JSON using POJO's for everything except for features, with either the JsonNode code above or a custom deserializer as in the #1 SO link above.
I have to parse the following JSON. I have two solutions and I'm trying to decide which is the preferred approach.
{
"created": true,
"entries": [{
"NewValue": "test1",
"Operation": "ADD",
"FieldIdentifier": "name",
"FieldType": "AN",
"FieldLength": 35,
"OriginalValue": ""
},
{
"NewValue": "test2",
"Operation": "ADD",
"FieldIdentifier": "age",
"FieldType": "AN",
"FieldLength": 35,
"OriginalValue": "testOrig"
}]
}
Solution 1: Use readTree to get the JSON into a JsonNode object we can query, then convert into a list
ObjectMapper mapper = new ObjectMapper();
JsonNode jsonObject = mapper.readTree(form.getFieldList());
ArrayNode fieldListArray = (ArrayNode) jsonObject.get("fieldList");
if (fieldList != null) {
FieldDefType[] listOfFieldDefs = mapper.convertValue(fieldList, FieldDefType[].class);
if (listOfFieldDefs != null) {
request.setFieldList(Arrays.asList(listOfFieldDefs));
}
}
Solution 2: Use TypeReference to map from JSON to POJO object and then retrieve my fields from there.
FieldListModel fieldListModel = new FieldListModel();
ObjectMapper mapper = new ObjectMapper();
fieldListModel = mapper.readValue(form.getFieldList(), new FieldListTypeReference());
request.setFieldList(fieldListModel.getEntries());
// At bottom of this class I have my named static inner class - Sonar scan requested this
static class FieldListTypeReference extends TypeReference<FieldListModel> {}
I'm leaning towards solution 1 as there is no need for a named static inner class and anecdotally I have heard on Stack Overflow that TypeReference is slow. The only issues with solution 1 I can think of is the need for more null checks and more lines of code.
I have the following JSON response from shipcloud.io:
[
{
"name": "dhl",
"display_name": "Deutsche Post DHL",
"services": [
"standard",
"returns",
"one_day",
"one_day_early"
],
"package_types": [
"parcel",
"bulk"
]
},
{
"name": "dpag",
"display_name": "Deutsche Post",
"services": [
"standard"
],
"package_types": [
"letter",
"parcel_letter",
"books"
]
},
{
"name": "dpd",
"display_name": "DPD - Dynamic Parcel Distribution",
"services": [
"standard",
"returns",
"one_day",
"one_day_early"
],
"package_types": [
"parcel",
"parcel_letter"
]
}
]
How can I deserialize this JSON array with Jackson? Usually I use a simple POJO and define
the property name of the list / array (#JsonProperty("blub") e.g.). Problem is, there is no property name used here...
I tried it using an empty property name, but it didn't work.
I'm just getting this error message:
Can not deserialize instance of Response.CarriersResponse out of
START_ARRAY token
If you want to use jackson, this is the solution that works for me:
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.UNWRAP_ROOT_VALUE, false);
You are deserializing multiple objects of the type, so you need to do it as a list, like this
// somewhere in an example TypeReferences class
public static final TypeReference<List<Response.CarriersResponse>> CARRIER_RESPONSES = new TypeReference<List<Response.CarriersResponse>>() {
};
// elsewhere where you're calling the mapper
List<Response.CarriersResponse> responses = mapper.readValue(text, TypeReferences.CARRIER_RESPONSES);
You could instantiate it in-place, but that's a design decision between performance vs total memory consumption.
Try to deserialize to Response.CarriersResponse[] class.
Response.CarriersResponse[] responses = mapper.readValue(text, Response.CarriersResponse[].class);
I have a JSON that looks like this
[
{
"itemLabel":"Social Media",
"itemValue":90
},
{
"itemLabel":"Blogs",
"itemValue":30
},
{
"itemLabel":"Text Messaging",
"itemValue":60
},
{
"itemLabel":"Email",
"itemValue":90
},
]
I want to place all of those objects into an array to manipulate it easier in one of my code. Thus I want to do something like
[
{
"data": [
{
"itemLabel": "Social Media",
"itemValue": 90
},
{
"itemLabel": "Blogs",
"itemValue": 30
},
{
"itemLabel": "Text Messaging",
"itemValue": 60
},
{
"itemLabel": "Email",
"itemValue": 90
}
]
}
]
How do I go about to add in that data array element using Jackson? I have done mostly read using Jackson but have not done too many writes. Any help would be appreciated.
I'm not completely sure what are you intending and there is probably a more elegant solution to this (using POJOs rather than Collections and Jacksons JSON representation), but I guess this example will clear it out to you. But if you have some more complicated processing you might want to write custom (de)serializers or something like that. Written using Jackson 2.3.3
ObjectMapper mapper = new ObjectMapper();
JsonNode parsedJson = mapper.readTree(json); //parse the String or do what you already are doing to deserialize the JSON
ArrayNode outerArray = mapper.createArrayNode(); //your outer array
ObjectNode outerObject = mapper.createObjectNode(); //the object with the "data" array
outerObject.putPOJO("data",parsedJson);
outerArray.add(outerObject);
System.out.println(outerArray.toString()); //just to confirm everything is working