Issue
I'm currently migrating some code from Jackson 1.x to Jackson 2.5 json mapper and came a long a problem that wasn't there in 1.x.
This is the setup (see code below):
- interface IPet
- class Dog implements IPet
- IPet is annotated with @JsonTypeInfo and @JsonSubTypes
- class Human has a property of type IPet that is annotated with @JsonSerialize(using=CustomPetSerializer.class)
The problem: If I serialize an instance of Dog it works as expected (also the type info is added to the json string by Jackson). However when I serialize an instance of the Human class an exception is thrown saying:
com.fasterxml.jackson.databind.JsonMappingException: Type id handling not implemented for type com.pet.Dog (through reference chain: com.Human["pet"])
The serialize(...) method of the CustomPetSerializer class is not invoked (tested using a breakpoint).
The code:
IPet implementation:
@JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=JsonTypeInfo.As.PROPERTY, property="type")
@JsonSubTypes({
@JsonSubTypes.Type(value=Dog.class, name="dog")
//,@JsonSubTypes.Type(value=Cat.class, name="cat")
//more subtypes here...
})
public interface IPet
{
public Long getId();
public String getPetMakes();
}
Dog implementation:
public class Dog implements IPet
{
@Override
public String getPetMakes()
{
return "Wuff!";
}
@Override
public Long getId()
{
return 777L;
}
}
Human who owns a dog:
public class Human
{
private IPet pet = new Dog();
@JsonSerialize(using=CustomPetSerializer.class)
public IPet getPet()
{
return pet;
}
}
CustomPetSerializer implementation:
public class CustomPetSerializer extends JsonSerializer<IPet>
{
@Override
public void serialize(IPet value, JsonGenerator gen, SerializerProvider serializers) throws IOException, JsonProcessingException
{
if(value != null && value.getId() != null)
{
Map<String,Object> style = new HashMap<String,Object>();
style.put("age", "7");
gen.writeObject(style);
}
}
}
JUnit test method:
@Test
public void testPet() throws JsonProcessingException
{
ObjectMapper mapper = new ObjectMapper();
Human human = new Human();
//works as expcected
String json = mapper.writeValueAsString(human.getPet());
Assert.assertNotNull(json);
Assert.assertTrue(json.equals("{\"type\":\"dog\",\"id\":777,\"petMakes\":\"Wuff!\"}"));
//throws exception: Type id handling not implemented for type com.pet.Dog (through reference chain: com.Human["pet"])
json = mapper.writeValueAsString(human); //exception is thrown here
Assert.assertNotNull(json);
Assert.assertTrue(json.contains("\"age\":\"7\""));
}
Solution
You'll need to additionally override serializeWithType
within you CustomPetSerializer
because IPet
is polymorphic. That's also the reason why serialize
is not called. Check this related SO question that explains in detail when serializeWithType
is called. For instance, your serializeWithType
implementation might look something like this:
@Override
public void serializeWithType(IPet value, JsonGenerator gen,
SerializerProvider provider, TypeSerializer typeSer)
throws IOException, JsonProcessingException {
typeSer.writeTypePrefixForObject(value, gen);
serialize(value, gen, provider); // call your customized serialize method
typeSer.writeTypeSuffixForObject(value, gen);
}
which will print {"pet":{"type":"dog":{"age":"7"}}}
for your Human
instance.
Answered By - edi