How to decode generic data in Jackson? - java

How can I do like this:
Test<String> data = OBJECT_MAPPER.decodeValue("sss", Test<String>.class);
When I call this operation I get an error. I need decode generic class.
Thanks for the help.

You can use TypeReference. Test<String>.class is not possible in Java.
TypeReference testStringType = new TypeReference<Test<String>>() { };
Object value = mapper.readValue(json, testStringType);
Also works:
JavaType javaType = mapper.getTypeFactory().constructParametricType(Test.class, String.class);
Test<String> value1 = mapper.readValue(json, javaType);
See also:
Jackson - Deserialize using generic class
Jackson - Deserialize Generic class variable

Related

class com.google.gson.internal.LinkedTreeMap cannot be cast to class [duplicate]

I have a domain object Foo, and I want to parse some JSON such as
[
{"prop": "val"},
{"prop": "val2"},
]
I want to get a List<Foo>. Something like this
List<Foo> foos = new Gson().fromJson(json, /*what goes here ?*/);
You need to use a TypeToken to correctly express the type. Class is not sufficient in this case, because of the interaction with the generic type.
Type listType = new TypeToken<List<Foo>>(){}.getType();
List<Foo> projects = (List<Foo>) gson.fromJson(response, listType);

TypeToken usage mandatory?

Is is mandatory to use TypeToken (as recommended in the Gson doc) as type when converting a list into json like below -
new Gson().toJson(dateRange, new TypeToken<List<String>>() {}.getType());
For me below code is also working -
new Gson().toJson(dateRange, List.class);
Just want to make sure that code doesn't break.
As per docs -
If the object that your are serializing/deserializing is a
ParameterizedType (i.e. contains at least one type parameter and may
be an array) then you must use the toJson(Object, Type) or
fromJson(String, Type) method. Here is an example for serializing and
deserializing a ParameterizedType:
Type listType = new TypeToken<List<String>>() {}.getType();
List<String> target = new LinkedList<String>();
target.add("blah");
Gson gson = new Gson();
String json = gson.toJson(target, listType);
List<String> target2 = gson.fromJson(json, listType);
This is the special case, in other cases you can use class type directly.
For reference - http://google.github.io/gson/apidocs/com/google/gson/Gson.html
Hope this helps

Jackson Unmarshall custom object instead of LinkedHashMap

