Reputation: 13301
I am using Jackson to unmarshal polymorphic types from JSON. I am using the @JsonTypeInfo
, @JsonSubTypes
, and @JsonTypeName
annotations similar to Example 4 in this post. My question is, say now I need someone else to extend my code and add a 3rd class: public class Duck extends Animal
outside of the original code base. How can I (or the others) add the SubType information without modifying the source code (annotation) of the public abstract Animal class
?
UPDATE:
I am forced to use @JsonTypeName to solve the POJO version changes. For example:
package my.zoo;
@JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.PROPERTY,
property = "type")
@JsonSubTypes({
@Type(value = Cat.class, name = "[email protected]"),
@Type(value = Dog.class, name = "[email protected]"),
@Type(value = Catv2.class, name = "[email protected]")})
public abstract class Animal {
...
}
@JsonTypeName("[email protected]")
public class Cat extends Animal {
...
}
@JsonTypeName("[email protected]")
public class Catv2 extends Animal {
...
}
@JsonTypeName("[email protected]")
public class Dog extends Animal {
...
}
// in another java package/project
package another.zoo;
import my.zoo.*;
@JsonTypeName("[email protected]")
public class Dogv2 extends Animal {
...
}
Now the problem I am facing is that I cannot unmarshal a JSON that has type name "[email protected]" without adding @Type(value = another.zoo.Dogv2.class, name = "[email protected]")})
to the Animal
class. So doing this with annotation is obviously not possible. Is there a way to do this at runtime?
UPDATE 2:
I just found this SO question with the same/similar use case. My concern here is that using annotation will prevent people from extending/implementing your base class/interface. I would like a way to still keep extensibility of my base class/interface and make sure my (un)marshalling logic will work with future concrete types.
Upvotes: 13
Views: 11219
Reputation: 279880
Don't use @JsonSubTypes
. Use @JsonTypeInfo#use
with JsonTypeInfo.Id.CLASS
to identify types for serialization and deserialization.
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = "type")
abstract class Animal { /* whatever */ }
Jackson will then store the fully qualified name to serialize the object and use it again to determine a target class for deserialization.
You'll have to make sure none of the subclasses have a property named type
.
It's possible to make this work with @JsonSubTypes
but it involves mixins and is not a very maintainable solution.
Upvotes: 2
Reputation: 13301
I ended up using Reflections library to find all subtype of Animal
class, and register JsonSubTypes with mapper.registerSubtypes(Class<?>... classes)
method.
Upvotes: 10