Jackson handling Wrapped elements - java

I'm parsing the response from last.fm API. But it seems that they used some wrapper for some of the responses, which is causing a bit of a pain. To put an example:
{
"artists":{
"artist":[
{
"name":"Coldplay",
"playcount":"816763",
"listeners":"120815",
"mbid":"cc197bad-dc9c-440d-a5b5-d52ba2e14234",
"url":"http:\/\/www.last.fm\/music\/Coldplay",
"streamable":"1"
},
{
"name":"Radiohead",
"playcount":"846668",
"listeners":"99135",
"mbid":"a74b1b7f-71a5-4011-9441-d0b5e4122711",
"url":"http:\/\/www.last.fm\/music\/Radiohead",
"streamable":"1"
}
],
"#attr":{
"page":"1",
"perPage":"2",
"totalPages":"500",
"total":"1000"
}
}
}
Not only the response is wrapped in the artists object, but the array of object has also an object wrapper.
So a wrapper class like:
public class LastFMArtistWrapper {
public List<Artist> artists;
}
Would not work. I worked around this, creating two wrapper classes, but this looks really ugly. Is there any way we can use something like the #XMLElementWrapper in Jackson?

The JSON response you are getting back from the provider is a serialized representation of a hierarchy of different objects, but from your description, it sounds like you really only need to use and work with a specific subset of this representation, the collection of artists.
One solution of mirroring this representation involves creating the same hierarchy of Java classes, which creates extra overhead in the form of unneeded classes. From what I understand, this is what you wish to avoid.
The org.json project created a generic JSONObject class, which represents a single, generic key/value pair in a larger JSON representation. A JSONObject can contain other JSONObjects and JSONArrays, mirroring the representation without the extra overhead of maintaining and writing extra classes.
Thus, these two objects can be reused throughout multiple layers of hierarchy in a JSON representation, without requiring you to replicate the structure. Here is an example of how you could proceed:
// jsonText is the string representation of your JSON
JSONObject jsonObjectWrapper = new JSONObject(jsonText);
// get the "artists" object
JSONObject jsonArtists = jsonObjectWrapper.get("artists");
// get the array and pass it to Jackson's ObjectMapper, using TypeReference
// to deserialize the JSON ArrayList to a Java ArrayList.
List<Artist> artists = objectMapper.readValue(
jsonObjectWrapper.getString("artist"),
new TypeReference<ArrayList<Artist>>() { });
Using the above method, you cut out the extra overhead of having to write extra layers of POJO objects that do nothing but add unnecessary clutter.
TestCollectionDeserialization contains some examples of the readValue method when working with collections and may be helpful.

Related

Get all JSON keys in the order they are defined in the file

I am trying to get an array of all the json keys found in a json file loaded with Google Gson. I have used SomeJsonObject.keySet() in the past, but sets do not preserve the order of their contents.
What I am looking for is something like below:
JsonElement schema_pre = new JsonParser().parse(new FileReader("SchemaFile.json"));
JsonObject schema = gson.fromJson(schema_pre, JsonObject.class);
String[] keys_all = schema.keyArray();
From JSON RFC7159:
An object is an unordered collection of zero or more name/value pairs [...]
Consequently, the API you are asking for would return more information than actually contained in the JSON object. This behaviour would therefore not comply with the standard.
If you really need an ordering of your JSON objects, you can always express the information in a JSON compliant way by using arrays instead of objects:
{"first":1, "second":2} // Unordered
[{key:"first",value:1}, {key:"second",value:2}] // Ordered
Another motivation for expressing the information as arrays is that keys might change. Most NoSQL databases are capable of creating indexes on object attributes (e.g. MongoDB) and the normalization above is usually the best way to go, even when an ordering is not required
However, if desired, the map you are looking for can still be created as a temporary index for efficient access of JSON objects by looking them up using a specific key.
Consider JsonReader, it reads from beginning to end:
JsonReader reader = new JsonReader(/*Reader*/);
List<Object> objects = new ArrayList<>();
while (reader.hasNext()) {
String name = reader.nextName();
/////....
}

How to create a JsonNode chaining two arrays?

I'm building a complex JSON object with Jackson and I want to structure the code to display the JSON structure clearly, so I'm trying to use the JsonNode API with chained with, put and set methods. At one place inside all this chained stuff I need to add two arrays one after the other. Here's a simplified excerpt:
com.fasterxml.jackson.databind.ObjectNode json = new ObjectNode(JsonNodeFactory.instance);
json.with("data")
.set("array1", arrayNode1)
.set("array2", arrayNode1);
I want it to create this JSON:
{
"data": {
"array1": [...],
"array2": [...]
}
}
First set is fine because it's called on ObjectNode. The problem is it returns JsonNode, which doesn't have a set method, and so the second set call results in compilation error.
How can I chain setting two arrays?
It doesn't really improve readability that much, but if you have Guava on the classpath, you can do the following:
ObjectNode json = new ObjectNode(JsonNodeFactory.instance);
json.with("data")
.setAll(ImmutableMap.of(
"array1", arraynode1,
"array2", arraynode2));
setAll returns the object node.

