Embedded list of Data Object in Jackson - java

I have a JSON structure that looks like this:
{"data": [{"mykey": "someval"}, {"mykey": "someotherval"}], "foo": "bar"}
I also have
public MyClass {
public String mykey;
}
Now I would like to deserialize the content of "data" of my JSON into a List<MyClass> using Jackson, so I have this code:
ObjectMapper mapper = new ObjectMapper();
List<MyClass> l = (List<MyClass>) mapper.readerFor(new TypeReference<List<MyClass>>(){}).
withRootName("data").readValue(myJSONString);
However this gives me an exception:
com.fasterxml.jackson.databind.JsonMappingException: Unexpected token (FIELD_NAME),
expected END_OBJECT: Current token not END_OBJECT (to match wrapper object with root name
'data'), but FIELD_NAME
Anyone know what I need to do to get this parsed?

Update
List<MyClass> l = (List<MyClass>) mapper.readValue(mapper.readTree(myJSONString).findPath("data").toString(),
new TypeReference<List<MyClass>>(){});
This one line will retrieve the list you are looking for. The problem with what you are trying to do is you are trying to deserialize for the case where the data constitutes the JSON like
{"data":[{"mykey": "someval"}, {"mykey": "someotherval"}]}
But your JSON has additional values which is causing the issue. The above code isolates the data array using the Jackson JSON tree parser and then deserailize it in to the list.
Initial answer
Have a root object so that you can capture the list within that object.
#JsonInclude(JsonInclude.Include.NON_NULL)
#JsonPropertyOrder({
"data"
})
public class Root {
#JsonProperty("data")
private List<MyClass> data = null;
#JsonProperty("data")
public List<MyClass> getData() {
return data;
}
#JsonProperty("data")
public void setData(List<MyClass> data) {
this.data = data;
}
}
Now use the objectMapper to deserialize to this object.
Root root = mapper.readValue(myJSONString,Root.class);
Let me know how it works out.

Related

How to serialize an object and read it into JsonNode?

I have a simple Java object that has a String property that contains serialized java objects:
#Data
private class Test {
int id;
String data;
}
The data is created with
test.setData(mapper.writeValueAsString(anyJavaObjectThatShouldBeSerialized));
Then I send the Test Object to another server, where I want to deserialize the object from the data property into a JSONNode:
JsonNode node = mapper.readTree(test.getData());
The problem is, that the result of readTree is a TextNode and not a JsonNode. Therefore I can not access the properties of the serialized data.
The problem might be that the serialized object is interpreted as a String because when I print it it has quotationsmarks around it:
"{\"id\":39,\"name\":\"Im a test value\"}"
How do I get a JsonNode from the String that contains serialized objects? Please note that the data can be ANY Java object, that's why I serialized it into a String.
Make a double deserialization - first deserialize the string payload to json, then deserialize the json to class, or JsonNode in your case.
public class Temp {
public static void main(String[] args) throws Exception {
String payload = //your string payload;
ObjectMapper mapper = new ObjectMapper();
String json = mapper.readValue(payload, String.class);
JsonNode node = mapper.readTree(json);
}
}
As you noted, the cause is that you are making double serialization - first when setting the data field, then again when sending data to the other server.
You can avoid this double serialization/deserialization by making data an object.
#Data
class Test {
int id;
Object data;
}
test.setData(anyJavaObjectThatShouldBeSerialized);
Like this it will be serialized like JsonObject.
{
"id": 39,
"name": "Im a test value"
}

Add a parent node to Json output in java

