使用@JsonIdentityInfo的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>
循环引用和@JsonIdentityInfo
对于大多数使用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提供的用于自定义序列化和处理循环引用的工具。
JSON结构问题
问题
@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
使用@JsonIdentityInfo的Spring自定义序列化器相关推荐
- spring序列化_使用@JsonIdentityInfo的Spring自定义序列化器
spring序列化 介绍 Spring中从JSON到JSON的序列化/反序列化已广泛用于基于Spring的现代应用程序中. 它基于杰克逊. Jackson可以轻松地将任何POJO序列化为JSON,反之 ...
- kafka自定义序列化器
<kafka权威指南> Customer.java public class Customer {private int customId;private String customerN ...
- Redis序列化、RedisTemplate序列化方式大解读,介绍Genericjackson2jsonredisserializer序列化器的坑
前言 上一篇已经介绍了优雅的操作Redis: [小家Spring]Spring Boot中使用RedisTemplate优雅的操作Redis,并且解决RedisTemplate泛型注入的问题.本篇着重 ...
- 60-40-020-序列化-自定义序列化
1.视界 1. 案例说明 有一个 Java 实体类 Customer,定义如下: package com.bonc.rdpe.kafka110.beans;/*** @Title Customer.j ...
- 客户端序列码生成_Django REST Framework教程(2): 序列化器介绍及开发基于函数视图的API...
在上篇文章中,我们已经介绍了为什么要学习DRF,什么是序列化以及什么是符合RESTful规范的Web API.在本篇文章中我们将以博客为例,使用DRF提供的序列化器(Serializers类)开发两个 ...
- JDK11下J2Cache序列化器反射异常及--illegal-access解决方案
问题现象 最近线上部署应用时,发现如下异常: Failed to instantiate [net.oschina.j2cache.CacheChannel]: Factory method 'cac ...
- 序列化器serializers的使用
serializer.py中设置序列化器 from rest_framework import serializers from myapp.models import Poemclass PoemS ...
- 快速上手Django(六) -Django之Django drf 序列化器Serializer类
文章目录 快速上手Django(六) -Django之Django drf 序列化器Serializer类 1. 背景 2. 使用思路 3. 代码demo 4. [重要]序列化类 ModelSeria ...
- Django 基础(13)-Django drf 序列化器类to_representation和to_internal_value(处理返回的日期格式)、序列化类 ModelSerializer
文章目录 一.Django drf 序列化 1. 背景 2. 使用思路 3. 代码demo 4. [重要]序列化类 ModelSerializer 5. DRF序列化器to_representatio ...
最新文章
- Java7中的switch支持String的实现细节
- php命名空间 动态 new,PHP命名空间(namespace)的动态访问及使用技巧_php实例
- ThinkPHP函数详解:M方法
- 以在线教育销售CRM为例,谈谈业务大盘拆解优化的六步法
- [Python图像处理] 三十五.OpenCV图像处理入门、算数逻辑运算与图像融合(推荐)
- MVC + AJAX请求失败的问题
- maven 可执行 jar_Maven提示:有关可执行jar的所有信息
- oracle+循环插入sql,SQL server,Oracle循环插入百万数据
- Spring Cloud服务网关(Zuul)的配置项sensitiveHeaders
- docker下载出现异常 dial tcp: lookup ngc.download.nvidia.cn: no such host
- Web压力测试和手机App测试
- PreferenceScreen1
- CDN与智能DNS 基本原理
- 多金的ToG项目总结——数字人民币
- CentOS8下超详细安装配置kubernetes(K8S)
- 海豹突击队体能训练手册——前言
- 骑行运动耳机哪个好,五款最适合在骑行中佩戴的耳机分享
- 如果当初投10000买iPhone股票 现在能赚81部iPhoneX
- 清华大学网上课程面向全国免费开放!无需登录、注册!在家上清华!
- python 使用wxpy实现获取微信好友列表 头像 群成员