用于Collection类的Spring Boot自定义序列化程序 [英] Spring Boot custom serializer for Collection class
问题描述
我试图为我的对象的属性之一实现自定义序列化程序,以便在我从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屋!