json jackson inner collection deserialize as LinkedHashMap instead of MyObject - java

I have the following object structure, on which I can't do any modification (this is a library and I don't have sources) :
class Foo {
List bars; // with no generics (but this is a List<Bar>)
Baz baz;
}
class Bar {
Fiz fiz;
Buz buz;
}
class Baz {
int toto;
}
class Fiz {
String titi;
}
class Buz {
String tata;
}
And the following json :
{
"bars" : [
{
"fiz" : {
"titi" : "Hello"
},
"buz" : {
"tata" : "World"
}
},
{
"fiz" : {
"titi" : "Hola"
},
"buz" : {
"tata" : "El Mundo"
}
}
],
"baz" : {
"toto" : 42
}
}
I try to deserialize the json with the following code :
ObjectMapper mapper = new ObjectMapper();
// ... use the visibilityChecker because objects are immutable (no setter)
mapper.readValue(json, Foo.class);
I retrieve a List of LinkedHashMap instead of a List of Bar. I've check all others posts on the subject on stackoverflow, but each time, the list is at the top level so it is quite easy. I tried to use mixin without success, i tried with enableDefaultTyping but i got an error...
How can i do this ? I repeat I cannot modify the class files, add annotations, add intermediary objects, ... everything is in a library.
EDIT 21/12/2016 :
I tried with Mixin :
abstract class FooMixin {
#JsonProperty("bars")
#JsonDeserialize(contentAs = Bar.class)
List bars;
}
abstract class BarMixin {
#JsonProperty("fiz") Fiz fiz;
#JsonProperty("buz") Buz buz;
}
mapper.addMixin(Foo.class, FooMixin.class);
mapper.addMixin(Bar.class, BarMixin.class);
But got the same result (LinkedHashMap)...

Even if you don't control library code, you can still use mix-in annotations:
http://www.cowtowncoder.com/blog/archives/2009/08/entry_305.html
which is the way to go. One possibility here is to "mix in":
#JsonDeserialize(contentAs=Bar.class)
to augment type information; this needs to be before List-valued field or setter-method used to assign it.

Related

Deep JSON structures to Java object, is there a better way than what I did? It looks horrendous

So I have this JSON structure I'm getting as a HTTP response. Structure looks like this:
{
"field1": "value1",
"field2": {
"field2-2": [
{
"field2-2-1": "some value",
"field2-2-2": {
"key" : "some value"
}
}
]
}
}
Of course I simplified it but you get the idea. Now I use Gson to convert it into a HashMap:
HashMap<String, Object> resultMap = new Gson().fromJson(httpResult, type);
Now, to get to the "field2-2-2" in Java I do this:
LinkedTreeMap someMap = (LinkedTreeMap) resultMap.get("field2");
ArrayList<LinkedTreeMap> someList = (ArrayList) someMap.get("field2-2");
LinkedTreeMap anotherMap = someList.get(0);
anotherMap.get("key");
Once again I simplified it, but is there any better way to access this "deep" field? Or for the sake of readability can I chain methods somehow? What I mean is if this all have been ArrayLists I could do something like:
ArrayList<Arraylist<String>> sampleList = new ArrayList<>();
sampleList.get(0).get(0);
You could directly map it to POJO classes. Like:
#JsonInclude(Include.NON_NULL)
public class ApiResponse {
String field01;
Field2Class field02;
}
Field2Class.class
public class Field2Class {
ArrayList<Field02_2> field02_2;
}
Field02_2.class
public class Field02_2 {
String field02_2_1, field02_2_2;
}
With each class having getters, setters and default constructors.

Mapping JSON to Java Class using Jackson

I have a scenario where at run time depending upon the JSON values I have to convert them to choose which JAVA classes we have to use to initialize them.
Below are the two JSON examples
JSON Example1:
{
"parentVal1" : "parent Val 1",
"parentVal2" : "parent val 2",
"childVal1" : "child Val 1",
"childVal2" : {
"childPropVal1" : "child prop 1",
"grandChild" : {
"maleGrandChildVal1" : "male grand child 1",
"maleGrandChildVal2" : "male grand child 2"
}
}
}
JSON Example2:
{
"parentVal1" : "parent Val 1",
"parentVal2" : "parent val 2",
"childVal1" : "child Val 1",
"childVal2" : {
"childPropVal1" : "child prop 1",
"grandChild" : {
"femaleGrandChildVal1" : "female grand child 1",
"femaleGrandChildVal2" : "female grand child 2"
}
}
}
Now, corresponding JAVA classes which I made and class diagram are
public class Parent{
private String parentVal1;
private String parentVal2;
//getters and setters
}
public class Child extends{
private String childVal1;
private ChildProperty childVal2;
//getters and setters
}
public class ChildProperty{
private String childPropVal1;
private GrandChildAbstract grandChild;
//getters and setters
}
public class GrandChildAbstract{
}
public class MaleGrandChild extends GrandChildAbstract{
private String maleGrandChildVal1;
private String maleGrandChildVal2;
//setters and getters
}
public class FemaleGrandChild extends GrandChildAbstract{
private String femaleGrandChildVal1;
private String femaleGrandChildVal2;
//setters and getters
}
The problem is on the runtime I will get a JSON and have to decide whether it belongs to MaleGrandChild or FemaleGrandChild depending upon the ChildProperty.childPropVal1 that I get from JSON.
Can anyone help me are there any way which could initialize the ChildProperty.grandChild property with the respective FemaleGrandChild or MaleGrandChild object.
I am using Jackson for conversion from JSON to JAVA.
Did you read about polymorphic deserialization with Jackson? http://wiki.fasterxml.com/JacksonPolymorphicDeserialization.
Usually you provide the type using JsonTypeInfo. That allows Jackson to understand how to map a specific object.
If you can't really do any manipulation on the JSON (if not sourced by you, for example), you can write a custom mapper (not straightforward) or you can those properties to a simple Map, and let another part of your application deal with the type.

