Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Jackson: Suppress JsonTypeinfo at serialisation time?

My supertype is annotated with

@JsonTypeInfo(use = Id.NAME, include = As.PROPERTY, property = "_typeid")

-so that serialised subtypes include the extra field _typeid containing the name of the subtype. This is intentional and necessary for my app.

But in a particular context I would like to export them as "pure" JSON, i.e. without the _typeid metadata field.

Is there any way of making an ObjectMapper ignore the @JsonTypeinfo annotation during serialisation? I can't find any relevant config- or feature settings. Would I have to resort to postfiltering or alternative serialisers?

I know that removing or altering the annotations would do it, but this is not an option in this case.

ObjectMapper().configure(MapperFeature.USE_ANNOTATIONS, false);

-will turn off all annotations. This does remove the offending field, but also kills other annotations that I would like to work.

like image 598
Joachim Lous Avatar asked Feb 15 '19 11:02

Joachim Lous


People also ask

How do you tell Jackson to ignore a field during serialization?

If there are fields in Java objects that do not wish to be serialized, we can use the @JsonIgnore annotation in the Jackson library. The @JsonIgnore can be used at the field level, for ignoring fields during the serialization and deserialization.

Does Jackson use serializable?

Note that Jackson does not use java. io. Serializable for anything: there is no real value for adding that. It gets ignored.

What is Jackson object serialization?

Jackson is a solid and mature JSON serialization/deserialization library for Java. The ObjectMapper API provides a straightforward way to parse and generate JSON response objects with a lot of flexibility.

What is JsonIgnore Jackson?

The @JsonIgnore annotation marks a field of a POJO to be ignored by Jackson during serialization and deserialization. Jackson ignores the field both JSON serialization and deserialization. An example of Java class that uses the @JsonIgnore annotation is this.


2 Answers

You can add/remove annotation in runtime using JsonView annotation. Let's assume that we have one abstract class Base and one implementation Base1. Instead of adding annotations to Base directly we can do that by adding new interface with these annotations. See below example:

abstract class Base {
}

@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "_typeid")
@JsonSubTypes({
        @JsonSubTypes.Type(name = "base1", value = Base1.class)
})
interface BaseTypeView {
}

class Base1 extends Base {
    private String value = "Base 1";

    public String getValue() {
        return value;
    }

    public void setValue(String value) {
        this.value = value;
    }
}

Now, we need to create two ObjectMappers: one wich uses this view and another which does not.

import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;

public class JsonApp {

    public static void main(String[] args) throws Exception {
        ObjectMapper mapper = createObjectMapper();
        ObjectMapper mapperWithView = createObjectMapper();
        mapperWithView.addMixIn(Base.class, BaseTypeView.class);

        System.out.println(mapper.writeValueAsString(new Base1()));
        System.out.println(mapperWithView.writeValueAsString(new Base1()));
    }

    private static ObjectMapper createObjectMapper() {
        ObjectMapper mapper = new ObjectMapper();
        mapper.enable(SerializationFeature.INDENT_OUTPUT);
        return mapper;
    }
}

Above code prints:

{
  "value" : "Base 1"
}
{
  "_typeid" : "base1",
  "value" : "Base 1"
}
like image 132
Michał Ziober Avatar answered Oct 21 '22 19:10

Michał Ziober


Based on Michal's answer, this simplified version is pretty close to what I'm looking for:

import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.databind.ObjectMapper;

@JsonTypeInfo(use = JsonTypeInfo.Id.NAME)
@JsonSubTypes({
    @JsonSubTypes.Type(value = Cat.class)
})
class Animal { }

class Cat extends Animal {
    public final String genus = "felis";
}

public class Example {
    public static void main(String[] args) throws Exception {
        Cat bill = new Cat();

        ObjectMapper typed = new ObjectMapper();
        System.out.println(typed.writeValueAsString(bill));

        @JsonTypeInfo(use = JsonTypeInfo.Id.NONE)
        class NoTypes { }

        ObjectMapper untyped = new ObjectMapper().addMixIn(Animal.class, NoTypes.class);
        System.out.println(untyped.writeValueAsString(bill));
    }
}

This will output

{"@type":"Cat","genus":"felis"}
{"genus":"felis"}

This approach has the advantages that it requires no control over the data classes - everything can be done locally while configuring the mapper - and that the mixin is general and can be applied to any base class.

It does still require explicit configuration for every targeted base class, though, so it is not quite a general solution.

like image 33
Joachim Lous Avatar answered Oct 21 '22 17:10

Joachim Lous