JSON.toJSONString() 输出 “$ref“:“$[0]“问题解决及原因分析

在这里插入图片描述

一、背景

在构建一个公共的批处理方法类的时候,在测试输出的时候,打印了" r e f " : " ref":" ref":"[0][0]"的内容,这让我比较疑惑。不由得继续了下去…

二、问题分析

首先,我们需要明确 ,在使用诸如Java的序列化库(如Jackson、Gson或Fastjson等)将数据转换为JSON字符串时,JSON.toJSONString(map<String,String>) 调用中可能出现 “ r e f " : " ref":" ref":"[0][0]” 的原因。在JSON序列化过程中,“ r e f " : " ref":" ref":"[0][0]” 这类引用标记通常表示对象中存在循环引用,即一个对象直接或间接地引用了自己。JSON序列化库在检测到这种循环引用时,会尝试使用引用来避免无限递归,并节省内存。

对于简单的 Map<String, String> 类型,通常不应该出现循环引用,因为键值对本身不包含对其他键值对的引用。因此,这个问题可能是在其他部分的代码或序列化库的实现中产生的。

2.1 问题示例

假设我们有一个简单场景,其中Map中的值是一个自引用的类实例,这会导致序列化时出现$ref。


import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.annotation.JsonIdentityInfo;
import com.fasterxml.jackson.annotation.ObjectIdGenerators;@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
class SelfReferencingObject {int id;SelfReferencingObject selfRef;public SelfReferencingObject(int id) {this.id = id;}public void setSelfRef(SelfReferencingObject ref) {this.selfRef = ref;}
}public class Demo {public static void main(String[] args) throws Exception {ObjectMapper mapper = new ObjectMapper();SelfReferencingObject obj1 = new SelfReferencingObject(1);SelfReferencingObject obj2 = new SelfReferencingObject(2);// 创建循环引用obj1.setSelfRef(obj1);obj2.setSelfRef(obj2);Map<String, SelfReferencingObject> map = new HashMap<>();map.put("obj1", obj1);map.put("obj2", obj2);String json = mapper.writeValueAsString(map);System.out.println(json);}
}

上述代码在运行时,将会输出类似于以下内容,其中包含了$ref来表示循环引用:

{
“obj1”: {
“id”: 1,
“@ref”: “KaTeX parse error: Expected 'EOF', got '}' at position 8: [0]" }̲, "obj2": { …[1]”
}
}

在这里插入图片描述

三、原因解析

3.1 循环引用

如果Map中的值直接或间接地引用了Map本身或其他位于Map中的对象,形成了一个闭环,序列化时为了防止无限循环和堆栈溢出,序列化库会使用$ref来标记已处理过的对象,避免重复输出。

3.2 重复引用

即使没有循环引用,但如果多个键值对引用了相同的对象实例,一些序列化库也会使用$ref来优化输出,表示这些位置引用的是同一个对象。

四、解决方案

4.1. 禁用循环检测(不推荐,仅作演示)

大多数序列化库提供了配置选项来禁用循环引用检测,但这可能会导致其他问题,如无限循环序列化。

ObjectMapper mapper = new ObjectMapper();
mapper.disable(com.fasterxml.jackson.databind.deser.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
mapper.enable(com.fasterxml.jackson.databind.SerializationFeature.WRITE_DURABLE_OBJECT_IDS);
String json = mapper.writeValueAsString(map);
...

注意:此方法可能不会直接解决问题,且可能导致无限循环或其它错误,实际应用中应谨慎。

4.2. 自定义序列化策略

你可以通过实现自定义的序列化器或采用库提供的注解等方式,控制特定对象或类的序列化行为,避免$ref的产生。

// 使用Jackson的@JsonIdentityInfo注解解决循环引用
// 上面的SelfReferencingObject类已经添加了@JsonIdentityInfo注解// 序列化代码保持不变
使用@JsonIdentityInfo后,输出的JSON会为重复的对象生成唯一ID,而不是直接使用$ref。

4.3. 手动处理引用

在序列化前,检查并打破潜在的循环引用,比如将引用替换为ID或者浅拷贝对象以切断循环链。

public class Demo {public static void main(String[] args) throws Exception {ObjectMapper mapper = new ObjectMapper();SelfReferencingObject obj1 = new SelfReferencingObject(1);SelfReferencingObject obj2 = new SelfReferencingObject(2);// 防止循环引用,这里不设置selfRefMap<String, SelfReferencingObject> map = new HashMap<>();map.put("obj1", obj1);map.put("obj2", obj2);String json = mapper.writeValueAsString(map);System.out.println(json);}
}

这个例子中,我们直接不设置selfRef,从而避免了循环引用。

4.4. 使用特定库的功能

某些库如Jackson提供了@JsonIdentityInfo注解来处理循环引用问题,自动为重复的对象生成ID引用。

已在示例1中展示了如何使用Jackson的@JsonIdentityInfo注解处理循环引用。

或者使用JSON.toJSONString(Object object, SerializerFeature… features)方法,并传入SerializerFeature.DisableCircularReferenceDetect特性来禁用循环引用检测。

String jsonString = JSON.toJSONString(users, SerializerFeature.DisableCircularReferenceDetect);

在这里插入图片描述

五、补充知识点

在Java中,对象之间的循环引用、重复引用、自引用或相互引用,通常在代码层面直观体现为对象的属性互相指向对方。下面通过示例来具体展示这些引用方式:

5.1. 循环引用

当两个或多个对象互相持有对方的引用,形成一个闭环,这就是循环引用。


class Person {String name;Person friend;Person(String name) {this.name = name;}void setFriend(Person friend) {this.friend = friend;// 这里设置朋友的friend为自己,形成循环引用friend.setFriend(this);}
}public class Main {public static void main(String[] args) {Person alice = new Person("Alice");Person bob = new Person("Bob");alice.setFriend(bob); // 设置Alice的朋友是Bob}
}

在这个例子中,alice的朋友是bob,而bob的朋友又被设置为alice,形成了循环引用。

5.2. 重复引用

如果多个变量或数据结构引用同一个对象实例,就是重复引用。

class Book {String title;Book(String title) {this.title = title;}
}public class Main {public static void main(String[] args) {Book popularBook = new Book("Popular Title");List<Book> library = new ArrayList<>();library.add(popularBook);library.add(popularBook); // 同一个Book实例被添加两次,形成重复引用}
}

这里,popularBook这个Book实例被library列表重复引用了两次。

5.3. 自引用

自引用指的是对象的一个属性直接或间接地引用自身。

class Node {String data;Node next; // 可能指向自己,形成自引用Node(String data) {this.data = data;}void setNext(Node next) {this.next = next;}
}public class Main {public static void main(String[] args) {Node node = new Node("Node Data");// 形成自引用node.setNext(node);}
}

在Node类的例子中,next属性可以设置为指向自己,形成自引用。

总结

在解决这个问题时,关键是要找到循环引用的来源。这可能需要你深入检查代码和序列化库的实现。一旦找到循环引用的来源,你就可以采取适当的措施来避免它,例如修改代码逻辑或自定义序列化过程。如果问题是由序列化库引起的,更新到最新版本或寻找替代库可能是一个解决方案。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/4950.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

安卓开发Webview RTC 适配

一、场景 在混合开发方式中&#xff0c;项目前端使用了tracking.js 开发了一个人脸识别功能&#xff0c;但是在安卓端无法成功调用摄像头进行视频捕获&#xff0c;在浏览器中可以正常使用该功能。 二、问题分析 之前的音视频文件适配提供给前端的方式&#xff0c;都是通过inp…

@Injectable()

当你在 Nest.js 中使用 Injectable() 装饰器标记一个类时&#xff0c;它告诉 Nest.js 这个类是一个可以被注入到其他地方使用的服务。这意味着你可以在你的应用中的不同地方重复使用这个服务&#xff0c;而不必每次都手动创建它的实例。 比如说&#xff0c;假设你有一个服务叫…

【信息系统项目管理师知识点速记】范围管理:定义范围

定义范围是制定项目和产品详细描述的过程。其主要作用是描述产品、服务或成果的边界和验收标准。本过程需要在整个项目期间多次反复开展。 9.5.1 输入 项目章程 包含对项目的高层级描述、产品特征和审批要求。项目管理计划 范围管理计划:记录了如何定义、确认和控制项目范围的…

第八节 LLaVA模型CLI推理构建custom推理代码Demo

文章目录 前言一、parser 参数设定1、lora权重推理2、非lora权重推理3、量化权重推理4、实验总结 二、初始化模型三、模型推理四、完整代码Demo 前言 我在第七节介绍了cli.py推理源码解读&#xff0c;而我也因项目需要构建了推理demo&#xff0c;我们是用来自动生成标签和推理…

Rust Vec<T> 集合使用教程

Rust Vec 集合使用教程 文章目录 Rust Vec<T> 集合使用教程1. 创建和初始化 Vec<T>代码示例运行结果 2. 访问和修改 Vec<T> 中的元素代码示例运行结果 3. 添加和删除 Vec<T> 中的元素代码示例运行结果 4. 遍历 Vec<T>代码示例运行结果 5. 使用 V…

web服务的部署及高级优化

搭建web服务器 1.1、配置主机IP以及软件仓库搭建 [rootserver129 ~]# vmset.sh 100 //主机IP配置为172.25.254.100 1.2、查看搭建web服务器所需的软件包 [rootserver100 ~]# dnf search nginx 名称 精准匹配&#xff1a;nginx nginx.x86_64 : A high performance web serve…

头歌实践教学平台:CG7-v2.0-实体消隐

第1关&#xff1a;立方体消隐 一. 任务描述 1. 本关任务 (1) 理解深度缓冲器算法(Z-Buffer)算法; (2) 将triangle函数和main函数中的空白部分补充完整。 2. 输入 (1) 代码将自动输入一个边长为1的obj正方体模型&#xff0c;具体模型如下图&#xff1a; (2) 代码会自动对将…

Kafka Exactly Once 语义实现原理:幂等性与事务消息

01 前言 在现代分布式系统中&#xff0c;确保数据处理的准确性和一致性是至关重要的。Apache Kafka&#xff0c;作为一个广泛使用的流处理平台&#xff0c;提供了强大的消息队列和流处理功能。随着业务需求的增长&#xff0c;Kafka 的事务消息功能应运而生&#xff0c;它允许应…

力扣279完全平方数

力扣279完全平方数 给你一个整数 n &#xff0c;返回 和为 n 的完全平方数的最少数量 。 完全平方数 是一个整数&#xff0c;其值等于另一个整数的平方&#xff1b;换句话说&#xff0c;其值等于一个整数自乘的积。例如&#xff0c;1、4、9 和 16 都是完全平方数&#xff0c;…

【OceanBase诊断调优】—— OceanBase 数据库日志解读

适用版本&#xff1a;V2.1.x、V2.2.x、V3.1.x、V3.2.x observer.log 日志 OBServer 启动日志 搜索关键字&#xff1a; [NOTICE] 日志说明&#xff1a; OBServer 启动过程中比较关键的日志信息。 [2023-05-11 14:19:09.703272] INFO [SERVER] ob_server.cpp:533 [95303][0]…

单链表的经典oj题(1)

前言 这次博客将要以图解的形式&#xff0c;把单链表的经典题目&#xff0c;讲解&#xff0c;绝对是干货&#xff0c;来吧兄弟萌 第一题 给你一个链表的头节点 head 和一个整数 val &#xff0c;请你删除链表中所有满足 Node.val val 的节点&#xff0c;并返回 新的头节点 …

USB HID报告描述符学习

参考资料 HID 报告描述符 (qq.com)https://mp.weixin.qq.com/s?__bizMzU1ODI3MzQ1MA&mid2247485748&idx1&sn112bd8014eb96b03308b3b808549e8d4&chksmfc284ff1cb5fc6e770c2d2ece46c17bf2529901b45a357938978fa62163723556ad497b05c47&cur_album_id3340417…

力扣经典150题第四十七题:汇总区间

目录 题目描述和要求示例解释解题思路算法实现复杂度分析测试和验证总结和拓展参考资料 题目描述和要求 给定一个无重复元素的有序整数数组 nums&#xff0c;要求返回恰好覆盖数组中所有数字的最小有序区间范围列表。即&#xff0c;nums 的每个元素都恰好被某个区间范围所覆盖…

三、VLAN间路由(三层交换)

VLAN间路由可以通过二层交换机配合路由器来实现&#xff0c;也可以通过三层交换机来实现。 目录 1.单臂路由 2.通过三层交换机实现不同vlan的互访 1.单臂路由 注&#xff1a; 1.三层接口不能正确识别带vlan tag的数据帧 2.所有子接口与主接口共享MAC地址 命令 int g0/0/0.1…

Tom与Locust的渐入佳境

本书 第一章&#xff1a;Tom的Locust压力测试之旅 第二章&#xff1a;意外的挑战&#xff1a;系统性能问题的出现 第三章&#xff1a;高手相助&#xff1a;遇见性能测试专家 第四章&#xff1a;Locust初探&#xff1a;探寻压力测试工具 第五章&#xff1a;脚本编写&#xff1a…

Java Spring 中 Bean 的作用域(Scope)

在 Java Spring 框架中&#xff0c;Bean 的作用域&#xff08;Scope&#xff09;定义了 Bean 的生命周期以及其在 Spring 容器中的可见性。Spring 提供了几种不同的 Bean 作用域&#xff0c;以满足不同的应用需求。以下是 Spring 中主要的 Bean 作用域及其详细解释&#xff1a;…

试用了三个Ai音乐工具,我的偶像河图要完蛋了

试了三个生成音乐的ai工具&#xff0c;分别是爆火的suno,后期新秀udio&#xff0c;还有我们国内的天工。 先说感受&#xff0c;suno和天工我觉得稍微靠前&#xff0c;udio可能我的配置风格有问题&#xff0c;啪啪啪连选了好几个风格&#xff0c;生成的东西有点怪。 我随手写了…

语音识别的基本概念

语音识别的基本概念​​​​​​​ ​​​​​​​ 言语是一种复杂的现象。人们很少了解它是如何产生和感知的。天真的想法常常是语音是由单词构成的&#xff0c;而每个单词又由音素组成。不幸的是&#xff0c;现实却大不相同。语音是一个动态过程&#xff0c;没有明确区分的…

linux学习:线程安全(信号量+互斥锁读写锁+条件变量+可重入函数)

目录 信号量 有名信号量 步骤 api 创建、打开一个POSIX有名信号量 对 POSIX 有名信号量进行 P、V 操作 关闭、删除 POSIX 有名信号量 例子 无名信号量 步骤 api 初始化、销毁 POSIX 无名信号量 互斥锁读写锁 例子 两条线程 使用互斥锁来互斥地访问标准输出 在加锁…

算法人生(12):从“优先级队列算法”到“”六点优先工作法”

算法思想和生活中很多解决问题的思想有着异曲同工之妙&#xff0c;让我们来看下今天的“优先级队列算法”可以怎么应用到我们的生活中吧&#xff01; 优先级队列算法&#xff08;Priority Queue Algorithm&#xff09; 是一种特殊的数据结构&#xff0c;它在常规队列秉持着“先…