用于Collection类的Spring Boot自定义序列化程序 [英] Spring Boot custom serializer for Collection class

查看:132
本文介绍了用于Collection类的Spring Boot自定义序列化程序的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图为我的对象的属性之一实现自定义序列化程序,以便在我从REST控制器返回它时获得不同的JSON结构.

我的约束是我无法更改REST控制器或模型类的接口(因此我无法添加额外的注释等,这可能会使此操作更容易).我唯一能想到的就是使它的渲染与模型中描述的有所不同的是自定义序列化程序,如果有更好的方法,请随时告诉我在约束范围内的另一种方法.

我的模型如下所示:

public class WrapperModel {

  // a lot of autogenerated fields

  List<Property> properties;

  // getters/setters
}

public class Property {

  private String name;

  private String value;

  // getters / setters

}

因此,呈现时看起来像这样:

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

我想要的是这个

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

用于此的序列化程序很容易:

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();
}

}

现在,当我尝试在@Configuration文件中注册此序列化器时:

@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;
}

这是行不通的,因为List<Property>.class是模板类,因此不能用于addSerializer.还有其他添加此序列化器的方法或执行类似操作的方法吗?

我不想为WrapperModel添加自定义序列化程序,因为此类是自动生成的,并且可以添加和删除字段.这无需修改应用程序代码就可以实现(如果我有一个自定义的序列化程序,则还需要从序列化程序中添加/删除字段(?)).或者有没有办法只对类使用Standard序列化程序,并手动处理此List<>字段.

模型类是由Spring Boot openapi代码生成器生成的,因此我可以在模型字段的顶部放置一组非常有限的JSON注释(如果有注释的方法,请不要犹豫地发布)检查openapi源代码(如果支持该特定注释).但是我宁愿使用List<Property>的自定义序列化程序(如果可能的话),也可以编写WrapperModel的序列化程序,该序列化程序使用StdSerializer进行所有操作,并且自己处理List属性.

解决方案

MixIn

在这种情况下,我们需要使用MixIn功能.创建如下界面:

interface WrapperModelMixIn {

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

并按如下所示进行注册:

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

较旧的提案

您需要使用允许注册通用类型的序列化器的Jackson类型.更改后的序列化器可能如下所示:

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();
    }
}

您可以按如下所示进行注册:

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);

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.

解决方案

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);

这篇关于用于Collection类的Spring Boot自定义序列化程序的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