Create a factory JSON deserializers in Jackson - json

I have a bunch of interfaces annotated as follows:
#MyAnnotation(DefaultImplementation.class)
interface SomeInterface {
...
}
and an accompanying implementation
class DefaultImplementation implements SomeInterface {
...
}
When my ObjectMapper runs into a field of type SomeInterface I would like it to deserialize it as if it was a DefaultImplementation. That is, I would like encode the following logic in the deserialization process:
if (staticTypeToDeserialize.isAnnotationPresent(MyAnnotation.class))
deserializeAs(type.getAnnotation(MyAnnotation.class).value());
else
fallBackOnRegularDeserialization();

I hope I have a complete answer for you. Jackson can do this through a MixIn. I'm no expert on that, but I did it recently so ... In essence, you stick type information into the JSON, then Jackson can use that type information to deserialize it the way you want.
So first, you need a mixin class that knows about the types you are going to serialize and deserialize.
#JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.PROPERTY,
property = "type")
#JsonSubTypes({
#JsonSubTypes.Type(value = MyString.class, name = "mystring"),
#JsonSubTypes.Type(value = MyEvent.class, name = "myevent") })
public abstract class PolymorphicMixIn {
}
The MyString and MyEvent class derive from the same base class in my case. So everything I stick in and out of Jackson is that one class type. Say that baseclass is MyBaseClass.
So then in your jackson class, you need to tell the object mapper about this.
private ObjectMapper objectMapper = new ObjectMapper();
objectMapper.addMixInAnnotations(MyBaseClass.class, PolymorphicMixIn.class);
That's pretty much it. Now you can just call objectMapper.writeValueAsString and it'll write the Jackson and stick in type information.
To read it out, just pass in the class type you are expecting:
MyString outputString = objectMapper.readValue(argAsString, MyString.class);
I hope that's enough to get you where you need to be!

Related

JACKSON serialization of objects

I am new to JACKSON serialization, and writing Test cases for model classes.
So when i serialise an another object initialized in this model class following anomaly is seen::
Example::
class ToTest{
ABC abc;
//getter setter
}
class Test{
//everything that is needed
#Test
public void serialize() throws Exception{
ToTest toTest = new ToTest();
ABC abc = new ABC();
toTest.setABC(abc);
}
Now when I serilize this toTest object: the json string is missing the "ABC" class name. So i am not able to equalise them. Please help.
Jackson does not explicitly write out the class names when doing serialization do JSON. This is by design as the POJO objects used in serialization are intended for describing the contents of the JSON data, not necessarily preserving the class.
With that said, there are a few things you can do. If you want to preserve the original class, you can use annotations to add a class field, which might solve your issue. A quick search resulted in this as an example:
include class name in all objects serialized by jackson

does play framework not support class to json or json to class mapping?

I am newbie to play framework.
I had seen chapter 'JSON' in play framework documentation
It guide me to use case class, not normal class
so It seemed not to support json to class mapping
is it true?
"sorry for poor expression skill, I'm not a native"
What is exactly your problem ? Here is how it works in Java, it must be very close in Scala.
Let's say you have a class MyClass and and instance myObject of this class.
If you want to serialize :
JsonNode json = play.libs.Json.toJson(myObject);
And if you want to deserialize :
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
MyClass myObject = mapper.readValue(jsonNode.toString(), MyClass.class);
Obviously, you'll need to handle JsonParseException, JsonMappingException... to send a human readable message to your end user.

Json Ignore Property on JsonSubTypes

I have a Scala case class that represents my JSON as below:
class MyJSON(x: Int, typeA: TypeA, typeB: TypeB)
#JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type")
#JsonSubTypes(Array(
new Type(value = classOf[ClassA], name = "ClassA"),
new Type(value = classOf[ClassB], name = "ClassB")))
trait TypeA {
...
...
}
In my ClassA, I have certain fields that are deserialized from the JSON. But I also want that if there are certain fields that are not part of my Class objects, I want them to be ignored. What I did was I used the #JsonIgnoreProperties(ignoreUnknown=true) annotation on the MyJSON class as below:
#JsonIgnoreProperties(ignoreUnknown=true)
class MyJSON(x: Int, typeA: TypeA, typeB: TypeB)
It failed when my input JSON had some unknown fields. But when I moved this annotation to one of my Class (say ClassA) in my case, it was ignored. The problem is that I do not want to add this ignore properties annotation to all my classes, but rather I add it just to the top and want that propagated to all the types.
Try this
objectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
I found a much elegant solution to the problem. I used Jackson MixIn mechanism to actually add the additional annotations to my target types during runtime. Here is what I did:
#JsonIgnoreProperties({ Array("field1", "field2") })
abstract class MixInAnnotations{}
In my JSONMarshaller class where I create the ObjectMapper, I do the following:
val mapper = new ObjectMapper()
mapper.registerModule(new MyApplicationModule)
The MyApplicationModule now looks like:
public class MyApplicationModule extends com.fasterxml.jackson.databind.module.SimpleModule {
public MyApplicationModule() {
super(MyApplicationModule.class.getSimpleName());
// Add the corresponding MixIn Annotations to ignore the JSON properties
setMixInAnnotation(MyTargetClass1.class, MixInAnnotations.class);
setMixInAnnotation(MyTargetTrait.class, MixInAnnotations.class);
}
}
I call the setter methods to add the MixInAnnotations to my target classes. The target classes here could also be a trait that has the JsonSubTypes Annotation. So effectively, I'm not polluting all my types with the #JsonIgnoreProperties annotation.

