Spring Boot custom serializer for Collection class

问题: I was trying to implement a custom serializer for one of the properties of my object to get a different JSON structure when I return it from my REST controller. My constr...

问题:

I was trying to implement a custom serializer for one of the properties of my object to get a different JSON structure when I return it from my REST controller.

My constraints are I cannot change the interface of the REST controller or the model classes (so I cannot add extra annotation etc, that would maybe make this easier). The only thing I could think of, making it render different than described in the model is a custom serializer, if there are any better approaches for this, please don't hesitate to tell me a different approach that is within the constraints.

My models look something like this:

public class WrapperModel {

  // a lot of autogenerated fields

  List<Property> properties;

  // getters/setters
}

public class Property {

  private String name;

  private String value;

  // getters / setters

}

So when this is rendered is looks like so:

{   ....   
    "properties": [
      {"key1": "value1"}, {"key2": "value2"},...   
    ] 
}

What I would want is this:

{   ....   
    "properties": {
      "key1": "value1",
      "key2": "value2",
      ...  
    }
}

The serializer for this is easy enough:

public class PropertyListJSONSerializer extends StdSerializer<List<Property>> {

//....

@Override
public void serialize(List<Property> value, JsonGenerator gen,   SerializerProvider provider) throws IOException {
    gen.writeStartObject();
    for(Property p: value){
        gen.writeStringField(p.getName(), p.getValue());
    }
    gen.writeEndObject();
}

}

Now when I try to register this serializer inside a @Configuration file:

@Bean
public ObjectMapper objectMapper() {
    ObjectMapper mapper = new ObjectMapper();
    mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);

    SimpleModule module = new SimpleModule();
    module.addSerializer(List<Property>.class, new PropertyListJSONSerializer());
    mapper.registerModule(module);

    return mapper;
}

this doesn't work, because List<Property>.class cannot be used for addSerializer since it's a template class. Is there any other way to add this serializer or something that does something similar?

I do not want to add a custom serializer for WrapperModel since this class is autogenerated and fields can be added and removed. This should be possible without modifying the application code (if I had a custom serializer you would need to add/remove the fields from the serializer also(?)). Or is there a way to just use the Standard serializer for the class and just manually handle this one List<> field.

The model classes are generated by the Spring Boot openapi code generator, so there is a very limited set of JSON annotations I can put on top of the model fields (if there's an annotation way, please dont hesitate to post as I can check in the openapi sourcecode if that particular annotation is supported). But I would rather go with either a custom serializer for List<Property> if that is at all possible or writing a serializer for WrapperModel that uses StdSerializer for everything and only handle the List property myself.


回答1:

MixIn

In that case we need to use MixIn feature. Create interface like below:

interface WrapperModelMixIn {

    @JsonSerialize(using = PropertyListJSONSerializer.class)
    List<Property> getProperties();
}

and register it like below:

ObjectMapper mapper = new ObjectMapper();
mapper.addMixInAnnotations(WrapperModel.class, WrapperModelMixIn.class);

Older proposal

You need to use Jackson types which allow to register serialiser for generic type. Your serialiser after change could look like below:

class PropertyListJSONSerializer extends StdSerializer<List<Property>> {

    public PropertyListJSONSerializer(JavaType type) {
        super(type);
    }

    @Override
    public void serialize(List<Property> value, JsonGenerator gen, SerializerProvider provider)
        throws IOException {
        gen.writeStartObject();
        for (Property p : value) {
            gen.writeStringField(p.getName(), p.getValue());
        }
        gen.writeEndObject();
    }
}

And you can register it as below:

ObjectMapper mapper = new ObjectMapper();

CollectionType propertiesListType = mapper.getTypeFactory().constructCollectionType(List.class, Property.class);
SimpleModule module = new SimpleModule();
module.addSerializer(new PropertyListJSONSerializer(propertiesListType));
mapper.registerModule(module);
  • 发表于 2019-03-20 22:10
  • 阅读 ( 213 )
  • 分类:sof

条评论

请先 登录 后评论
不写代码的码农
小编

篇文章

作家榜 »

  1. 小编 文章
返回顶部
部分文章转自于网络,若有侵权请联系我们删除