Deserialise array of BSON objects as root object in Java? - java

My goal is to deserialise a BSON array of documents on Android. When the outer array is anonymous the deserialisation fails (trying to deserialise to CustomObject[].class). When the array is wrapped in an object with a key containing the array it works (deserialising to WrapperObject.class).
In JSON the object would look like this:
[{"id":....},{"id":....},{"id":....}....]
According to the BSON Specification the BSON array is a regular BSON document with integer values for keys. In other words the same object in BSON looks like this:
{"0":{"id":....},"1":{"id":....},"2":{"id":....}....}
I was trying to deserialise the above using bson4jackson when it threw a "Can not deserialize instance of x out of START_OBJECT token" error and in the stacktrace I noticed the unexpected token was a "0" - the BSON representation for the beginning of an array.
My solution, for now, is to wrap the array in a new root object which, in JSON, would look like this:
{"data":[{"id":....},{"id":....},{"id":....},....]}
Can you set bson4jackson, or any other deserialisation library for Java, to treat the root object as an array and deal with it as is, without wrapping?

The reason for this is that bson4jackson is a low-level library which does not know about the type of the object currently being parsed. In BSON arrays are objects and bson4jackson just assumes that every document has an object as its root.
However, there is a workaround for this. Jackson calls the low-level parser's isExpectedStartArrayToken() method whenever an array is read. So, it is possible for bson4jackson to switch to array parsing if the current object is a document, but an array is expected.
The fix has just been implemented. For further details see:
https://github.com/michel-kraemer/bson4jackson/issues/31

Related

How to convert efficiently a Java object to SoyData in Google Closure

We have a Java object with data that we want to use for template rendering - is there an efficient way of converting Java object to SoyData?
Currently we are using gson to convert it to tree and recursively populate SoyData object, but maybe there is something that wouldn't require gson conversion.
So in the end I added a constraint that the data can be only in a form of a map and I was able to use a library object com.google.template.soy.data.SoyMapData to do the work.

Gson streaming API with different types of objects according to value of JSON field

I'm trying to parse the Wikidata JSON dump using the Gson streaming API, since the file is around 70GB of json. The overall structure of the file is as follows:
[
{"type":"item",... other fields ...},
{"type":"property",... other fields ...},
.....
]
It is an array of objects in which each object can be of type item or property and I would like to instantiate a different class (namely I have a corresponding Item and Property class in my Java code) according to the object that I encounter.
Basically, I'd like to look at the type field and then parse the following JSON accordingly. Since the JsonReader doesn't seem to provide a getNextJsonObject() or similar function, is there a way to do this besides preprocessing the whole file and splitting the entries into two separate ones? The file is so big that I'd like to avoid the extra preprocessing step when I could do everything on the fly.
I actually found a very easy solution after a bit of thinking. The Gson API provides the method:
Gson.fromJson(JsonReader reader, Class class)
This will read the next object from the reader and deserialize to the class you pass as parameter. Since in my case I don't know which class to serialize to I can do the following:
JsonObject asd = gson.fromJson(reader, JsonObject.class);
if (asd.get("type").getAsString().equals("item")) {
// Instantiate item
} else {
// Instantiate property
}

Parsing a cypher query result (JSON) to a Java object

I use the jersey/jackson stack to address a neo4j database via the REST api, but I have some issues how to interpret the result.
If I read the node by its ID (/db/data/node/xxx) the result can be mapped to my DTO very easy by calling readEntity(MyDto.class) on the response. However, usage of internal IDs is not recommended and various use cases require to query by custom properties. Here cypher comes into play (/db/data/cypher).
Assuming a node exists with a property "myid" and a value of "1234", I can fetch it with the cypher query "MATCH (n {myid: 1234}) RETURN n". The result is a JSON string with a bunch of resources and eventually the "data" I want do unmarshall to a java object. Unmarshalling it directly fails with a ProcessingException (error reading entity from input stream). I see no API allowing to iterate the result's data.
My idea is to define some kind of generic wrapper class with an attribute "data", giving this one to the unmarshaller, and unwrapping my DTO afterwards. I wonder if there is a more elegant way to do this, like using "RETURN n.data" (which does not work) or something like this. Is it?
You should look into neo4j 2.0 where return n just returns the property map.
I usually tend to deserialize the result as a nested list/map (i.e. have ObjectMapper read to Object.class or Map.class) structure and grab the data map directly out of that.
There's probably a way to tell jackson to ignore all the information around that data field
If you want to have a nicer presentation you can also check out my cypher-rs project which returns only the data in question, nothing more.

Using reflection to set property values where types are unknown

I am using reflection to set value object properties at runtime. If everything were a string, I may not be asking this question, but that's not the case. I have a web service that returns json and I want to use the json returned by the service to populate the object. I have an ArrayList of strings called alphabeticalKeys that contains sorted keys in the json string. Here is the code I am using to dynamically populate the object (user):
for(String fieldName : alphabeticalKeys){
Log.d("JSON:" + fieldName, json.getString(fieldName));
Field f = userClass.getDeclaredField(fieldName);
f.setAccessible(true);
f.set(user, jsonObject.get(fieldName));
}
In the json data set, there are strings, doubles and more. This is part of a factory class where the type of object being returned is unknown at compile time. Also, the json fields' data types may vary depending on the type of object needed.
The json output matches the field names in the returned object, so I am looking for a way to handle the different data types returned in the json output. Can somebody offer up a suggestion?
Thx! Vivian
There are libraries available to aid in setting property values using reflection, converting to the appropriate type if necessary. For example, Spring Framework's BeanWrapper and Apache Commons BeanUtils.
There are also json libraries that will handle mapping json to/from java objects. For example, Gson and Jackson. This may make it easier, especially if the json structure closely matches the java object structure.

How would I decode this JSON with GSON (multi-d-array)?

I am using GSON to decode JSON strings that are returned from our server. I haven't had any problems, until I ran into this one particular JSON return from the API. The return is in the following format:
"success":1,"errors":[],"data":{"524":{"id":"524"}, "525":{"id":"525"}}
For the other returns I had data as an array of a class of my own creation, but for this return it says that it is an object and not an array. So how should I format my class?
***edit: What I am having trouble with is that the '524' and '525' fields are not static names. They are dependent on what the user's credentials are. There could be fields 323, 324, 325 or a single one 123. It all depends. How would I be able to handle this dynamically?
SOLVED*
What I had to was make 'data' a <String, Object> hashmap in my custom class. Then after the first decoding, I turned 'data' into an array of type Object []. Then for each Object[i], I converted it into a JSON string. After that I used gson.fromJson() to convert it into what I had originally intended for it to be.
If the API is giving inconsistent results and you can't find a reason on your end why it is doing so, one option is to parse the object into a GSON JSONObject o = gson.fromJson(String) and then convert the data to a list if it is not one already by doing o.getElement("data").isList(), etc..
When this is complete, you can then create the object via gson.fromJson(JSONObject,Class). The alternative is to have two classes, one for each instance, but this seems sloppy if this is the only reason to have two different classes.
GSON is correct. From server reply data is object with two members that are objects also. To be array data should have square brackets [] instead of curly brackets {}. More about JSON format here.
Server format was changed or you tried another API version or someone made bug on server side.

Categories