Mapping unpredictable keys from JSON to POJO - json

I am using the Jackson JSON library to map JSON streams into POJO's.
My JSON's keys have unpredictable names.
i.e
{
"Random_ID":
{
"Another_Random_ID":
{
"some_key": "value"
"some_key1": "value1"
}
}
...
}
I would like to map this request to a POJO (with the same structure), however the mapper will fail since there is no such setXXX (where XXX is a random_id - since i cannot predict the name).
What would be the best way to map this request to the corresponding object without manually parsing it with createJsonParser.

If names are unpredictable, POJOs are not the way to go.
But you can use the Tree Model, like:
JsonNode root = objectMapper.readTree(jsonSource);
and access it as a logical tree. Also, if you do want to convert the tree (or any of sub-trees, as identified by node that is the root of sub-tree), you can do:
MyPOJO pojo = objectMapper.treeToValue(node, MyPOJO.class);
and back to tree
JsonNode node = objectMapper.valueToTree(pojo);

Related

Json, Jackson: polymorphism of one field, based on the value of other field

Basically I'm trying to integrate with some other service that sends payloads with the following shape
{
"storeId":"123",
"type": "...",
"data": ...,
"createdAt": "...."
}
That shape never changes except for the property data, whose structure depends on the property type.
How can that be achieved with Jackson?
Use JsonNode to convert from string to json nodes JsonNode node = mapper.valueToTree(fromValue);
Then get your node using JsonNode dataNode = node.get("data"), same for type.
Get the class from the type node using Class yourBeanClass = Class.forName("package.+type from step 2");
Tokenize your data node using JsonParser parser = mapper.treeAsTokens(dataNode);
Finally get your bean instance by doing YourBean bean = mapper.readValue(parser, yourBeanClass);

Reading JSON thru web service into POJOs annotated for Hibernate

I am reading the following json through a web service. Is there a way to read the json into three appropriate POJOs? The POJOs are generated by hibernate and are used to communicate to the database.
Basically I need to read the person json into a Person POJO, the pets json into a set of Pet POJOs, and the toy json into a set of Toy POJOs.
The JSON
{
"person":{"first_name":"John", "last_name":"Smith"},
"pets":[{"species":"dog", "name":"Adama"}, {"species":"cat", "name":"Benton"} ],
"toys":[{"car":"corvet", "color":"black"}, {"action_figure":"hancock", "height":"1ft"} ]
}
The Web Service
#Post
public Representation readForm(Representation representation) {
try {
Person aPerson = …
Set<Pet> petSet = …
Set<Toy> toySet = ...
….
You can use xStream . You will have to create a VO having all 3 types of your objects as properties. Give them respective aliases and you will get all 3 types of objects in that VO. You can get them simply by calling their getters.

Parse a large file of non-schematized json using Jackson?

I have a very large .json file on disk. I want to instantiate this as a Java object using the Jackson parser.
The file looks like this:
[ { "prop1": "some_value",
"prop2": "some_other_value",
"something_random": [
// ... arbitrary list of objects containing key/value
// pairs of differing amounts and types ...
]
},
// ... repated many times ...
{
}
]
Basically it's a big array of objects and each object has two string properties that identify it, then another inner array of objects where each object is a random collection of properties and values which are mostly strings and ints, but may contain arrays as well.
Due to this object layout, there isn't a set schema I can use to easily instantiate these objects. Using the org.json processor requires attempting to allocate a string for the entire file, which often fails due to its size. So I'd like to use the streaming parser, but I am completely unfamiliar with it.
What I want in the end is a Map where the String is the value of prop1 and SomeObject is something that holds the data for the whole object (top-level array entry). Perhaps just the JSON which can then be parsed later on when it is needed?
Anyway, ideas on how to go about writing the code for this are welcome.
Since you do not want to bind the whole thing as single object, you probably want to use readValues() method of ObjectReader. And if structure of individual values is kind of generic, you may want to bind them either as java.util.Maps or JsonNodes (Jackson's tree model). So you would do something like:
ObjectMapper mapper = new ObjectMapper();
ObjectReader reader = mapper.reader(Map.class); // or JsonNode.class
MappingIterator<Map> it = reader.readValues(new File("stuff.json"));
while (it.hasNextValue()) {
Map m = it.nextValue();
// do something; like determine real type to use and:
OtherType value = mapper.convertValue(OtherType.class);
}
to iterate over the whole thing.

deserializing json with arrays

im using jackson to deserialize some Json. I am reading through a large json document and pulling out blocks and telling jackson to take that block and deserialize it to an object that I created (Actually several objects as there are nested arrays) in java to match the json.
The code im using to deserialize is
fooObject newFoo = mapper.readValue(newNode,fooObject.class);
The problem is there is a value in the block that is sometimes a hash such as
addWidgetStrategy={"get":2,"spend":6,"textLabel":"bikes"}
and sometimes an array
addWidgetStrategy=[{"get":1.5,"spend":3,"textLabel":"thursday"},{"get":3,"spend":5,"textLabel":"tuesday"}]
So in fooObject I need to deal with addWidgetStrategy which has its own object. If in fooObject I put
public addWidgetStrategy addWidgetStrategy;
The above works until it tried to deserialize an array
If I put
public List<addWidgetStrategy> addWidgetStrategy;
it works just for arrays and blows up when its just a single hash
How can I parse that same Json element addWidgetStrategy regardless if its an array or a single hash?
For arrays it should be:
fooObject[] newFoo = mapper.readValue(newNode,fooObject[].class);
You can read it like this:
JsonNode jsonNode = mapper.readTree(json);
if (jsonNode.isArray()) {
fooObject[] newFoo = mapper.readValue(jsonNode,fooObject[].class);
...
} else {
fooObject newFoo = mapper.readValue(jsonNode,fooObject.class);
....
}

Marshalling an empty collection to json using jersey

I have a strange issue marshalling an empty object collection to json using jersey with the jaxb based json support. My object looks like
...
#XmlWrapper(name = "stuff") #XmlElement(name = "s")
private List<Foo> foos;
...
Marshaling this to json produces the expected results
... stuff: [{ "s": ... }, { "s": ... }] ...
except when the list is empty. I would expect to see
... stuff: [] ...
but I see
... stuff: [null] ...
instead. Any idea what's wrong? The problem seems to be related to the #XmlElementWrapper annotation, removing it I don't get the the stuff property in the output at all.
Are you serializing an empty list, or are you serializing an un-instantiated null object?
ie. I would expect:
private List<Foo> foos; - would serialize to 'stuff: [null]'
and I would also expect:
private List<Foo> foos = new ArrayList<Foo>(); - we serialize to 'stuff: []'
If that isn't the case, you can always direct Jackson (which is the default JSON serializer bundled with Jersey) to omit the writing of bean properties as null value..
I would suggest using POJO mapping based on Jackson. I am not sure why you want that intermediate "s" in there, but POJO would produce (and consume) simpler structure:
"stuff" : [ { ... }, { ... } ]
For that you need no annotations with POJO mapping; JAXB annotations are only needed for XML processing, since XML has no natural mechanism to distinguish arrays from objects (unlike JSON).
I managed to solve JSON array and primitive field "bug" in Jersey json library. Secret ingredient is JSONConfiguration and ContextResolver magic. See my following post it has a full code example, customized ContextResolver and rest Application class might be somewhat fuzzy logic in first look.
How to serialize Java primitives using Jersey REST
json array for zero or single-element Java lists
primitive integer or boolean fields without quotation chars