How can I create a JSON object given a schema and object

I have an object that I'd like to generate a JSON object for.
Currently I have an object that is wrapped by another object, a ResourceObject.
This resource object only has a subset of the exposed variables on the top level object.
I use this resource object to generate the json by annotating it. This is to keep the original object cleaner as it's used in other places in the code. However, maintaining this filtering code increases complexity and is mostly boilerplate code.
Ideally, I'd like to pass the object and the expected schema to do the filtering for me, but I don't see anything like that available. I'm hoping my search criteria is just not correct.
Example Class1
Class1 {
private String name = "C1";
private String version = "1.0";
public String getName() {
return name;
}
public String getVersion() {
return version;
}
}
Example Resource class
ResourceClass1 {
private Class1 class1;
ResourceClass1 (Class1 c1) {
class1 = c1;
}
public String getName() {
return class1.getName();
}
}
Example Schema
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "Class1",
"type" : "object",
"properties" : {
"name" : {
"type" : "string"
},
}
}
Right now, getting the JSON from the Resource class gives me something to the affect of:
{
"properties" : {
"name" : "C1"
},
}
Is there a package which would get me the same json by passing in only the schema and java object?

Is it possible to de/serialize map itself polymorphic in jackson?

I've searched a lot and only find questions about polymorphic deserialization on the content inside a map. Is it possible to polymorphic deserializing the map itself?
For example, I have a Book class contains a Map as a member variable.
public class Book {
#JsonProperty
private Map<String, Object> reviews;
#JsonCreator
public Book(Map<String, Object> map) {
this.reviews = map;
}
}
Another class have a list of Book class.
public class Shelf {
#JsonProperty
private List<Book> books = new LinkedList<>();
public void setBooks(List<Book> books) {
this.books = books;
}
public List<Book> getBooks() {
return this.books;
}
}
And a test class. One book's review map is a Hashtable and another book's review map is a HashMap.
public class Test {
private Shelf shelf;
#BeforeClass
public void init() {
Map<String, Object> review1 = new Hashtable<>(); // Hashtable here
review1.put("test1", "review1");
Map<String, Object> review2 = new HashMap<>(); // HashMap here
review2.put("test2", "review2");
List<Book> books = new LinkedList<>();
books.add(new Book(review1));
books.add(new Book(review2));
shelf = new Shelf();
shelf.setBooks(books);
}
#Test
public void test() throws IOException{
ObjectMapper mapper = new ObjectMapper();
mapper.configure(SerializationFeature.INDENT_OUTPUT, true);
// mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
String json = mapper.writeValueAsString(shelf);
System.out.println(json);
Shelf sh = mapper.readValue(json, Shelf.class);
for (Book b : sh.getBooks()) {
System.out.println(b.getReviews().getClass());
}
}
}
The test output
{
"name" : "TestShelf",
"books" : [ {
"reviews" : {
"test1" : "review1"
}
}, {
"reviews" : {
"test2" : "review2"
}
} ]
}
class java.util.LinkedHashMap
class java.util.LinkedHashMap
The serialization works fine. But after deserialization, both review1 and review2 are LinkedHashMap. I want review1 and review2 to be their actual types which are Hashtable to review1 and HashMap to review2. Is there any way to achieve this?
I don't want to use mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); because it will add the type info for all json properties in the json message. And if there is any better way to do it I don't want to use customized deserializer either. Thanks in advance.
I posted the question on Jackson user forum and they suggest to customized the TypeResolverBuilder and set it in the ObjectMapper instance.
ObjectMapper.setDefaultTyping(...)
My customized TypeResolverBuilder is below and it solved my problem.
public class MapTypeIdResolverBuilder extends StdTypeResolverBuilder {
public MapTypeIdResolverBuilder() {
}
#Override
public TypeDeserializer buildTypeDeserializer(DeserializationConfig config,
JavaType baseType, Collection<NamedType> subtypes) {
return useForType(baseType) ? super.buildTypeDeserializer(config, baseType, subtypes) : null;
}
#Override
public TypeSerializer buildTypeSerializer(SerializationConfig config,
JavaType baseType, Collection<namedtype> subtypes) {
return useForType(baseType) ? super.buildTypeSerializer(config, baseType, subtypes) : null;
}
/**
* Method called to check if the default type handler should be
* used for given type.
* Note: "natural types" (String, Boolean, Integer, Double) will never
* use typing; that is both due to them being concrete and final,
* and since actual serializers and deserializers will also ignore any
* attempts to enforce typing.
*/
public boolean useForType(JavaType t) {
return t.isMapLikeType() || t.isJavaLangObject();
}
}
This solution requires both server side and client side to use the customized TypeResolverBuilder. I know it is not ideal, but it is the best solution I found so far. The details of the solution can be found in this post on my blog.
The readValue call has no idea where the input JSON came from. It doesn't know that it was generated from a Hashtable or a HashMap or a TreeMap or any other type of Map. All it has to work with is the target type, Shelf, and its nested Book. The only thing Jackson can introspect from Book is that it has a field of type Map.
Map is an interface. Since you can't instantiate an interface, Jackson has to make a decision on the implementation type of Map that it wants to use. By default, it uses LinkedHashMap. You can change the default by following the solution posted here.
An alternative is to declare the field with the concrete type you want
private HashMap<String, Object> reviews;
Now Jackson knows to deserialize the JSON into a HashMap. Obviously, this will only work with a single type.
The actual solution is for you not to care about the actual implementation class. You decided it was going to be a Map. You shouldn't care what implementation it uses under the covers. Use the power of polymorphism.
(Note that the use of Hashtable has long been discouraged.)

