I have a JSON, that looks like the following:
{
"users": [{
"name": "John"
}]
}
and I have the class where I want to deserialize my JSON:
@JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
property = "type",
defaultImpl = User.class
)
@JsonSubTypes({
@JsonSubTypes.Type(name = "myUser", value = MyUser.class),
@JsonSubTypes.Type(name = "guestUser", value = GuestUser.class)
})
@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true)
public class User {
public String name;
}
public class MyUser extends User {
...
}
public class Request {
List<MyUser> users; // I can't change List<MyUser> to List<User>
}
and when I try to deserialize it the next way:
public static void main(String[] args) {
ObjectMapper mapper = new ObjectMapper();
Request request = mapper.readValue(json, Request.class);
System.out.println(request);
}
I have gotten an exception:
Exception in thread "main" com.fasterxml.jackson.databind.exc.InvalidTypeIdException: Missing type id when trying to resolve subtype of [simple type, class com.example.MyUser]: missing type id property 'type' (for POJO property 'users')
I can't understand why I get this exception... I define an explicit class MyUser
.
Can anyone help me to avoid this exception?
I suggest to use Jackson Mixin feature. the feature allows to externally add and modify annotations to classes outside of the source of the class itself.
in this case, you wish to deserialize list of users and tell jackson to ignore the type info that is specified in User.
first step is to create a class with @JsonTypeInfo
that explicitly defines the impl to use. we can use an interface since it is never instantiated. it is only used to hold the annotation
@JsonTypeInfo(
use = JsonTypeInfo.Id.NONE, // use no property as type info
defaultImpl = MyUser.class // use this impl as deserialization target
)
public interface UserMixin {
}
next we bind the mixin to the User class when creating the mapper:
ObjectMapper mapper = new ObjectMapper()
.addMixIn(User.class, UserMixin.class);
this tells jackson to use any annotation from the mixin when deserializing into User
object. in case of two same annotations, the mixin will be used. overriding annotation from target class.
test method
public static void main(String[] args) {
String json = """
{
"users": [{
"name": "John"
}]
}
""";
try {
ObjectMapper mapper = new ObjectMapper()
.addMixIn(User.class, UserMixin.class);
Request request = mapper.readValue(json, Request.class);
System.out.println(request.users.get(0).name);
} catch (Exception e) {
e.printStackTrace();
}
}
output is as expected...