I have a Java object Results:
public class MetaData {
private List<AttributeValue<String,Object>> properties
private String name
...
... getters/setters ...
}
The AttributeValue class is a generic key-value class. It's possible different AttributeValue's are nested. The (value) Object will then be another AttributeValue and so forth.
Due to legacy reasons the structure of this object cannot be altered.
I have my JSON, which I try to map to this object.
All goes well for the regular properties. Also the first level of the list is filled with AttributeValues.
The problem is the Object. Jackson doesn't know how to interpret this nested behavior and just makes it a LinkedHashMap.
I'm looking for a way to implement custom behavior to tell Jackson this has to be a AttributeValue-object instead of the LinkedHashMap.
This is how I'm currently converting the JSON:
ObjectMapper om = new ObjectMapper();
MetaData metaData = om.readValue(jsonString, new TypeReference<MetaData>(){});
And this is example JSON. (this is obtained by serializing an existing MetaData object to JSON, I have complete control over this syntax).
{
"properties":[
{
"attribute":"creators",
"value":[
{
"attribute":"creator",
"value":"user1"
},{
"attribute":"creator",
"value":"user2"
}
]
},{
"attribute":"type",
"value": "question"
}
],
"name":"example"
}
(btw: I've tried the same using GSON, but then the object is a StringMap and the problem is the same. Solutions using GSON are also welcome).
edit In Using Jackson ObjectMapper with Generics to POJO instead of LinkedHashMap there is a comment from StaxMan:
"LinkedHashMap is only returned when type information is missing (or if Object.class is defined as type)."
The latter seems to be the issue here. Is there a way I can override this?
If you have control over the serialization, try calling enableDefaultTyping() on your mapper.
Consider this example:
Pair<Integer, Pair<Integer, Integer>> pair = new Pair<>(1, new Pair<>(1, 1));
ObjectMapper mapper = new ObjectMapper();
String str = mapper.writeValueAsString(pair);
Pair result = mapper.readValue(str, Pair.class);
Without enableDefaultTyping(), I would have str = {"k":1,"v":{"k":1,"v":1}} which would deserialize to a Pair with LinkedHashMap.
But if I enableDefaultTyping() on mapper, then str = {"k":1,"v":["Pair",{"k":1,"v":1}]} which then perfectly deserializes to Pair<Integer, Pair<...>>.

I can't return List<bean> from the Json? Java.lang.ClassCastException

There is json in my metod. I want json change to object bean. compiler warns and asks to remove generic.
This example does not work:
Gson gson = new Gson();
List<MyBean> myBean = gson.fromJson(
result.getBody(), List<myBean>.class);
So does run, but I can not get to the bean:
Gson gson = new Gson();
List<MyBean> myBean = gson.fromJson(
result.getBody(), List.class);
MyBean.get(0).getFirstName();
error java.lang.ClassCastException: com.google.gson.internal.StringMap cannot be cast to com.home.bean.MyBean
How do I solve this problem?
Try this:
Type myBeanListType = new TypeToken<ArrayList<MyBean>>() {}.getType();
List<MyBean> mappedList = new Gson().fromJson(result.getBody(), myBeanListType );
Your code would have to be changed to:
List<MyBean> myBean = gson.fromJson(result.getBody(), new TypeToken<List<MyBean>>() {}.getType());
Per the Gson user guide here, (under Serializing and Deserializing Generic Types section):
When you call toJson(obj), Gson calls obj.getClass() to get
information on the fields to serialize. Similarly, you can typically
pass MyClass.class object in the fromJson(json, MyClass.class) method.
This works fine if the object is a non-generic type. However, if the
object is of a generic type, then the Generic type information is lost
because of Java Type Erasure.
You can solve this problem by specifying the correct parameterized
type for your generic type. You can do this by using the TypeToken
class.
Hence, you would have to use TypeToken when dealing with generics.

Get element name with GSON

I have JSON like this:
{"foos":[{"id":1}, {"id":2}]}
I can turn it into List<Foo> pretty simply with GSON, like this:
Type t = new TypeToken<List<Foo>>(){}.getType();
JsonObject resp = new Gson().fromJson(
new JsonParser().parse(json).getAsJsonObject().get("foos",t);
But let's assume that I also have another JSON, where the name of the array and type changes
{"bars":[{"id":3},{"id":9}]}
Of course I could just swap the "foos" parameter for "bars", but if it's possible, I'd like my software to do it for me.
Is there a way to extract the name of the array child with the GSON library?
I'm not sure if I understood what you want correctly, but aren't you referring to the use of generics? I mean you could write a method that returns you a List of your relevant class? Something along the lines of
Type type = new TypeToken<List<MyClass>>() {}.getType();
List<MyClass> myObjects = getMyObjects(new JsonParser().parse(json).getAsJsonObject().get("foos"), type);
public static List<T> getMyObjects(String jsonString, Type type) {
Gson gson = new Gson();
List<T> myList = gson.fromJson(jsonString, type);
return myList;
}
Looking at your JSON examples, I assume that the name of the list element can change, but not the content of the list. If this is correct, you could parse your JSON response just like this:
Type mapType = new TypeToken<Map<String, List<Foo>>>() {}.getType();
Map<String, List<Foo>> map = gson.fromJson(jsonString, mapType);
And then you can access the name of the list with:
String listName = map.keySet().iterator().next();
If the content of the list could also change, things get a bit more complicated...

Categories