Using Gson to parse Json array and object with no name

I know there are many JSON with GSON questions but none of them relate to me directly. My JSON is formatted differently.
I have a JSON data I want to parse using GSON which looks like the following:
[
{
"foo":"1",
"bar":[ { "_id":"bar1"} ],
"too":["mall", "park"]
}
]
And I have the model Classes:
ItemArray Class
public class ItemArray
{
List<Item> itemArray;
//Get set here
}
Item Class
public class Item
{
String foo;
List<Bar> bar;
List<String> too;
//Get set here
}
Bar Class
public class Bar
{
String id;
//Get set here
}
Heres the question. Is the JSON in the correct format? If so, are the model classes in the correct format?
If not, please shove me in the right direction. Thank you in advance!
PS. I can modify the JSON data format if need be.
According to your json, you should simply have :
public class ItemArray extends List<Item> {
}
if you want to keep you java class and change your json it should be :
{
itemArray: [
{
"foo":"1",
"bar":[ { "_id":"bar1"} ],
"too":["mall", "park"]
}
]
}
Oh, and there is a mismatch with the id and _id for Bar :
public class Bar
{
String _id;
//Get set here
}
You could also use an annotation to change the field's name during Json de/serialization.
And last but not least, consider typing your values better. Don't see any data as strings if they are not, you will not a lot of processing in java code to convert things. For instance :
"foo" : 1,
and see foo as an int data member, not a String.
Some times we get JsonArray [ {..} , {..} ] as a response (without 'itemArray' name like yours)
In that case you can use following code
Type fooType = new TypeToken<ArrayList<Item>>() {}.getType();
List<Item> array = new Gson().fromJson(response, fooType);
find more about this Official doc - Gson Array-Examples
If you have a JsonArray like [ {..} , {..} ] you can do this with Gson:
Item[] items = gson.fromJson(json, Item[].class);
To check Json is valid use this tool http://jsonlint.com/
Class Bar(
private String _id;
//create getter/setters
{
public class Item
{
String foo;
List<Bar> bar;
List<String> too;
//Get set here
}
//this is also fine
public class ItemList
{
List<Item> itemArray;
//Get set here
}
you named of list of items "itemArray", but in your json you have not named the corresponding array of items "itemArray".
So make it itemArray, The problem is not in your json, it is valid. Problem is in its representation for Gson,
Gson map keys of json on the variables of object (i.e Java POJO) with same name.
If the name of your list is Class is
List<Item> itemArray;
then the corresponding json array name should also be itemArray, take a look blow
{
itemArray: [
{
"foo":"1",
"bar":[ { "_id":"bar1"} ],
"too":["mall", "park"]
}
]
}
so you can convert json into object like that
Reader reader = new InputStreamReader(IOUtils.toInputStream(json_string));
ItemList itemList = json.toObject(reader, ItemList.class);
Take a look into blow reference for more details
https://stackoverflow.com/questions/13625206/how-to-parse-the-result-in-java/13625567#13625567

Categories

Resources