JSON Jackson - exception when serializing a polymorphic class using a custom serializer

I am currently porting some code from Jackson 1.x to Jackson 2.5 json mapper and there was a problem that was not in 1.x.

This is the setting (see code below):

  • IPet interface
  • Dog class implements IPet
  • IPet annotated with @JsonTypeInfo and @JsonSubTypes
  • class A person has a property of type IPet, which is annotated using @JsonSerialize (using = CustomPetSerializer.class).

Problem: If I serialize a Dog instance, it works as expected (also type information is added to the Jackson json string). However, when I serialize an instance of the Human class, an exception is thrown:

com.fasterxml.jackson.databind.JsonMappingException: type identifier processing is not applied for the com.pet.Dog type (via the reference chain: com.Human ["pet"])

The serialize (...) method of the CustomPetSerializer class is not called (checked 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; } } 

Dog Owner:

 public class Human { private IPet pet = new Dog(); @JsonSerialize(using=CustomPetSerializer.class) public IPet getPet() { return pet; } } 

Executing CustomPetSerializer:

 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 validation 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\"")); } 
+7
java json jackson
source share
2 answers

You need to further override serializeWithType inside you CustomPetSerializer , because IPet is polymorphic. This is also the reason serialize not called. Check out this related SO question , which explains in detail when serializeWithType is called. For example, an implementation of serializeWithType 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.

+13
source share

Since Jackson 2.9 writeTypePrefixForObject() and writeTypeSuffixForObject() are deprecated (I don't understand why). According to the new approach, it will look as follows:

 @Override public void serializeWithType(IPet value, JsonGenerator gen, SerializerProvider provider, TypeSerializer typeSer) throws IOException, JsonProcessingException { WritableTypeId typeId = typeSerializer.typeId(value, START_OBJECT); typeSer.writeTypePrefix(gen, typeId); serialize(value, gen, provider); // call your customized serialize method typeSer.writeTypeSuffix(gen, typeId); } 

So now an extra line has been added, so I’m not sure why this is a step forward, perhaps it is more efficient to reuse the typeId object.

Source: Jackson ObjectNode class is currently located in the main. Not the best source, but could not see update documents explaining what to do.

+2
source share

All Articles