Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

@class getting added even though it's the defaultImpl

Tags:

java

json

jackson

Using Jackson (the JSON/Object mapper), let's say I have the following interface and classes:

@JsonTypeInfo(defaultImpl = Duck.class, use = JsonTypeInfo.Id.CLASS)
public interface Animal {
}

public class Duck implements Animal {
    private String name;
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
}

public class Park {
    private Animal animal;
    public Animal getAnimal() { return animal; }
    public void setAnimal(Animal animal) { this.animal = animal; }
}

And I try to read (deserialize) this piece of JSON:

{
    "animal": {
        "name": "Quacky"
    }
}

using:

Park park = objectMapper.readValue(json, Park.class);

It works! Even without any type information (@class or @type) it is able to construct the Duck because I specified a defaultImpl for the Animal interface.

Now I expect the same behaviour when doing it the other way around.

String json = objectMapper.writeValueAsString(park);

But instead I get this JSON:

{
    "animal": {
        "@class": "com.example.Duck",
        "name": "Quacky"
    }
}

It does not seem to respect the defaultImpl when serializing! I want the "@class" attribute gone from the output file. Does anyone have any idea of how I can tell Jackson not to add the "@class" attribute for my Duck?

like image 599
Tom van Zummeren Avatar asked Feb 14 '14 08:02

Tom van Zummeren


1 Answers

You need to tell Jackson not to include class information for the implementation class. As per the documentation,

More granular (and powerful) way to define what type information to add, and where, is to use @JsonTypeInfo annotation (and possibly couple of related ones). For example, we could have annotated Animal as follows:

@JsonTypeInfo(use=JsonTypeInfo.Id.CLASS, include=JsonTypeInfo.As.PROPERTY, property="@class") class Animal { } (which, incidentally is equivalent to the default settings for typing).

What does that mean?

  • All instances of annotated type and its subtypes use these settings (unless overridden by another annotation)
  • "Type identifier" to use is fully-qualified Java class name (like "org.codehaus.jackson.sample.Animal")
  • Type identifier is to be included as a (meta-)property, along with regular data properties; using name "@class" (default name to use depend on type if: for classes it would be "@class")
  • Use default type resolver (no @JsonTypeResolver added); as well as default type id resolver (no @JsonTypeIdResolver)

Do this by adding an annotation to Duck.

@JsonTypeInfo(use = JsonTypeInfo.Id.NONE)
public class Duck implements Animal {
    private String name;
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
}

Failing that, add

SimpleModule simpleModule = new SimpleModule("SimpleModule", new Version(1,0,0,null));
simpleModule.addSerializer(new JsonSerializer< Duck > {
    @Override
    public void serialize(Duck duck, JsonGenerator jgen,
            SerializerProvider provider) throws IOException,
            JsonProcessingException {
        jgen.writeStartObject();
        jgen.writeStringField("name", duck.getName());
        jgen.writeEndObject();
    }
});
mapper.registerModule(simpleModule);
like image 155
stevedbrown Avatar answered Sep 20 '22 11:09

stevedbrown