Jackson - unwrapping object - java

I am getting responses from server that are wrapped with some additional info. For example:
{
"response_a" : ...,
"some_metadata" : 1234,
"more_metadata" : abcd
}
or
{
"response_b" : [...],
"some_metadata" : 1234,
"more_metadata" : abcd
}
The "response_x" can be custom object, list or hashmap, it can have different name depending on request.
Is there a way deserialize just the response_x, or get it as string using jackson?

You can deserialize above JSON to Map and retrieve the property using get method:
ObjectMapper mapper = new ObjectMapper();
MapType mapType = mapper.getTypeFactory().constructMapType(HashMap.class, String.class, Object.class);
Map<String, Object> result = mapper.readValue(json, mapType);
Object responseX = result.get("response_x");

Related

JSON Deserialization issue : Error deserialize JSON value into type

I have a composite object like below:
Map<String, Object> m = new HashMap<>();
m.put("a", "b");
m.put("c", "{\"a\" :3, \"b\" : 5}");
m = {a=b, c={"a" :3, "b" : 5}}
I have to submit this request via https call in order to deserialize to a java object, hence i converted this to JSON string, using,
objectmapper.writeValueAsString(m)
when i convert it , it is appending quotes to the value of c:
{"a":"b","c":"{\"a\" :3, \"b\" : 5}"}
and While deserializing this object at the client side, the request fails saying
"Error deserialize JSON value into type: class"
Any help??
The type of the value C is String, so the object mapper escapes all illegal characters a wraps the string in quotes.
You could make C an another map:
Map<String, Object> c = new HashMap<>();
c.put("a", 3);
c.put("b", 5);
Map<String, Object> m = new HashMap<>();
m.put("a", "b");
m.put("c", c);
Or you can create custom POJO and use #JsonRawValue annotation:
public class MyPojo{
private String a;
#JsonRawValue
private String c;
// getter and setters
}
MyPojo m = new MyPojo();
m.setA("b");
m.setB("{\"a\" :3, \"b\" : 5}");
objectmapper.writeValueAsString(m);
From the documentation:
Marker annotation that indicates that the annotated method or field should be serialized by including literal String value of the property as is, without quoting of characters. This can be useful for injecting values already serialized in JSON or passing javascript function definitions from server to a javascript client.
Warning: the resulting JSON stream may be invalid depending on your input value.
The client error meaning probably is that it can't deserialize String into an Object (it expects { instead of ").
You better use JSONObject for c value:
JSONObject json = new JSONObject();
json.put("a", 3);
json.put("b", 5);
Map<String, Object> m = new HashMap<>();
m.put("a", "b");
m.put("c", json);
Complete code:
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import net.minidev.json.JSONObject;
import java.util.HashMap;
import java.util.Map;
public static void main(String[] args) throws JsonProcessingException {
JSONObject json = new JSONObject();
json.put("a", 3);
json.put("b", 5);
Map<String, Object> m = new HashMap<>();
m.put("a", "b");
m.put("c", json);
ObjectMapper objectMapper = new ObjectMapper();
String valueAsString = objectMapper.writeValueAsString(m);
System.out.println(valueAsString);
}
The output is:
{"a":"b","c":{"a":3,"b":5}}

Read single value from nested object using Gson in Java

