I am using jackson-datatype-guava along with Jackson to handle serialization of Guava objects. It all works fine with other objects but facing issue with Optional.
I have a serializer deserializer interface from a framework with following methods which I am forced to use.
String toJson(Object object);
<T> T fromJson(String json, Class<T> valueType);
That means my Jackson implementation of this interface which uses ObjectMapper also can only use following method on mapper.
mapper.readValue(json, valueType);
Which is not working in case of 'Optional'.
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new GuavaModule());
mapper.readValue(mapper.writeValueAsString(Optional.of("string")), Optional.class);
This gives error saying Null JavaType passed. If I use TypeReferece<Optional<String>> in mapper, it works fine but is there a way to make it work with above interface?
//works fine but can't use this method as interface only accepts `Class` param.
mapper.readValue(mapper.writeValueAsString(Optional.of("string")), new TypeReference<Optional<String>>(){});
Update
I tried using 2.4.* versions of these library as suggested in comments and it's working fine for Optional<String> but does not still work for Optional<MyClass> where MyClass is simple pojo. Seems while deserializing, it thinks, json of MyClass is a map and converts it into Optional<Map>.
public class MyClass {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Main Program:
MyClass obj = new MyClass();
obj.setName("PAC");
Optional<MyClass> obj1 = mapper.readValue(mapper.writeValueAsString(Optional.of(obj)), Optional.class);
System.out.println(obj1); //prints as Map
System.out.println(obj1.get().getName()); //Fails doing casting to MyClass
Output:
Exception in thread "main" Optional.of({name=PAC})
java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to MyClass
at OptionalTest.main(OptionalTest.java:22)
I also tried enabling default typing in mapper by mapper.enableDefaultTyping() but without any luck.
Error at mapper.readValue... line after enabling default typing for DefaultTyping.NON_FINAL
Invalid type id 'MyClass' (for id type 'Id.class'): Class MyClass is not assignable to com.google.common.base.Optional
Any further suggestions on this?
The interface you are forced to use is poor, it needs to support java.lang.reflect.Type in order to allow you to pass generic types. Then you can use jacksons TypeReference (or TypeToken from guava) to obtain an instance of a Optional type.
Related
this is my class
public class Response<T> {
private final T data;
private final String error;
I can only change a class. Can jackson serialise it without configuring objectMapper?
EDIT:
I'm using this object as a method result parameter in spring mvc #RestController. And jackson's objectMapper.canSerialize(Response.class) returns false.
EDIT:
I've fixed it this way:
public class Response {
private T data;
private String error;
#JsonIgnore
#JsonTypeInfo(
use = JsonTypeInfo.Id.CLASS,
include = JsonTypeInfo.As.PROPERTY
)
private Class<T> type;
...
#JsonIgnore
public Class<T> getType() {
return type;
}
While serializing, I think there will not be an issue as T data is going to hold the object of some real object and libraries like Gson will be able to serialize them properly, but there can be an issue with deserialization due to the fact that it doesn't know in which type it has to be deserialized. For that, you may be required the pass the specific Type so that to tell the library in which type you are trying to deserialize.(Note: I have worked with mostly Gson hence answers are influenced by that)
Refer : https://stackoverflow.com/a/7299718/1093333
I need to write a custom deserializer for a class with generics. I couldn't find a way to do this, however I cannot imagine I'm the only one with this problem. As far as I've thought through it, there would be two ways to implement this, yet none of them are implementable:
Providing a Class parameter to the deserializer in the constructor of the deserializer doesn't work, because when registering the deserializer, the relationship between Type.class and the instance of the deserializer with the passed Class instance is lost.
For example:
public class Foo<T> {}
public class FooDeserializer<T> {
public FooDeserializer(Class<T> type) { ... }
...
}
// Boilerplate code...
module.addDeserializer(Foo.class, new FooDeserializer<Bar1>(Bar1.class));
module.addDeserializer(Foo.class, new FooDeserializer<Bar2>(Bar2.class));
This doesn't work, when an ObjectMapper instance gets an instance of Foo, there's no type information of the generic parameter available (type erasure) so it simply chooses the last deserializer registered.
It doesn't help to keep a reference of the generic type in the class because an instantiated version of the class cannot be passed to the deserializer (the interface is readValue(String, Class)).
For example:
String json = "...";
ObjectMapper mapper = ...;
Foo<Bar1> foo = new Foo<>(Bar1.class);
foo = mapper.readValue(json, Foo.class); // Can't pass empty foo instance with Class<?> field containing Bar1.class
Something like this would be needed:
mapper.readValue(json, Foo.class, Bar1.class); // Doesn't exist in jackson
Any suggestions how to do this?
EDIT:
I found a way to solve the problem, however it's not a clean solution:
I extend the FooDeserializer with a Class field to save the type of Foo's generic parameter. Then, every time I want to deserialize some json into a new Foo instance, I get a new ObjectMapper instance (I use ObjectMapper#copy on a preconfigured instance from a factory) and pass it a new Module which contains an instance of the FooDeserializer with the class parameter (I know the type at this time). Module, FooDeserializer and the ObjectMapper copy are short living, they only get instantiated for this single deserialization action. As I said, not very clean, but still better than subclassing Foo many times and writing a deserializer for each.
Example:
public class FooDeserializer<T> extends StdDeserializer<T> {
private Class<T> type;
public FooDeserializer(Class<T> type) { this.type = type }
...
}
// Meanwhile, before deserialization:
ObjectMapper mapper = MyObjectMapperFactory.get().copy();
SimpleModule module = new SimpleModule(new Version(0,0,1,null,null,null);
module.addDeserializer(Foo.class, new FooDeserializer(Bar1.class);
mapper.addModule(module);
Foo<Bar1> x = mapper.readValue(json, Foo.class);
Probably putting this into a utility method to hide the uglyness.
I don't think you need to write our own custom deserializer. You can use this syntax to deserialize objects that use generics, taken from another Stack Overflow thread.
mapper.readValue(jsonString, new TypeReference<Data<String>>() {});
Couple sources to help you:
Jackson - Deserialize using generic class
http://www.tutorialspoint.com/jackson/jackson_data_binding_generics.htm
You don't need to write a custom deserializer. A generic can be deserialized by putting Jackson annotations on the type that is passed as parameter to the generic class.
Annotate class Bar1 as follows:
#JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.WRAPPER_OBJECT)
#JsonTypeName("Bar1")
class Bar1 {
}
Now when you deserialize an instance of Foo<Bar1>, Jackson will put type parameter info into JSON. This JSON can then be deserialized into generic class Foo.class just as you would deserialize any other class.
ObjectMapper mapper = ...;
Foo<Bar1> foo = new Foo<Bar1>();
String json = objectMapper.writeValueAsString(foo);
foo = mapper.readValue(json, Foo.class); // no need to specify the type Bar1.
So if every class that can be passed as parameter to Foo has annotations on it, then JSON can be deserialized without knowing the type parameter at compile time.
Please refer Jackson documentation on Polymorphic Deserialization and Annotations.
public void serialize(IPerson person, OutputStream output) throws Exception {}
public void deserialize(InputStream input) throws Exception {}
I have an interface named IPerson, it has basic functionality.
I want to serialize the person object and be able to deserialize it from the deserialize method.
However, the scenario is this I cannot use Java's serializable interface as I can't be sure what implementation of IPerson will be used.
I have chosen to use Jackson's FasterXML, using ObjectMapper m = new ObjectMapper();
The problem I am having is that since IPerson is an interface I cannot serialize it directly using mapper.writerValue(output, person), I figured I must convert this object into something else, say a ByteArray then serialize it?
Also, this would be converting this something else into an object when deserializing? I have minimal experience with what exactly I should convert this object to and how to do so? Any ideas?
When using the default ObjectMapper you will have to make sure the objects you serialize are Java Beans. For non-bean classes you can set field visibility using m.setVisibility(PropertyAccessor.FIELD, Visibility.ANY); or annotate your class using #JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY).
For deserializing you will have to tell the ObjectMapper the target type. This can be done by providing a concrete implementation type to readValue or by storing the classname within the exported JSON. For this you can set m.enableDefaultTypingAsProperty(DefaultTyping.OBJECT_AND_NON_CONCRETE, "__class"); and annotate your objects with #JsonTypeInfo
ObjectMapper om = new ObjectMapper();
om.enableDefaultTypingAsProperty(DefaultTyping.OBJECT_AND_NON_CONCRETE, "__class");
IPerson value = new MyPerson();
String s = om.writeValueAsString(value);
IPerson d = om.readValue(s, IPerson.class);
using
interface IPerson {
void doSomething();
}
#JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, property = "__class")
#JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY)
class MyPerson implements IPerson {
String name;
#Override
public void doSomething() {
}
}
Note that, you will need a default constructor for this to work or work with #JsonCreator and #JsonProperty (see jackson-annotations for details)
I have been searching all day for something that answers this, but I have not had a lot of luck thus far.
My question is straightforward: how do I deserialize an anonymous object correctly using Jackson.
private interface Interface1
{
int getValue();
}
public static void testAnonymousObject() throws IOException
{
ObjectMapper mapper = new ObjectMapper();
mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
mapper.setVisibility(PropertyAccessor.ALL, Visibility.NONE);
mapper.setVisibility(PropertyAccessor.FIELD, Visibility.ANY);
mapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
Interface1 testObject = new Interface1()
{
private final int value = 5;
#Override
public int getValue()
{
return value;
}
};
String json = mapper.writeValueAsString(testObject);
System.out.println("JSON = " + json);
Interface1 received = (Interface1) mapper.readValue(json, Object.class);
System.out.println(received);
}
The output of this is: JSON = ["com.foo.test.JacksonTest$1",{"value":5}] before I get an exception:
Exception in thread "main" com.fasterxml.jackson.databind.JsonMappingException: Can not deserialize Class com.foo.test.JacksonTest$1 (of type local/anonymous) as a Bean.
EDIT Just to clarify, both Jackson and XStream are able to serialize the object. But only XStream seems to be able to deserialize the object back. So this scenario can be made to work.
As of the time I am writing this, it seems that Jackson does not serialize inner classes or anonymous classes correctly. Other packages such as XStream and Kryo, do however.
Because inner classes do not have a default zero argument constructor (they have a hidden reference to the outer/parent class) Jackson cannot instantiate them.
you can check this link
Problem is not just about it being an inner class (which may or may not be problematic, depending on whether implementation is static or non-static), but also in that no type information is included -- all Jackson sees is type Interface1. To enable reading it back it is necessary to either include type information ("polymorphic type handling"), or to specify mapping between abstract type and implementation class.
Given that you are using an anonymous inner class, you would be able to support this usage by enabled so-called "default typing" (see ObjectMapper javadocs for enableDefaultTyping() or such).
But you may also need to implement specific strategy, if you do not want to enable type inclusion for all non-final types.
To see whether type id is included you can enable default typing with one of default options and have a look at JSON being produced: there should be an additional type id ("#class" property when class name is used as id).
A ready-to-use code-snippet for a generic JSON-deserialization to a Java POJO with Jackson using nested classes:
static class MyJSON {
private Map<String, Object> content = new HashMap<>();
#JsonAnySetter
public void setContent(String key, Object value) {
content.put(key, value);
}
}
String json = "{\"City\":\"Prague\"}";
try {
MyPOJO myPOJO = objectMapper.readValue(json, MyPOJO.class);
String jsonAttVal = myPOJO.content.get("City").toString();
System.out.println(jsonAttVal);
} catch (IOException e) {
e.printStackTrace();
}
#JsonAnySetter ensures a generic JSON-parsing and population.
I encountered an deserialization issue when using both enableDefaultTyping and providing a generic TypeRefernce. It seems that Jackson is unable to decide which type information is more important. This Test case is demonstarting the problem:
#Test
public void roundTripTest() throws JsonGenerationException,
JsonMappingException, IOException {
// 0 Value Test
Integer[] integers = new Integer[] {};
Wrap<Integer[]> beforeResult = new Wrap<Integer[]>(integers);
File file = new File("/tmp/jsonTest");
mapper.writeValue(file, beforeResult);
TypeReference<Wrap<Integer[]>> typeRef = new TypeReference<JacksonMapperTest.Wrap<Integer[]>>() {
};
Wrap<Integer[]> afterResult = mapper.readValue(file, typeRef);
assertNotNull(afterResult);
}
public static class Wrap<T> {
private T wrapped;
public Wrap() {
}
public Wrap(T wrapped) {
this.wrapped = wrapped;
}
public T getWrapped() {
return wrapped;
}
public void setWrapped(T wrapped) {
this.wrapped = wrapped;
}
}
where mapper is :
mapper = new ObjectMapper();
mapper.enableDefaultTyping();
and the exception is:
org.codehaus.jackson.map.JsonMappingException: No suitable constructor found for type [simple type, class JacksonMapperTest$Wrap<[Ljava.lang.Integer;>]: can not instantiate from JSON object (need to add/enable type information?)
pretty weird, eh? by using beforeResult.getClass instead of TypeRefernce it is possible to omit the issue, but it is still not the prefered behavior.
Did I miss any kind of options in order to solve this?
I was using Jackson 1.9.3
[EDIT]
Using Maps Instead of an Array as Wrapped Objects works as expected!
This seems to be a bug in jackson...however its author is probably more competent to tell if it is a bug or not... if you are free to change from jackson take a look at genson http://code.google.com/p/genson/.
Gensons equivalent of enableDefaultTyping is setUseRuntimeTypeForSerialization (not exactly the same but it is very similar in most cases). It uses the runtime type during serialization. Here is an example:
Genson genson = new Genson.Builder().setUseRuntimeTypeForSerialization(true).create();
String json = genson.serialize(beforeResult);
System.out.println(json);
GenericType<Wrap<Integer[]>> type = new GenericType<Wrap<Integer[]>>() {};
Wrap<Integer[]> afterResult = genson.deserialize(json, type);
EDIT
If you need to be able to deserialize to polymoprhic or unknown types with genson use setWithClassMetadata(true) of Genson.Builder. This feature actually is used only for json objects (no matter if it is an abstract class or not).
I am not 100% sure of exact root cause, but I suspect this has to do with Java type erasure, and problems from passing generics-aware information and mixing it with default type information. Type info is actually only based on non-generic types; however, it does work for specific kinds of generic types (Maps, Collections).
But in this case you have custom generic type; and I think this is what makes Jackson unable to indicate generic type of property for purposes of dynamic typing.
The usual workaround is to try sub-classing if possible; but fortunately you were able to find a better workaround.