Jackson: Deserialize interface without annotations and mix-ins - interface

I am trying to create a way to serialize and deserialize objects to JSON using Jackson without having to rely on annotations, mix-ins or any code that is object-specific (ie: specific deserializers). However, I'm having trouble deserializing interfaces.
An idea that I had was to store the class name of the object within the JSON. For instance, if I have the following classes:
MyClassOne.java
package test.classes;
public class MyClassOne{
private String myString;
private MyClass myReference;
public MyClassOne(String myString, MyClass myReference) {
this.myString = myString;
this.myReference = myReference;
}
public String getMyString() {
return myString;
}
public MyClass getMyReference() {
return myReference;
}
}
MyClassTwo.java
package test.classes;
public class MyClassTwo implements MyClass{
private int myInt;
public MyClassTwo(int myInt) {
this.myInt = myInt;
}
public int getMyInt() {
return myInt;
}
}
I would like to use Jackson to serialize MyObjectOne instances into something similar to:
{
"_class":"test.classes.MyClassOne" ,
"myString":"Hello World",
"myReference":{
"_class":"test.classes.MyClassTwo",
"myInt":2
}
}
I was wondering if that could work and how to accomplish it with Jackson.

An idea that I had was to store the class name of the object within the JSON.
You could consider enableDefaultTypingAsProperty() for automatic inclusion of type information:
ObjectMapper mapper = new ObjectMapper();
mapper.enableDefaultTypingAsProperty(DefaultTyping.NON_FINAL, "_class");
I am trying to create a way to serialize and deserialize objects to JSON using Jackson without having to rely on annotations, mix-ins or any code that is object-specific (ie: specific deserializers).
Consider the following code using the types defined in your own question:
ObjectMapper mapper = new ObjectMapper();
mapper.enableDefaultTypingAsProperty(ObjectMapper.DefaultTyping.NON_FINAL, "_class");
MyClass myClass = new MyClassTwo(2);
MyClassOne myClassOne = new MyClassOne("Hello World", myClass);
String json = mapper.writer()
.withDefaultPrettyPrinter()
.writeValueAsString(myClassOne);
It will produce the following JSON:
{
"_class" : "com.example.examples.foo.MyClassOne",
"myString" : "Hello World",
"myReference" : {
"_class" : "com.example.examples.foo.MyClassTwo",
"myInt" : 2
}
}
To deserialize the above JSON, however, your classes must have a default constructor (or use #JsonCreator).

Related

Serialization object to Json and convert back - Unity [duplicate]

This question already has answers here:
Serialize and Deserialize Json and Json Array in Unity
(9 answers)
Closed 5 years ago.
I have these two classes:
public class MyParent
{
public string a;
public MySub[] b;
}
public class MySub
{
public int sub;
}
I initialize them like this:
myPC = new MyParent();
myPC.a = "test";
myPC.b = new MySub[2];
myPC.b[0] = new MySub();
myPC.b[1] = new MySub();
myPC.b[0].sub = 1;
myPC.b[2].sub = 2;
But when I serialize the object, The result is only {"a":"test"}
string json = JsonUtility.ToJson(myPC);
Debug.Log(json); // {"a":"test"}
How do I do that correctly and convert JSON back to an object?
Internally, JsonUtility uses the Unity Serializer; therefore the object you pass in must be supported by the serializer: it must be a MonoBehaviour, ScriptableObject, or plain class/struct with the Serializable attribute applied.
To ensure a custom class can be serialized, it needs the Serializable attribute
and must follow these rules:
Is not abstract
Is not static
Is not generic, though it may inherit from a generic class
Change your custom class to this:
[System.Serializable]
public class MyParent
{
public string a;
public MySub[] b;
}
[System.Serializable]
public class MySub
{
public int sub;
}
For more in-depth information on Script Serialization, check the official documentation: https://docs.unity3d.com/Manual/script-Serialization.html

How to list field annotations in Kotlin?

I have an annotation
public #interface Field {
String value();
}
and java class, annotated by it:
public class Animal {
#Field("name")
private String name;
}
I try to list all field' annotations by the next code:
for(field in clazz.declaredFields){
for(annotation in field.annotations){
when(annotation){
is Field -> {
//do something
}
}
}
}
where clazz is Class<T>
but field.annotations is empty.
How to list annotations correctly?
Java annotations, by default, are not retained at runtime so you'll need to specify such:
import java.lang.annotation.Retention;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
#Retention(RUNTIME)
public #interface Field {
String value();
}
Kotlin annotations are retained by default:
annotation class Field(val value: String)
The issue isn't Kotlin specific, you just haven't configured Field annotation properly. By default, each annotation is retained with RetentionPolicy.CLASS, meaning it won't be accessible via reflection. You have to use RetentionPolicy.RUNTIME if you want to access the annotation in the runtime.
#Retention(RetentionPolicy.RUNTIME)
public #interface Field {
String value();
}

Polymorphic serialization using Jackson when return type is marker interface

