spring序列化
Spring中从JSON到JSON的序列化/反序列化已广泛用于基于Spring的现代应用程序中。 它基于杰克逊。 Jackson可以轻松地将任何POJO序列化为JSON,反之亦然。 这段代码写得很好。 我从来没有遇到任何问题。 涉及自定义序列化程序时,将变得更加困难。 这篇文章展示了如何在带有自动装配字段的Spring中使用自定义序列化程序。
通常,一个类的自定义序列化器是从继承的 com.fasterxml.jackson.databind.ser.std.StdSerializer。 此类定义了一些构造函数,但是框架只需要一个无参数的构造函数即可调用超类,如下所示:
public CustomSerializer() {
this(null);
}
public CustomSerializer(Class<ObjectToSerialize> t) {
super(t);
}
然后是实际编写JSON必须实现的主要方法:
@Override
public void serialize(ObjectToSerialize value, JsonGenerator gen, SerializerProvider provider) throws IOException {
gen.writeStartObject();
...
provider.defaultSerializeField("some field", value.getField(), gen);
...
gen.writeEndObject();
}
创建序列化程序类时,必须将其注册为ObjectToSerialize的序列化程序。 这可以通过在类上使用@JsonSerialize注释来完成:
@JsonSerialize(using = CustomSerializer.class)
public class ObjectToSerialize {
现在,杰克逊将对该类的所有实例使用此自定义序列化程序。 如有必要,可以通过子类编写自定义解串器 com.fasterxml.jackson.databind.deser.std.StdDeserializer <T>
对于大多数使用Spring和Hibernate的商业应用程序,循环引用的问题迟早会出现。 这是一个简单的例子。
我们有2个课程:
public class Building {
@Id
@GeneratedValue(<parameters>)
private Long id;
private Set<Apartment> apartments;
}
public class Apartment {
@Id
@GeneratedValue(<parameters>)
private Long id;
private Building building;
}
如果我们尝试序列化至少有一个公寓的建筑物,则会得到StackOverflowException。
杰克逊有一个解决此问题的方法-@JsonIdentityInfo。
如果将注释@JsonIdentityInfo添加到此类,则:
@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
public class ObjectToSerialize {
那么任何ObjectMapper都将通过用其ID替换除第一个对象以外的所有对象的出现而打破循环。 像这样:
{
"id": 1,
"apartments": [
{
"id": 2,
"building": 1 - the object is replaced with its ID
},
{
"id": 3,
"building": 1 - the object is replaced with its ID
}
]
}
这些是Jackson提供的用于自定义序列化和处理循环引用的工具。
@JsonIdentityInfo适用于简单的应用程序。 但是随着应用程序以默认格式增长,它可能会影响JSON的结构。 例如,如果某些方法在一个响应中返回建筑物和区域,则可能会发生以下情况:
{
"buildings": [
{
"id": 1,
"apartments": [
{
"id": 2,
"building": 1 - the object is replaced with its ID
},
{
"id": 3,
"building": 1 - the object is replaced with its ID
}
]
}
],
"districts": [
{
"buildings": [
{
"id": 5,
...
},
1, - the object is replaced with its ID
{
"id": 6,
...
}
]
}
]
}
从解析器的角度来看,这种替换可能是完全不可预测的。 在数组中,它可能会遇到对象和ID。 这可能发生在任何字段和任何对象上。 如果序列化提供程序多次找到该对象,则使用@JsonIdentityInfo对该类进行注释的任何对象都将替换为其ID。 序列化提供程序发现的具有相同ID的每个第二,第三,第四等实例都将替换为其ID。
这里的解决方案是使用单独的ObjectMapper编写JSON的一部分。 已经看到的ID的列表存储在由ObjectMapper创建的序列化提供程序中。 通过创建单独的ObjectMapper(具有可能不同的配置),可以重置列表。
对于返回不同对象类型的“复合” JSON结果,可以编写自定义序列化程序。 在此自定义序列化程序中,“标头”是使用JsonGenerator方法手动编写的,当达到JSON中的正确级别时,我们将创建一个新的ObjectMapper并编写外观更好的JSON。
{
"buildings": [ - create a new ObjectMapper
{
"id": 1,
"apartments": [
{
"id": 2,
"building": 1 - the object is replaced with its ID
},
{
"id": 3,
"building": 1 - the object is replaced with its ID
}
]
}
],
"districts": [ - create a new ObjectMapper
{
"buildings": [
{
"id": 5,
...
},
{ - the object is written as a JSON Object not an ID
"id": 1,
...
},
{
"id": 6,
...
}
]
}
]
}
要将JSON写入原始生成器,我们可以使用 ObjectMapper.writeValueAsString和 JsonGenerator.writeRawValue(String)。
PS也可以通过以下方式创建新的序列化提供程序 DefaultSerializerProvider.createInstance(SerializationConfig,SerializerFactory),但它可能更复杂。
我们希望能够在自定义序列化程序中使用@Autowire。 这是Spring的最佳功能之一! 实际上,如果使用默认的ObjectMapper,它就可以工作。 但是,如果我们使用JSON结构问题的解决方案,则该解决方案不适用于由我们自己的对象映射器实例化的自定义序列化程序。
我们自己的对象映射器必须配置有特殊的HandlerInstantiator:
// try to use the default configuration as much as possible
ObjectMapper om = Jackson2ObjectMapperBuilder.json().build();
// This instantiator will handle autowiring properties into the custom serializers
om.setHandlerInstantiator(
new SpringHandlerInstantiator(this.applicationContext.getAutowireCapableBeanFactory()));
如果自定义对象映射器是在另一个由默认ObjectMapper创建的自定义序列化器中创建的,则它可以自动装配ApplicationContext。
翻译自: https://www.javacodegeeks.com/2019/01/spring-custom-serializers-jsonidentityinfo.html
spring序列化