I have converted some info to Json format using Jackson in Java. Below is the output I get
[{"lat":45.9,"lon":10.9,"title":"Title A1","html":"<h3>Content A1</h3>","icon":"http://maps.google.com/mapfiles/markerA.png"},{"lat":44.8,"lon":1.7,"title":"Title B1","html":"<h3>Content B1</h3>","icon":"http://maps.google.com/mapfiles/markerB.png","show_infowindow":false},{"lat":51.5,"lon":-1.1,"title":"Title C1","html":"<h3>Content C1</h3><p>Lorem Ipsum..</p>","zoom":8,"icon":"http://maps.google.com/mapfiles/markerC.png"}]
My question is how can I get it in the below format, basically adding the Json to a root node which called locations
{"locations":[{"lat":45.9,"lon":10.9,"title":"Title A1","html":"<h3>Content A1</h3>","icon":"http://maps.google.com/mapfiles/markerA.png"},{"lat":44.8,"lon":1.7,"title":"Title B1","html":"<h3>Content B1</h3>","icon":"http://maps.google.com/mapfiles/markerB.png","show_infowindow":false},{"lat":51.5,"lon":-1.1,"title":"Title C1","html":"<h3>Content C1</h3><p>Lorem Ipsum..</p>","zoom":8,"icon":"http://maps.google.com/mapfiles/markerC.png"}]}
You may wrap the array into a JSONObject like so
ObjectMapper mapper = new ObjectMapper();
Map<String, Object> map = new HashMap<String, Object>();
String json = jsonArray.toString();
map.put("locations", json);
json = mapper.writeValueAsString(map);
you can achieve this by following changes.
Let's Assume, your JSON will be created based upon Bean.java class likewise,
[{"lat":45.9,"lon":10.9,"title":"Title A1","html":"<h3>Content A1</h3>","icon":"http://maps.google.com/mapfiles/markerA.png"},{"lat":44.8,"lon":1.7,"title":"Title B1","html":"<h3>Content B1</h3>","icon":"http://maps.google.com/mapfiles/markerB.png","show_infowindow":false},{"lat":51.5,"lon":-1.1,"title":"Title C1","html":"<h3>Content C1</h3><p>Lorem Ipsum..</p>","zoom":8,"icon":"http://maps.google.com/mapfiles/markerC.png"}]
Now, As per your new requirement, you want something likewise,
{"locations":[{"lat":45.9,"lon":10.9,"title":"Title A1","html":"<h3>Content A1</h3>","icon":"http://maps.google.com/mapfiles/markerA.png"},{"lat":44.8,"lon":1.7,"title":"Title B1","html":"<h3>Content B1</h3>","icon":"http://maps.google.com/mapfiles/markerB.png","show_infowindow":false},{"lat":51.5,"lon":-1.1,"title":"Title C1","html":"<h3>Content C1</h3><p>Lorem Ipsum..</p>","zoom":8,"icon":"http://maps.google.com/mapfiles/markerC.png"}]}
So, in this case you need to create one more class, let's say it's SuperBean.java then it should be likewise,
public class SuperBean {
private Bean [] locations;
public Bean[] getBean() {
return locations;
}
public void setBean(Bean[] locations) {
this.locations = locations;
}
}
So, your JSON will be created likewise,
{"locations":[......]} // as per your requirement.

In Gson, how can I deserialize a Map of Lists of arbitrary classes?

I have an API that returns data as follows:
{
gene: [ ],
attribute: [ ],
dataset: [ ]
}
Each List contains an object that should be deserialized to a specific class. For example, each JSON object in the dataset list should be deserialized to a Dataset class. I have not been able to get this to work. Here is my current attempt:
Type listType = new TypeToken<HashMap<String, List<Object>>>() {}.getType();
HashMap<String, List<Object>> map = new Gson().fromJson(json, listType);
List<Dataset> datasets = (List<Dataset>) (Object) map.get("dataset");
Dataset ds = datasets.get(0);
The error I am getting is:
java.lang.ClassCastException: com.google.gson.internal.LinkedTreeMap cannot be cast to Dataset
Thanks in advance.
A colleague suggested writing a wrapper class to describe the schema. This has made serializing and deserializing straightforward.
public class JsonSchema {
private List<Dataset> datasets;
...
public List<Dataset> getDatasets() {
return datasets;
}
public void setDatasets(List<Dataset> datasets) {
this.datasets = datasets;
}
...
}
Then, to serialize the JSON, I use the setters and to deserialize the JSON, I write:
JsonSchema jsonSchema = new Gson().fromJson(json, JsonSchema.class);
List<Dataset> datasets = jsonSchema.getDatasets();

Populate POJO with array (root JSON node) using Jackson