Below is my Json. I want to get the value of "Id" inside "Details" using Gson
{
"name": "testAutomation-1",
"owner": "TestUSer",
"description": "testAutomation-1",
"subSet": [
"test-audit"
],
"labels": "{\"engagementType\":\"Sx\",\"type\":\"collect\"}",
"createdTime": 1548508294790,
"updatedTime": 1548654725381,
"scheduleDateTime": null,
"timeZone": null,
"jobStatus": "Report-Requested",
"loggedInUser": null,
"Details": [
{
"Status": "Completed",
"Id": "fe1f6b40-216b-11e9-a769-25be4c5889e7"
}
]
}
I created a map using the below code. But not sure how to get the value
Map<String, Object> createmap = new HashMap<String, Object>();
Gson gson = new Gson();
Type type = new TypeToken<Map<String, Object>>(){}.getType();
createmap = gson.fromJson(jsonobj.toString(), type);
Assuming that you need the first element of the array, you could do something like this (knowing that DATA is the JSon String you provided):
Map<String, JsonElement> resultMap;
Gson gson = new Gson();
Type type = new TypeToken<Map<String, JsonElement>>() {}.getType();
JsonElement jsonElement = gson.fromJson(DATA, JsonElement.class);
resultMap = gson.fromJson(jsonElement.toString(), type);
JsonArray details = resultMap.get("Details").getAsJsonArray();
JsonObject recordElement = details.get(0).getAsJsonObject();
System.out.println(recordElement.get("Id").getAsString());
All this does is fetch the Details array as a JsonArray and it fetches the value of the Id element afterwards.
Since you seem to be dealing with a UUID, you could maybe benefit from using:
UUID uuid = UUID.fromString(recordElement.get("Id").getAsString());
As per Gson guide, you need to use the correct parameterized type instead of your generic type. Then you can easily access the value you need from the map.
Map<String, Dummy> map = new HashMap<String, Dummy>();
Type t = new TypeToken<Map<String, Dummy>>() {}.getType();
map = new Gson().fromJson(jsonobj.toString(), type);
Then you can get the map value (map.get("details") and read the id from that.
In your JSON Details is a list or an array. So there is not just one id in some cases and you should prepare for that also.
It is usually also a good habit to create DTOs to describe data, so like two below classes:
Data
#Getter
public class Data {
// Recommendation, in Java field name should start lower case
#SerializedName("Details")
List<Detail> details; // Could be some List also
}
Detail
#Getter
public class Detail {
// Recommendation, in Java field name should start lower case
#SerializedName("Id")
private UUID id;
}
This also allows later to flexibly pick up any other fields you need from the same JSON.
Serialize your JSON like:
Data data = gson.fromJson(YOUR_JSON, Data.class);
Check the ids - for example - like:
data.getDetails().forEach(d -> log.info("{}", d.getId()));

Jackson: understand if source JSON is an array or an object

Parsing JSON in Jackson library would require:
for an object
MapType hashMapType = typeFactory.constructMapType(HashMap.class, String.class, Object.class);
Map<String, Object> receivedMessageObject = objectMapper.readValue(messageBody, hashMapType);
for an array of objects
Map[] receivedMessage = objectMapper.readValue(messageBody, HashMap[].class)
What would be the best way to check whether I have array or object in messageBody, in order to route to the correct parsing? Is it just to directly check for array token in MessageBody?
An option is just to treat everything that might be an array as an array. This is often most convenient if your source JSON has just been auto-transformed from XML or has been created using an XML-first library like Jettison.
It's a sufficiently common use case that there's a Jackson switch for this:
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
You can then just deserialize a property into a collection type, regardless of whether it's an array or an object in the source JSON.
If you want to know whether your input is an array or an object, you can simply use the readTree method. A simple example:
ObjectMapper mapper = new ObjectMapper();
String json1 = "{\"key\": \"value\"}";
String json2 = "[\"key1\", \"key2\"]";
JsonNode tree1 = mapper.readTree(json1);
System.out.println(tree1.isArray());
System.out.println(tree1.isObject());
JsonNode tree2 = mapper.readTree(json2);
System.out.println(tree2.isArray());
System.out.println(tree2.isObject());
If you want to be able to deserialize to multiple types, have a look at Polymorphic Deserialization
This is what I did based on the answer from #ryanp :
public class JsonDataHandler {
public List<MyBeanClass> createJsonObjectList(String jsonString) throws JsonMappingException, JsonProcessingException {
ObjectMapper objMapper = new ObjectMapper();
objMapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
List<MyBeanClass> jsonObjectList = objMapper.readValue(jsonString, new TypeReference<List<MyBeanClass>>(){});
return jsonObjectList;
}
}

Jackson deserialize json string with "pseudo array" of objects

I'm trying to read data from this API https://coinmarketcap.com/api/
For this endpoint https://api.coinmarketcap.com/v2/ticker/.
I'm having issues mapping the data field to a POJO. The field really contains an array of objects but in terms of the json it's not really defined as an array.
i.e. instead of
data: [{"id":"1","name":"some object"},{"id":"5","name":"another object"},...]
the json has named fields like so
data: {"1":{"id":"1","name":"some object"},"5":{"id":"5","name":"another object"},...}
I can manually parse this using
objectMapper.readTree(new URL("https://api.coinmarketcap.com/v2/ticker/"));
but is there a way automatically map these to a List?
You can parse it into a map (as #teppic said) and then get the map values as a list.
To deserialize into a map, you can see the answer from this question: Deserializing into a HashMap of custom objects with jackson
TypeFactory typeFactory = mapper.getTypeFactory();
MapType mapType = typeFactory.constructMapType(HashMap.class, String.class, Theme.class);
HashMap<String, Theme> map = mapper.readValue(json, mapType);
Assuming you have a class called Item with the id and name fields, you can do this:
String json = "{\"1\":{\"id\":\"1\",\"name\":\"some object\"},\"5\":{\"id\":\"2\",\"name\":\"another object\"}}";
ObjectMapper mapper = new ObjectMapper();
// create your map type <String, Item>
TypeFactory typeFactory = mapper.getTypeFactory();
MapType mapType = typeFactory.constructMapType(HashMap.class, String.class, Item.class);
HashMap<String, Item> map = mapper.readValue(json, mapType);
// get the list
List<Item> list = new ArrayList<Item>(map.values());
System.out.println(list);
Output:
[Item [id=1, name=some object], Item [id=2, name=another object]]
Your other option would be a custom deserializer, or reading the tree as you mentioned.
try this
String json = "[{\"name\":\"Steve\",\"lastname\":\"Jobs\"}]";
JsonArray jArray = (JsonArray)new JsonParser().parse(json);
String sName = jArray.get(0).getAsJsonObject().get("name").getAsString());
String sLastName = jArray.get(0).getAsJsonObject().get("lastname").getAsString());
see you later.

getting parameter value of json object in java

I have a JsonObject :
{"result":[{"active":"true"}]}
How can I get value of active?
You can use Jackson
ObjectMapper mapper = new ObjectMapper();
Map<String, Object> parsedMap = mapper.readValue(jsonString, new TypeReference<HashMap<String, Object>>() {
});
We get a Map with key value pairs. With your input you will get a Map<List<Map<String,String>>>. So to get "active", you can use something like parsedMap.get("result").get(0).get("active") (Note: This is just a pseudo code)

Categories