I have a rest service which returns marker interface and this interface have multiple implementations and don't have any common property in the implementations.
#RequestMapping(value = "/users/{userName}", method = RequestMethod.GET)
public User getUser(#PathVariable("userName") String userName) {
return userService.getUser(userName);
}
User and its implementations.Note : User is marker interface.
public interface User {}
public AdminUser implements User { // some properties & its setters & getters }
public SupportUser implements User { // some properties & its setters & getters }
I use Jackson 1.9.1 to serialize and deserialize.
When I hit above service, I am getting below response
{}
When I debug it, I see user implementation object is prepared and sent back to Jackson for serialization.But jackson is sending empty response to browser.Can anyone suggest how to use serialize when return type is marker interface.
Use #JsonTypeInfo and #JsonSubTypes to deserialization polymorphic types, which maintain sub type information while serializing java object and recreate the exact sub type.
Lets take a example, animal is a Interface and it can be an tiger or a lion, and they both extend the Animal Interface . While deserializing we want to create the exact animal type and demonstrate the use of #JsonTypeInfo and #JsonSubTypes annotations.
#JsonTypeInfo(use=JsonTypeInfo.Id.NAME,
include=JsonTypeInfo.As.PROPERTY,
property="name")
#JsonSubTypes({
#JsonSubTypes.Type(value=Lion.class, name="lion"),
#JsonSubTypes.Type(value=Tiger.class, name="tiger"),
})
public interface Animal {
}
#JsonTypeName("lion")
public class Lion implements Animal {
private String name;
private String roar;
//constructor & setters & getters
}
#JsonTypeName("tiger")
public class Tiger implements Animal {
private String name;
private String purr;
//constructor & setters & getters
}
Main Method :
List<Animal> animal = new ArrayList<Animal>();
animal.add(new Lion("lion", "roar"));
animal.add(new Tiger("tiger", "purr"));
animal.add(new Tiger("tiger", "purrrrrrrrr"));
URL url = JacksonPolymorphicSerialization.class.getClassLoader().getResource("animals.json");
File file = new File(url.getPath());
// de-serailization sub types
new ObjectMapper().writeValue(file, animal);
// serailization animals and its subtype
List<Animal> animals = new ObjectMapper().readValue(file, List.class);
System.out.println(animals);
output : [{name=lion, roar=roar}, {name=tiger, purr=purr}, {name=tiger, purr=purrrrrrrr}]
Hope this helps you understanding serializing and deserializing polymorphic types using Jackson.
You can use #JsonTypeInfo annotation which adds a property to share type information between serializing/deserializing.
Read more here: JsonTypeInfo.html

Morphia converter calling other converters

I want to convert Optional<BigDecimal> in morphia. I created BigDecimalConverter, and it works fine. Now I want to create OptionalConverter.
Optional can hold any object type. In my OptionalConverter.encode method I can extract underlying object, and I'd like to pass it to default mongo conversion. So that if there is string, I'll just get string, if there is one of my entities, I'll get encoded entity. How can I do it?
There are two questions:
1. How to call other converters?
2. How to create a converter for a generic class whose type parameters are not statically known?
The first one is possible by creating the MappingMongoConveter and the custom converter together:
#Configuration
public class CustomConfig extends AbstractMongoConfiguration {
#Override
protected String getDatabaseName() {
// ...
}
#Override
#Bean
public Mongo mongo() throws Exception {
// ...
}
#Override
#Bean
public MappingMongoConverter mappingMongoConverter() throws Exception {
MappingMongoConverter mmc = new MappingMongoConverter(
mongoDbFactory(), mongoMappingContext());
mmc.setCustomConversions(new CustomConversions(CustomConverters
.create(mmc)));
return mmc;
}
}
public class FooConverter implements Converter<Foo, DBObject> {
private MappingMongoConverter mmc;
public FooConverter(MappingMongoConverter mmc) {
this.mmc = mmc;
}
public DBObject convert(Foo foo) {
// ...
}
}
public class CustomConverters {
public static List<?> create(MappingMongoConverter mmc) {
List<?> list = new ArrayList<>();
list.add(new FooConverter(mmc));
return list;
}
}
The second one is much more difficult due to type erasure. I've tried to create a converter for Scala's Map but haven't found a way. Unable to get the exact type information for the source Map when writing, or for the target Map when reading.
For very simple cases, e.g. if you don't need to handle all possible parameter types, and there is no ambiguity while reading, it may be possible though.

GWT RPC serializing

I am trying to send over MyClass through RPC, but am getting :
Type MyClass was not assignable to 'com.google.gwt.user.client.rpc.IsSerializable' and did not have a custom field serializer.For security purposes, this type will not be serialized.
I have looked at GWT - occasional com.google.gwt.user.client.rpc.SerializationException and tried their solution, but it did not work.
The difference is that MyClass is located in another project.
The project structure is:
MyApiProject
-contains MyClass
MyClientProject
MyServerProject
I have also tried passing an enum through the RPC from MyApiProject, which also failed.
public class MyClass
implements Serializable
{
private static final long serialVersionUID = 5258129039653904120L;
private String str;
private MyClass()
{
}
public MyClass(String str)
{
this.str = str;
}
public String getString()
{
return this.str;
}
}
in the RemoteService I have:
mypackage.MyClass getMyClass();
in the RemoteServiceAsync I have:
void getMyClass(AsyncCallback<mypackage.MyClass> callback);
I had to change implements Serializable to implements IsSerializable
This usually pops up when you are using another type inside of your class that is not serializable. Check the properties of your class and make sure they are all serializable, post the code of MyClass here and I can look at it as well.
I believe GWT requires an RPC serializable class to also have a public no-argument constructor.
Try removing
private MyClass()
{
}
or set it to
public MyClass()
{
}