I'm using Jackson and RESTEasy to hook into an external API. The API mainly returns simple objects which I have managed to successfully populate into POJOs.
I'm hitting a problem where I get an array of objects back e.g.
[
{
"variable1": "someValue1",
"variable2": "someValue2",
"variable3": "someValue3"
}
{
"variable1": "someValue4",
"variable2": "someValue5",
"variable3": "someValue6"
}
{
"variable1": "someValue7",
"variable2": "someValue8",
"variable3": "someValue9"
}
]
I have 2 classes: one called VariableObject which looks like this:
public class VariableObject {
private String variable1;
private String variable2;
private String variable3;
}
and VariableResponse which looks like:
public class VariableResponse {
private List<VariableObject> variableObjects;
}
My client uses JAXRS Response class to read the entity into the class i.e
return response.readEntity(VariableResponse.class);
I get a stack trace which reads:
Caused by: org.codehaus.jackson.map.JsonMappingException: Can not deserialize instance of VariableResponse out of START_ARRAY token
I understand you can return these as a List of POJOs i.e List quite easily, but this is not what I want to do.
The question really is two parts:
a. Can I possibly populate the VariableResponse POJO using Jackson (some how) preferably without a customer deserialiser? Maybe some annotation exists (this would be ideal)?
b. Is there some way to detect if an Array is being retuned as the root JSON node in the response and then act accordingly?
Help greatly appreciated.
Your JSON is indeed an array of objects.
You can deserialize it with:
response.readEntity(new GenericType<List<VariableObject>>() {});
And then create a new instance of VariableResponse passing resulting List as a constructor parameter like this:
public class VariableResponse {
private final List<VariableObject> variableObjects;
public VariableResponse(List<VariableObject> variableObjects) {
this.variableObject = new ArrayList<>(variableObjects);
}
}
You might forget to add comma after each {..}. After correcting your JSON string, I converted it into ArrayList<VariableObject> using TypeReference and ObjectMapper.
sample code:
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.type.TypeReference;
...
TypeReference<ArrayList<VariableObject>> typeRef = new TypeReference<ArrayList<VariableObject>>() {};
ObjectMapper mapper = new ObjectMapper();
try {
ArrayList<VariableObject> data = mapper.readValue(jsonString, typeRef);
for (VariableObject var: data) {
System.out.println(var.getVariable1()+","+var.getVariable2()+","+var.getVariable3());
}
} catch (Exception e) {
System.out.println("There might be some issue with the JSON string");
}
output:
someValue1,someValue2,someValue3
someValue4,someValue5,someValue6
someValue7,someValue8,someValue9
If you prefer your own response type direct.
Try just extending ArrayList?
public VariableResponse extends ArrayList<VariableObject> {
}

Parse JSON array with jackson where objects are strings?

I have a slightly odd question. I have created an object, let's call it a Profile, that successfully parses single JSON objects via an API that I call. There is also a multi-profile interface that will return a JSON array of Profile objects. The problem is, the multi-profile interface turns the sub objects into strings. Is there an automatic way I can tell jackson to parse these into objects?
Example of a single object:
{ "foo": "bar" }
Example of a multi object:
[ "{ \"foo\": \"bar\" }", "{ \"blah\": \"ugh\" }" ]
(Sorry can't use real data)
Notice that the sub objects are actually strings, with escaped quotes inside them.
For completeness, my code for the multi object parse looks like this:
ObjectMapper mapper = new ObjectMapper();
Profile[] profile_array = mapper.readValue(response.content, Profile[].class);
for (Profile p: profile_array)
{
String user = p.did;
profiles.put(user, p);
}
As I said, in the single-profile case, the Profile object parses. In the multi-profile case, I get this exception:
Exception: org.codehaus.jackson.map.JsonMappingException: Can not construct instance of com.xyz.id.profile.Profile, problem: no suitable creator method found to deserialize from JSON String
I suppose you'll have to create a custom deserializer and apply it to the every element of that array.
class MyCustomDeserializer extends JsonDeserializer<Profile> {
private static ObjectMapper om = new ObjectMapper();
#Override
public Profile deserialize(JsonParser jp, DeserializationContext ctxt) {
// this method is responsible for changing a single text node:
// "{ \"foo\": \"bar\" }"
// Into a Profile object
return om.readValue(jp.getText(), Profile.class);
}
}
There is no out-of-the-box support for "re-parsing" of embedded JSON-in-JSON content.
But this sounds like a possible request for enhancement (RFE)...
Have you tried using JAXB?
final ObjectMapper mapper = new ObjectMapper();
// Setting up support of JAXB
final AnnotationIntrospector introspector = new JaxbAnnotationIntrospector();
// make deserializer use JAXB annotations (only)
mapper.getDeserializationConfig().setAnnotationIntrospector(
introspector);
// make serializer use JAXB annotations (only)
mapper.getSerializationConfig().setAnnotationIntrospector(
introspector);
final StringReader stringReader = new StringReader(response);
respGetClasses = mapper.readValue(stringReader,
FooBarClass.class);
The above should get you started...
Also, you would need to mark each subclass like so:
#XmlElement(name = "event")
public List<Event> getEvents()
{
return this.events;
}

Categories