force jackson mapper to always add class type on writeValue without annotations

Is it possible to configure jackson to always add the type of the serialized object to the generated json output.
For example:
package org.acme;
class ClassA
{
String a;
String b;
}
and I want the generated json to be:
["org.acme.ClassA",{"a":"str1","b":"str2"}]
You can do that with enableDefaultTyping() of the ObjectMapper
e.g.
mapper.enableDefaultTyping(DefaultTyping.OBJECT_AND_NON_CONCRETE);
See ObjectMapper API
If your are free to change from Jackson and do not especially need the format to match the one your are showing you can try Genson http://code.google.com/p/genson.
For example if your requirement is to be able to deserialize interfaces or abstract classes based on the original type of the object you serialized you can do:
interface Entity {}
static class Person implements Entity {}
Genson genson = new Genson.Builder().setWithClassMetadata(true).create();
// json will be equal to {"#class":"my.package.Person"}
String json = genson.serialize(new Person());
// and now Genson is able to deserialize it back to Person using the information
// in the Json Object
Person person = (Person) genson.deserialize(json, Entity.class);
Another nice feature is the ability to define aliases for your classes, so you show less information in the json stream but also this allows you to do refactoring without worring of existing json streams (for example if you store it in a database).
Genson genson = new Genson.Builder().addAlias("person", Person.class).create();
// json value is {"#class": "person"}
String json = genson.serialize(new Person());
Have a look at the wiki.

How to customize Jackson type information mechanism

In Jackson, I am using annotation #JsonTypeInfo to include polymorphism support.
If, I do not want to go with annotation based approach, I can use global default typing or override the type information handling module.
I have tried global type information but it is emitting type information for all non final type.
What I need ,
I want to include type information only for polymorphic type.
I want to change default format of type info (to key-value pair)
Is it possible to achieve above two points just by twitting global configuration?
If not, what extension point should I used used to customize type-information module ?
I have read JacksonAnnotationIntrospector is the class which deals with type info.
Should I customize it to achieve above mentioned two points?
Help with Example will be well and good.
You can use Jackson's DefaultTypeResolverBuilder for this purpose. Extend this class and override the useForType method appropriately. Here is an example that adds type information only for the classes belonging to the test.jackson package (and sub-packages):
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper.DefaultTypeResolverBuilder;
import com.fasterxml.jackson.databind.ObjectMapper.DefaultTyping;
public class CustomTypeResolverBuilder extends DefaultTypeResolverBuilder
{
public CustomTypeResolverBuilder()
{
super(DefaultTyping.NON_FINAL);
}
#Override
public boolean useForType(JavaType t)
{
if (t.getRawClass().getName().startsWith("test.jackson")) {
return true;
}
return false;
}
}
Now, consider that you have Foo.java in test.jackson package and Bar.java in org.myorg package, each containing an int variable called "integer" and a String variable called "string".
You can serialize objects of these two classes this way:
ObjectMapper objectMapper = new ObjectMapper();
TypeResolverBuilder<?> typeResolver = new CustomTypeResolverBuilder();
typeResolver.init(JsonTypeInfo.Id.CLASS, null);
typeResolver.inclusion(JsonTypeInfo.As.PROPERTY);
typeResolver.typeProperty("#CLASS");
objectMapper.setDefaultTyping(typeResolver);
Foo foo = new Foo(10, "Foo");
Bar bar = new Bar(20, "Bar");
System.out.println(objectMapper.writeValueAsString(foo));
System.out.println(objectMapper.writeValueAsString(bar));
The corresponding output will be:
{"#CLASS":"test.jackson.Foo","integer":10,"string":"Foo"}
{"integer":20,"string":"Bar"}
You can also customize the name of the attribute that represents the type ("#CLASS" in the above example). Hope this helps!
You can use the Moonwlker library.
With it, you can create an ObjectMapper like this:
ObjectMapper objectMapper = new ObjectMapper();
MoonwlkerModule module =
MoonwlkerModule.builder()
.fromProperty("#CLASS").toSubclassesOf(Animal.class)
.build();
objectMapper.registerModule(module);
And then use that mapper to (de)serialize. The Moonwlker website contains more details and configuration options.