Jackson adding additional fields to JSON throws com.fasterxml.jackson.databind.exc.InvalidDefinitionException

1,624 views
Skip to first unread message

Aravind Baliga

unread,
May 3, 2022, 11:03:43 AM5/3/22
to jackson-user
I am using Jackson to serialize my Java POJO classes. In addition to fields, I have in Java POJO, I would like to add some additional information in JSON I am writing my own custom `CustomClassSerializer`. If I use this class and register to `ObjectMapper` then I get the error:
```
Exception in thread "main" com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Type id handling not implemented for type org.acme.Tiger (by serializer of type org.acme.CustomModule$CustomClassSerializer)
```

I am unable to understand what might be going wrong here. If I remove the custom registered model then everything works perfectly.

Can someone please let me know what may be the cause of this issue? I am currently using Jackson 2.13.2 latest version dependencies: `jackson-core, jackson-databind, jackson-annotations, jackson-datatype-jdk8`:

Following is the sample code:
```
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import lombok.*;

@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, visible = true, property = "type")
@JsonSubTypes({@JsonSubTypes.Type(value = Bat.class, name = "Bat"),
        @JsonSubTypes.Type(value = Tiger.class, name = "Tiger")})
@JsonInclude(JsonInclude.Include.NON_NULL)
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class Animal {
    private String type;
    private String name;
}
```


```
@JsonInclude(JsonInclude.Include.NON_NULL)
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Data
@NoArgsConstructor
@AllArgsConstructor
@JsonTypeName("Tiger")
public class Tiger extends Animal {
    private String livingType;
    private String foundIn;
}
```

```
@JsonInclude(JsonInclude.Include.NON_NULL)
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Data
@NoArgsConstructor
@AllArgsConstructor
@JsonTypeName("Bat")
public class Bat extends Animal{
    private String livingType;
    private String foundIn;
}
```

```
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.BeanDescription;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.BeanSerializerFactory;

import java.io.IOException;

public class CustomModule extends SimpleModule {
    public CustomModule() {
        addSerializer(Tiger.class, new CustomClassSerializer());
    }

    private static class CustomClassSerializer extends JsonSerializer {
        @Override
        public void serialize(Object value, JsonGenerator jgen, SerializerProvider provider) throws IOException {
            jgen.writeStartObject();
            jgen.writeObjectField("my_extra_field1", "some data");
            jgen.writeObjectField("my_extra_field2", "some more data");
            JavaType javaType = provider.constructType(Tiger.class);
            BeanDescription beanDesc = provider.getConfig().introspect(javaType);
            JsonSerializer<Object> serializer = BeanSerializerFactory.instance.createSerializer(provider, javaType);
            serializer.unwrappingSerializer(null).serialize(value, jgen, provider);
            jgen.writeEndObject();
        }
    }
}
```


```
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;

public class TestMain {
    public static void main(String[] args) throws JsonProcessingException {
        final ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
        objectMapper.registerModule(new CustomModule());

        Tiger tiger = new Tiger();
        tiger.setType("Tiger");
        tiger.setName("Shera");
        tiger.setFoundIn("Ground");
        tiger.setLivingType("Tree");
        System.out.println(objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(tiger));
    }
}
```

I would like to know what are the causes for the above-mentioned exception.

Tatu Saloranta

unread,
May 3, 2022, 11:31:57 PM5/3/22
to jackson-user
On Tue, May 3, 2022 at 8:03 AM Aravind Baliga <bav...@gmail.com> wrote:
>
> I am using Jackson to serialize my Java POJO classes. In addition to fields, I have in Java POJO, I would like to add some additional information in JSON I am writing my own custom `CustomClassSerializer`. If I use this class and register to `ObjectMapper` then I get the error:
> ```
> Exception in thread "main" com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Type id handling not implemented for type org.acme.Tiger (by serializer of type org.acme.CustomModule$CustomClassSerializer)
> ```
>
> I am unable to understand what might be going wrong here. If I remove the custom registered model then everything works perfectly.

Yes. Your custom deserializer is not doing what it needs to do, as per
exception message.

This because:

>
> Can someone please let me know what may be the cause of this issue? I am currently using Jackson 2.13.2 latest version dependencies: `jackson-core, jackson-databind, jackson-annotations, jackson-datatype-jdk8`:
>
> Following is the sample code:
> ```
> import com.fasterxml.jackson.annotation.JsonInclude;
> import com.fasterxml.jackson.annotation.JsonSubTypes;
> import com.fasterxml.jackson.annotation.JsonTypeInfo;
> import lombok.*;
>
> @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, visible = true, property = "type")

^^^^^^^^

This means that Polymorphic Type Handling is enabled and all
serializers and deserializers need to then support this
handling. Standard Jackson (de)serializers do that.
Your custom serializer has to do the same if (but only if) you use
`@JsonTypeInfo`.

But are you sure you need that?

IYou can check out definition of `JsonSerializer` to see the
additional method you need to implement, which is:

public void serializeWithType(T value, JsonGenerator gen,
SerializerProvider serializers,
TypeSerializer typeSer)

and basically has to add necessary Type Id as part of serialization;
exactly what needs to be done depends on the kind of Type Id to be
added as well as the kind of contents being serialized (JSON Object,
Array or Scalar).

If you go down this route you may want to look for standard Jackson
serializer implementations for guidance: this class:

src/main/java/com/fasterxml/jackson/databind/ser/std/BeanSerializerBase.java

for example implements the method; it is non-trivial but doable.

In general it's good to try to avoid having to define fully
functioning custom serializers if possible, when using more advanced
features like polymorphic handling.

-+ Tatu +-
Reply all
Reply to author
Forward
0 new messages