Home > Enterprise >  Jackson polymorphic serialization: how to remove super type property wrapper?
Jackson polymorphic serialization: how to remove super type property wrapper?

Time:06-30

I have this situation:

@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.WRAPPER_OBJECT)
@JsonSubTypes({
        @JsonSubTypes.Type(value = FlyingCar.class, name = "flying_car"),
        @JsonSubTypes.Type(value = WaterProofPhone.class, name = "water_proof_phone"),
        @JsonSubTypes.Type(value = ElectricBicycle.class, name = "electric_bicycle")
})
public abstract class Product {
}

and every subclass is defined like:

@JsonTypeName("flying_car")
public class FlyingCar extends Product {
     private double verticalSpeed; 
     private String name;
}

When I serialize the class below, I would like to not include a product property in the json:

public class Cart {
   private long id;
   private LocalDateTime date;
   private Product product;
}

Example serializing this configuration:

Product product = new FlyingCar(123.5,"StarShip");
Cart cart = new Cart();
cart.setProduct(product);

String json = objectMapper.writeValueAsString(cart);

Produces this json:

{
  "id":..,
  "product": { <--- How can I remove this wrapper ?
    "flying_car":{
      "vertical_speed": 123.5,
      "name": "StarShip"
    }
  }
}

How to simply have a json like this, without the supertype wrapper?

{
  "id":..,
  "flying_car":{
      "vertical_speed": 123.5,
      "name": "StarShip"
   }
}

I tried the @JsonUnwrapped on product but it does not work as expected.

Thanks for your help

CodePudding user response:

As mentioned in the comments, you have to use a custom serializer to implement that.

The following serializer implementation should work as expected.

public class CustomSerializer extends JsonSerializer<Cart> {
    @Override
    public void serialize(Cart cart, JsonGenerator gen, SerializerProvider serializers) throws IOException {
        final Object product = cart.getProduct();
        Class<?> responseClass = product.getClass();
        JavaType responseJavaType = serializers.constructType(responseClass);
        gen.writeStartObject();
        gen.writeFieldName(serializers.findTypeSerializer(responseJavaType).getTypeIdResolver().idFromValue(product));
        serializers.findValueSerializer(responseClass).serialize(product, gen, serializers);
        /* Here you must manually serialize other properties */
        gen.writeObjectField("id", cart.getId());
        gen.writeEndObject();
    }


}

And you need to set this seriliazer for your Cart class :

@JsonSerialize(using = CustomSerializer.class)
public class Cart {
...

}

CodePudding user response:

To complete @Nemanja's answer, I found a simpler solution using @JsonAnyGetter:

public class Cart {
   private long id;
   private LocalDateTime date;
   @JsonIgnore
   private Product product;

   @JsonAnyGetter
   Map<String, Product> determineProduct(){
     if (this.product instanceof FlyingCar){
       return Map.of("flying_car", this.product);
     }
     ....other type checking
  }
}

It's a bit simpler and we don't have to define a custom serializer

  • Related