起因
业务中创建了一个存储表格数据的对象,如下
@Data
public class SheetDataDto {
private List<SheetColumnInfo> columns;
private List<SheetRowInfo> rows;
private Map<String, Object> meta;
@Data
public static class SheetColumnInfo {
private String field;
private String title;
private String typeName;
private Map<String, Object> meta;
public SheetColumnInfo() {
this.meta = MapUtil.empty();
}
}
@Data
public static class SheetRowInfo {
private List<CellInfo> cells;
private Integer createId;
private Map<String, Object> meta;
public SheetRowInfo() {
this.cells = new ArrayList<>();
this.meta = MapUtil.empty();
}
public SheetRowInfo(Integer createId) {
this.createId = createId;
this.cells = new ArrayList<>();
this.meta = MapUtil.empty();
}
}
@Data
public static class CellInfo {
private String field;
private String value;
private Map<String, Object> meta;
public CellInfo() {
this.meta = MapUtil.empty();
}
}
public SheetDataDto() {
this.columns = new ArrayList<>();
this.rows = new ArrayList<>();
this.meta = MapUtil.empty();
}
}
该类包含嵌套结构,并且由于各个meta
属性在构造时需要为空,所以用了MapUtil.empty()
直接赋值给了meta
构造一个新对象,并对属性赋值,对象内容如下
SheetDataDto(columns=[SheetDataDto.SheetColumnInfo(field=jsm2dr, title=Name, typeName=string, meta={}), SheetDataDto.SheetColumnInfo(field=gadh9i, title=Notes, typeName=string, meta={}), SheetDataDto.SheetColumnInfo(field=xq1n1n, title=Status, typeName=string, meta={})], rows=[SheetDataDto.SheetRowInfo(cells=[], createId=1, meta={}), SheetDataDto.SheetRowInfo(cells=[], createId=1, meta={}), SheetDataDto.SheetRowInfo(cells=[], createId=1, meta={})], meta={})
但是在使用Fastjson对该对象进行序列化时,各个meta
会被序列化为 "meta":{"$ref":"$.data.columns[0].meta"}
,可是这个meta
本身就是一个空Map
{
"meta": { "$ref": "$.columns[0].meta" },
"rows": [
{ "meta": { "$ref": "$.columns[0].meta" }, "cells": [], "createId": 1 },
{ "meta": { "$ref": "$.columns[0].meta" }, "cells": [], "createId": 1 },
{ "meta": { "$ref": "$.columns[0].meta" }, "cells": [], "createId": 1 }
],
"columns": [
{
"meta": {},
"field": "jsm2dr",
"title": "Name",
"typeName": "string"
},
{
"meta": { "$ref": "$.columns[0].meta" },
"field": "gadh9i",
"title": "Notes",
"typeName": "string"
},
{
"meta": { "$ref": "$.columns[0].meta" },
"field": "xq1n1n",
"title": "Status",
"typeName": "string"
}
]
}
分析
Fastjson 在序列化过程中检测到循环引用(即对象结构中存在相互引用),为了防止无限递归和栈溢出,Fastjson 会自动启用循环引用检测,并使用$ref
来引用已经序列化过的相同对象,避免重复输出 在上述问题中,SheetDataDto
、SheetColumnInfo
和 SheetRowInfo
对象内部的 meta
属性虽然都为空,但MapUtil.empty()
是一个静态的空Map,三个对象的meta
其实都是同一个对象。当 Fastjson 序列化这些对象时,由于它们的 meta
属性都为空且互相引用,为了避免重复输出空对象,Fastjson 选择了使用 $ref
引用来替代直接输出空 meta 对象
解决
1、消除循环引用 将this.meta = MapUtil.empty();
替换为this.meta = new HashMap<>();
,让所有的meta
都是互不相关的空Map
2、设置 SerializerFeature
使用 SerializerFeature.DisableCircularReferenceDetect
特性可以禁用 Fastjson 的循环引用检测功能
JSON.toJSONString(sheet, SerializerFeature.DisableCircularReferenceDetect);
警告:禁用循环引用检测可能会导致无限递归或栈溢出问题,如果对象结构中确实存在循环引用,请谨慎使用此方法
3、使用 JSON.toJSONStringZ()
Fastjson 提供了一个 toJSONStringZ()
方法,它会在序列化时忽略空对象
JSON.toJSONStringZ(sheet);
注意:toJSONStringZ() 方法可能会改变原始序列化的结果,因为它会忽略所有空对象,而不仅仅是引起循环引用的空对象,并且该方法已被标记为deprecated
参考
-
通义灵码
-
fastjson SerializerFeature详解[1]
fastjson SerializerFeature详解: https://blog.csdn.net/u010246789/article/details/52539576
原文始发于微信公众号(程序员段宝):Fastjson序列化空Map出现key为$ref的键值对的问题
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/272497.html