Jackson Serialization/Deserialization Polymorphism many-to-one using existing property - jackson

I have a class XDevice which subclasses the Device class.
I have JsonTypeName annotations of both of these classes which i use for serialization/deserialization using an existing property 'type' in Device class.
Below are the annotations on the class.
#XmlRootElement
#JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXISTING_PROPERTY,
property = "type", visible = true)
#JsonSubTypes({
#JsonSubTypes.Type(value = XDevice.class, name = "XDev"),
})
#JsonTypeName("Dev")
public class Device
The child class XDevice just contains one annotation #JsonTypeName("XDev").
The type field is an enumeration which supports many other type values for future needs.
Now there is a new type value possible "Dev1" which needs to be serialized/deserialized to the Device object also.
I am not sure how i can do this , since this type value is not a subclass and i already have a single JsonTypeName annotation on the Device class.
Any help would be appreciated.

Related

Why don't I need to write property at object class in sealed class by Kotlin?

I'm developing the app by using Kotlin.
sealed class DestinationScreen(val route:String){
object Signup: DestinationScreen(route = "signup")
}
#Composable
fun InstagramApp(){
val navController = rememberNavController()
NavHost(navController = navController, startDestination = DestinationScreen.Signup.route){
composable(DestinationScreen.Signup.route){
SignupScreen(navController = navController)
}
}
}
I don't know Why Signup singleton class can have the property "route" using argv?
I understand it inherits DestinationScreen. So it also has route property.
But Destination class doesn't have concrete the property route.
If Destination class is data class, make sense it doesn't need to declare the property.
No need for{}. And data class has the property not declareing it by using argv.
So I mean DestinationScreen should has concrete property route, if Signup inherit different property's value, it should override.
Why can this codes above work? Does this feature have seal class or object class?
Please teach me. Thank you.
But Destination class doesn't have concrete the property route
Yes, it does. The route property is declared right there in its constructor.
if Signup inherit different property's value, it should override
Not sure what you mean by this, but Signup doesn't need to override the property. It already inherits the property. By passing a value to the super-class's constructor, the existing property gets an initial value as passed by the sub-class without overriding it.
You mention sealed and data class types, but they are irrelevant to this discussion. Inheritance works the same way with sealed and non-sealed classes.
Any time a class extends another class, it also is a type of that class and inherits all of its properties and functions, no overriding needed.

Adding a property using Jackson MixIn's?

I know we can use Jackson MixIn's to rename a property or to ignore a property (see examples here). But is it possible to add a property?
The added property can be:
A constant (such as a version number)
A computed value (e.g. if the source class has properties for getWidth() and getHeight(), but we want to ignore both and export a getArea() instead)
Flattened information from nested members (e.g. a class has a member Information which in turn has a member Description, and we want to have a new property for description and skipping the nesting structure of Information)
From documentation:
"Mix-in" annotations are a way to associate annotations with classes,
without modifying (target) classes themselves, originally intended to
help support 3rd party datatypes where user can not modify sources to
add annotations.
With mix-ins you can:
1. Define that annotations of a '''mix-in class''' (or interface)
2. will be used with a '''target class''' (or interface) such that it
appears
3. as if the ''target class'' had all annotations that the ''mix-in''
class has (for purposes of configuring serialization /
deserialization)
To solve your problems you can:
Create new POJO which has all required fields.
Implement custom serialiser.
Before serialisation convert POJO to Map and add/remove nodes.
Use com.fasterxml.jackson.databind.ser.BeanSerializerModifier to extend custom serialisers. See: Jackson custom serialization and deserialization.
For example, to add a constant version to each object you can wrap it in Verisoned class:
class Versioned {
private final String version;
#JsonUnwrapped
private final Object pojo;
public Versioned(String version, Object pojo) {
this.version = version;
this.pojo = pojo;
}
public String getVersion() {
return version;
}
public Object getPojo() {
return pojo;
}
}
Now, if you wrap an Arae(width, height) object:
Area area = new Area(11, 12);
String json = mapper.writeValueAsString(new Versioned("1.1", area));
output will be:
{
"version" : "1.1",
"width" : 11,
"height" : 12
}

EMF-JSON: Include properties of EObject Subclass

I am using EMF-JSON for serializing an EMF model instance. The problem is, that a subclass of EObject gets treated as such and not as the subclass. Thus, properties in the subclass get lost during serialization, as the EObjectSerializer is selected. How can I make sure that the properties of the subclass are included without changing the class itself (referring to the inclusion annotations)? In the following you find the generated interfaces. In this case, the ref property of the ArithVar class is not serialized.
public interface ArithExpr extends EObject {}
public interface ArithVar extends ArithExpr {
VarType getRef();
void setRef(VarType value);
}
public interface VarType extends EObject {}
Any ideas?
From the documentation:
References are by default serialized as JSON objects that contain two fields. The first field is the type of the referenced object and the second field is the URI of the referenced object. The type field is named eClass and the URI field is named $ref.
So my guess is it should work by default. Have you tried debugging through EObjectSerializer ? My guess is it probably uses the EMF reflective API to iterate through all the features of an EObject and serialize them.

