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):
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\""));
}
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.
Since Jackson 2.9 writeTypePrefixForObject()
and writeTypeSuffixForObject()
have been deprecated (I'm unclear why). It seems under the new approach it would now be:
@Override
public void serializeWithType(IPet value, JsonGenerator gen,
SerializerProvider provider, TypeSerializer typeSer)
throws IOException, JsonProcessingException {
WritableTypeId typeId = typeSer.typeId(value, START_OBJECT);
typeSer.writeTypePrefix(gen, typeId);
serialize(value, gen, provider); // call your customized serialize method
typeSer.writeTypeSuffix(gen, typeId);
}
So an extra line now, so not sure why it's a step forward, perhaps it's more efficient reusing the typeId
object.
Source: Jackson's ObjectNode class currently in master. Not the best source but couldn't see any upgrade docs explaining what to do.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With