Jackson - Working with arrays of objects, appending and removing

I'm working with the Jackson API in Java for dealing with JSON. I've been working with it a bit here and there, but nothing too in-depth.
Currently, I'm looking for a good way to take an array of JSON objects (either via a stream or String) that was created from a list of POJOs and append or remove a POJO. In the case of appending, duplicate checking isn't really necessary. As a simple example, let's say I have this array built from a list of Java objects with a single variable named "field":
[{"field":"value"},{"field":"value2"}]
And I'd like to append an object of the same type with "field" set to "value3". I could simply deserialize the whole array into a List of Java Objects, add the new object, then serialize it back into JSON, but that feels like overkill. It would be better if I could use Jackson to simply serialize the new object and append it to the end of the JSON array. The same would apply to removing an existing object from the array.
I've found a way, but strangely, it's over twice as slow as the direct deserialize-add-reserialze method with a list of 500 POJOs that have three fields each, and it only gets worse with more objects.
ObjectMapper mapper = new ObjectMapper();
JsonParser parser = mapper.getJsonFactory().createJsonParser(input);
JsonGenerator gen = mapper.getJsonFactory().createJsonGenerator(output, JsonEncoding.UTF8);
gen.writeStartArray();
parser.nextToken();
while (parser.nextToken() == JsonToken.START_OBJECT) {
//gen.writeTree(parser.readValueAsTree());
//parser.skipChildren();
//EDIT: This is much faster as the only method in the loop:
gen.copyCurrentStructure(parser);
}
gen.writeTree(mapper.valueToTree(/*new Object to add*/);
gen.writeEndArray();
gen.close();
parser.close();
Even if I don't get each object as a tree and instead move them iteratively as fields/values, it's a bit faster, but still considerably slower than the alternative. Is this to be expected or is there a better way to handle it as streaming data rather than the bulk JSON-to-Java-to-JSON method?
EDIT: AHA! Found that the JsonGenerator can directly copy the current structure from a JsonParser withcopyCurrentStructure(JsonParser). Using this in the while loop is faster and now outruns the bruteforce method by a considerable amount.

Jackson: deserialization of Object Array having different Type Items

Jackson: Deserialization of Object Array having different Type Items
I am converting an object array to json, that works fine. But when I try to get object array back from json, some items lose their type. For example: java.sql.Date object are converted into String.
Example:
List<Object> list = new ArrayList<Object>();
list.add(BigDecimal.ONE);
list.add(new java.util.Date(System.currentTimeMillis()));
String text = objectMapper.writeValueAsString(list );
List<Object> deserializeList = objectMapper.readValue(text , TypeFactory.defaultInstance().constructCollectionType(ArrayList.class, valueType));
BigDecimal is converted to Integer
Date is converted to String
Kindly, help me to maintain the datatype of the object array without using POJO class, i need to return a multi dimension array of object from my server to the client.
First of all, I don't exactly know your needs and your API but the best way to fix your problem is to use a POJO and let Jackson deal with clear data types (not Object).
But ok, you don't want that. What you can do is to implement a custom deserializer for Jackson (see http://wiki.fasterxml.com/JacksonHowToCustomDeserializers).
If you really want to keep a collection for the serialization / deserialization, just create a new class, child of List, and create a custom deserializer for it.

Jackson multiple objects and huge json files

I get the feeling that the answer might be a duplicate of this: Jackson - Json to POJO With Multiple Entries but I think that potentially the question is different enough. Also I'm using raw data binding rather than full data binding.
So like the asker of that question, I have multiple objects in a file and I'm trying to turn them into POJOs and stuff them into a database of my design so I can access the data quickly rather than slowly.
The files here are in the order of tens of GB, with up to millions of objects in each file. Anyway here is what I have so far:
ObjectMapper mapper = new ObjectMapper();
Map<String,Object> data = mapper.readValue(new File("foo.json"), Map.class);
System.out.println(data.get("bar"));
And this works great for printing the bar element of the first object in foo, but I need a way to iterate through every element in a way that won't eat up all my memory.
Thanks.
You don't have to choose between Streaming (JsonParser) and ObjectMapper, do both!
Traverse a bit with parser, but then call JsonParser.readValueAs(MyType.class) to bind individual JSON Object.
Or, call ObjectMapper's readValue() method passing JsonParser at appropriate points. Or use ObjectMapper.reader(Type.class).readValues() and iterate that way.
Use this code sample to see the basic idea.
final InputStream in = new FileInputStream("json.json");
try {
for (Iterator it = new ObjectMapper().readValues(
new JsonFactory().createJsonParser(in), Map.class); it.hasNext();)
System.out.println(it.next());
}
finally { in.close();} }
Assuming you have an array wrapping your objects, create a JsonParser and then call readValuesAs with the appropriate type. It gives you back an Iterator with all your objects that reads through the file as you consume the objects.

Categories