Why does Json.NET not include $type for the root object when TypeNameHandling is Auto?

When I set Json.NET to serialize with TypeNameHandling set to TypeNameHandling.Auto, it correctly sets $type for child properties of an object but does not do so for the root object being serialized. Why?
Please consider the following repro:
public class Animal
{
public Animal[] Offspring { get; set; }
}
public class Dog : Animal {}
Animal fido = new Dog
{
Offspring = new Animal[] { new Dog() }
};
var json = JsonConvert.SerializeObject(fido,
new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.Auto
});
The Json emitted into the json variable is:
{
"Offspring": [{
"$type": "MyApp.Dog, MyApp",
"Offspring": null
}]
}
The Json.NET Documentation says that for TypeNameHandling.Auto the behavior is:
Include the .NET type name when the type of the object being serialized is not the same as its declared type.
My question is - Why does fido not have
"$type": "MyApp.Dog, MyApp", like its puppy? :)
UPDATE: I've found out from the accepted answer to this question that I can force $type to be added by doing this:
var json = JsonConvert.SerializeObject(fido,
typeof(Animal),
new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.Auto,
Formatting = Formatting.Indented
});
But my question still holds - Why does Json.NET not do this by itself as per the documentation?
Short answer: it doesn't because it can't.
As you stated in your question, setting TypeNameHandling to Auto directs Json.Net to include the .NET type name when the actual (run-time) type of the object being serialized is not the same as its declared (compile-time) type. In order to do that, Json.Net needs to know both types for every object.
For everything inside the root object, this is straightforward: just get the runtime type of the root object via GetType(), then use reflection to get all of its declared properties and their types, and for each one compare the declared type to the actual type to see if they differ. If they do, output the type name.
But for the root object itself, Json.Net doesn't have access to both types. All the information it has is the object referenced by fido, whose runtime type is Dog. There's no way for Json.Net to discover that the fido variable was declared as Animal, unless you provide that context somehow. And that is exactly why Json.Net provides overloads of SerializeObject which allow you to specify the compile-time type of the object being serialized. You must use one of these overloads if you want the TypeNameHandling.Auto setting to work for the root object.
Brian is absolutely correct, Json.NET has no way of knowing the compile-time declared type of the object it's being passed as the value parameter is declared as an object. The easy fix for this was if Json.NET added generic serialize methods so that the compile-time declared type would automatically flow over to Json.NET but the library's author has decided against my proposal for this here.
As an alternative, I've wrapped all my json (de)serialization needs in a JsonHelper class with generic serialize methods which use the typeof expression to automatically pass the compile-time declared type of the value to be serialized.
Newer versions of Json.Net allow you to pass the expected type to the serialize method
ser.Serialize(stream, rootObject, typeof(BaseClass));
You can pass the base class to the serialize method and TypeNameHandling.Auto will write the $type if the object and expected type do not match.

WCF - serializing inherited types

I have these classes:
[DataContract]
public class ErrorBase {}
[DataContract]
public class FileMissingError: ErrorBase {}
[DataContract]
public class ResponseFileInquiry
{
[DataMember]
public List<ErrorBase> errors {get;set;};
}
An instance of the class ResponseFileInquiry is what my service method returns to the client. Now, if I fill ResponseFileInquiry.errors with instances of ErrorBase, everything works fine, but if I add an instance of inherited type FileMissingError, I get a service side exception during serialization:
Type 'MyNamespace.FileMissingError' with data contract name 'FileMissingError'
is not expected. Add any types not known statically to the list of known types -
for example, by using the KnownTypeAttribute attribute or by adding them to the
list of known types passed to DataContractSerializer.'
So serializer is getting confused because it's expecting the List to contain the declared type objects (ErrorBase) but it's getting inherited type (FileMissingError) objects.
I have the whole bunch of error types and the List will contain combinations of them, so what can I do to make it work?
You should add KnownType attribute to your base class
[DataContract]
[KnownType(typeof(FileMissingError))]
public class ErrorBase {}
Read more about KnownType attribute in this blog
Try this:
[DataContract]
[KnownType(typeof(FileMissingError))]
public class ErrorBase {}
As the error message states, any information that cannot be know statically (like the polymorphic relationship you have expressed here) must be supplied via attributes. In this case you need to specify that your FileMissingError data contract is a known type of its base class, ErrorBase.
A tad bit late, but maybe for future generations. =)
If you don't want to add an attribute for every child class to your parent class, you could construct a list of known types in the parent classes static constructor using
IEnumerable<Assembly> assemblies = AppDomain.CurrentDomain
.GetAssemblies()
.Where(a => !a.GlobalAssemblyCache);
IEnumerable<Type> serializableTypes = assemblies.SelectMany(a => a.GetTypes())
.Where(t => IsSerializable(t));
// ...
private static bool IsSerializable(Type type)
{
return type.GetCustomAttributes(true).Any(a => a is DataContractAttribute);
}
and pass this list to the de/serializers constructor. I don't know how robust this solution is, but that's what I am doing and so far it works. It is a little slow, so make sure to cache the result.