Java中JSON把引用相同的对象变为"$ref":问题的分析与解决

2023-11-17

Java中JSON把引用相同的对象变为"$ref":问题的分析与解决

后台返回给前端的数据一般是JSON格式的,使用com.alibaba.fastjson时,在把后台的响应数据转化为JSON格式时,具有相同引用的对象会变成" r e f " : " ref": " ref":".list[0]",导致前端解析出现错误。

问题重现

先定义一个类People
People有四个变量,分别是编号、姓名、年龄和喜欢的人,其中变量喜欢的人是一个对象(People类的对象)

public class People {
	public Integer id;
	public String name;
	public Integer age;
	public People like;
	
	public People(Integer id,String name,Integer age,People like) {
		this.id = id;
		this.name = name;
		this.age = age;
		this.like = like;
	}
	public void show() {
		System.out.println("name:"+this.name);
		System.out.println("   id:"+this.id);
		System.out.println("   age:"+this.age);
		if(this.like != null) {
			System.out.println("   like:"+this.like.name+"\n");
		} else {System.out.println();}
	}
	}
	
	public static void main(String[] args) {
	}
}

创建一个Map,然后添加数据
在主方法中加入下面的代码

		People p1 = new People(1,"张三",11,null);
		People p2 = new People(2,"李四",12,p1);
		People p3 = new People(3,"王五",13,p1);
	    Map<String,People> map = new HashMap<String, People>(); 
	    
	    map.put(p1.name,p1);
	    map.put(p2.name,p2);
	    map.put(p3.name,p3);
	    map.put("宋六",p3);
	    
	    map.get(p1.name).show();
	    map.get(p2.name).show();
	    map.get(p3.name).show();
	    map.get("宋六").show();
	    System.out.println(map);
	    System.out.println(p2.like+" | "+p3.like);

运行结果如下

name:张三
   id:1
   age:11

name:李四
   id:2
   age:12
   like:张三

name:王五
   id:3
   age:13
   like:张三

name:王五
   id:3
   age:13
   like:张三
{李四=lin.People@15db9742, 张三=lin.People@6d06d69c, 王五=lin.People@7852e922, 宋六=lin.People@7852e922}
lin.People@6d06d69c | lin.People@6d06d69c

王五和宋六是相同的对象,所以他们的引用地址也是完全相同的

李四和王五的like是相同的,所以他们的like的引用地址也是完全相同的

现在我们把map转为JSON

String text = JSON.toJSONString(map);
JSONObject json = JSONObject.parseObject(text);
System.out.println(json);

结果如下:

{"李四":
   {"like":{"name":"张三","id":1,"age":11},
   "name":"李四",
   "id":2,
   "age":12
   },
 "张三":
   {"$ref":"$.李四.like"},
 "王五":
   {"like":{"$ref":"$.李四.like"},
   "name":"王五",
   "id":3,
   "age":13},
 "宋六":
   {"$ref":"$.王五"}
}

可以看到,具有相同引用的对象第二次出现时,会变成"$ref":+第一次出现的位置
我们先分析一下原因

分析

出现这个问题的原因是fastjson有循环引用检测机制
循环引用:

  • 当一个对象包含另一个对象时,fastjson就会把该对象解析成引用
  • 当一个对象和另一个对象完全相同时,fastjson同样会把该对象解析成引用

解决方法

1.禁止循环引用检测

我们在组装返回给前端的数据时,如果需要多次使用相同的对象,可以在组装数据时禁止fastjson的循环引用检测,禁用后,fastjson就不会把相同的对象解析为引用了
使用DisableCircularReferenceDetect来禁止循环引用检测

		People p1 = new People(1,"张三",11,null);
		
		//需要第二次使用对象p1
		String text1 = JSON.toJSONString(p1,SerializerFeature.DisableCircularReferenceDetect);
		People p4 = JSON.parseObject(text1, People.class);
		People p2 = new People(2,"李四",12,p4);
		//需要第三次使用对象p1
		String text2 = JSON.toJSONString(p1,SerializerFeature.DisableCircularReferenceDetect);
		People p5 = JSON.parseObject(text2, People.class);
		People p3 = new People(3,"王五",13,p5);
		
	    Map<String,People> map = new HashMap<String, People>(); 
	    map.put(p1.name,p1);
	    map.put(p2.name,p2);
	    map.put(p3.name,p3);
	    //需要第二次使用对象p2
	    String text3 = JSON.toJSONString(p2,SerializerFeature.DisableCircularReferenceDetect);
		People p6 = JSON.parseObject(text3, People.class);
		map.put("宋六",p6);
		 
		System.out.println("p2的like:"+p2.like+" | "+"p3的like:"+p3.like);
		System.out.println("map中的李四:"+map.get("李四")+" | "+"map中的宋六:"+map.get("宋六"));
		System.out.println("map中的李四的like:"+map.get("李四").like+" | "+"map中的宋六的like:"+map.get("宋六").like);
	    String text4 = JSON.toJSONString(map);
	    JSONObject json = JSONObject.parseObject(text4);
	    System.out.println(json);

现在我们再运行一下代码,看一下组装的map

p2的like:lin.People@6438a396 | p3的like:lin.People@e2144e4
map中的李四:lin.People@6477463f | map中的宋六:lin.People@3d71d552
map中的李四的like:lin.People@6438a396 | map中的宋六的like:lin.People@1cf4f579
{"李四":{"like":{"name":"张三","id":1,"age":11},"name":"李四","id":2,"age":12},"张三":{"name":"张三","id":1,"age":11},"王五":{"like":{"name":"张三","id":1,"age":11},"name":"王五","id":3,"age":13},"宋六":{"like":{"name":"张三","id":1,"age":11},"name":"李四","id":2,"age":12}}

可以看到问题已经解决
——

2.深度复制

深度复制和禁止循环引用检测的原理是相似的,都是先创建一个全新的对象,再使用它
我们先添加一个深度复制的公共方法,该方法使用序列化来实现深度复制(深度拷贝)
我们先让People类实现Serializable接口

public class People implements Serializable

然后添加下面的静态方法

	@SuppressWarnings("unchecked")
	public static <T extends Serializable> T clone(T obj){
        T cloneObj = null;
        try {
            //写入字节流
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            ObjectOutputStream obs = new ObjectOutputStream(out);
            obs.writeObject(obj);
            obs.close();
            //分配内存,写入原始对象,生成新对象
            ByteArrayInputStream ios = new ByteArrayInputStream(out.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(ios);
            //返回生成的新对象
            cloneObj = (T) ois.readObject();
            ois.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return cloneObj;
    }

然后修改主方法

		People p1 = new People(1,"张三",11,null);
		
		//需要第二次使用对象p1
		People p2 = new People(2,"李四",12,(People)clone(p1));
		//需要第三次使用对象p1
		People p3 = new People(3,"王五",13,(People)clone(p1));
		
	    Map<String,People> map = new HashMap<String, People>(); 
	    map.put(p1.name,p1);
	    map.put(p2.name,p2);
	    map.put(p3.name,p3);
	    //需要第二次使用对象p2
		map.put("宋六",(People)clone(p2));
		 
		System.out.println("p2的like:"+p2.like+" | "+"p3的like:"+p3.like);
		System.out.println("map中的李四:"+map.get("李四")+" | "+"map中的宋六:"+map.get("宋六"));
		System.out.println("map中的李四的like:"+map.get("李四").like+" | "+"map中的宋六的like:"+map.get("宋六").like);
	    String text4 = JSON.toJSONString(map);
	    JSONObject json = JSONObject.parseObject(text4);
	    System.out.println(json);

运行主方法

p2的like:lin.People@58372a00 | p3的like:lin.People@5fd0d5ae
map中的李四:lin.People@7ba4f24f | map中的宋六:lin.People@2d98a335
map中的李四的like:lin.People@58372a00 | map中的宋六的like:lin.People@16b98e56
{"李四":{"like":{"name":"张三","id":1,"age":11},"name":"李四","id":2,"age":12},"张三":{"name":"张三","id":1,"age":11},"王五":{"like":{"name":"张三","id":1,"age":11},"name":"王五","id":3,"age":13},"宋六":{"like":{"name":"张三","id":1,"age":11},"name":"李四","id":2,"age":12}}

可以看到问题已经解决
——

最后粘上所有的代码

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;

public class People implements Serializable{
	private static final long serialVersionUID = 1L;
	public Integer id;
	public String name;
	public Integer age;
	public People like;
	
	public People(Integer id,String name,Integer age,People like) {
		this.id = id;
		this.name = name;
		this.age = age;
		this.like = like;
	}
	public void show() {
		System.out.println("name:"+this.name);
		System.out.println("   id:"+this.id);
		System.out.println("   age:"+this.age);
		if(this.like != null) {
			System.out.println("   like:"+this.like.name+"\n");
		} else {System.out.println();}
	}
	@SuppressWarnings("unchecked")
	public static <T extends Serializable> T clone(T obj){
        T cloneObj = null;
        try {
            //写入字节流
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            ObjectOutputStream obs = new ObjectOutputStream(out);
            obs.writeObject(obj);
            obs.close();
            //分配内存,写入原始对象,生成新对象
            ByteArrayInputStream ios = new ByteArrayInputStream(out.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(ios);
            //返回生成的新对象
            cloneObj = (T) ois.readObject();
            ois.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return cloneObj;
    }
	
	public static void main(String[] args) {
		People p1 = new People(1,"张三",11,null);
		
		//需要第二次使用对象p1
		People p2 = new People(2,"李四",12,(People)clone(p1));
		//需要第三次使用对象p1
		People p3 = new People(3,"王五",13,(People)clone(p1));
		
	    Map<String,People> map = new HashMap<String, People>(); 
	    map.put(p1.name,p1);
	    map.put(p2.name,p2);
	    map.put(p3.name,p3);
	    //需要第二次使用对象p2
		map.put("宋六",(People)clone(p2));
		 
		System.out.println("p2的like:"+p2.like+" | "+"p3的like:"+p3.like);
		System.out.println("map中的李四:"+map.get("李四")+" | "+"map中的宋六:"+map.get("宋六"));
		System.out.println("map中的李四的like:"+map.get("李四").like+" | "+"map中的宋六的like:"+map.get("宋六").like);
	    String text4 = JSON.toJSONString(map);
	    JSONObject json = JSONObject.parseObject(text4);
	    System.out.println(json); 
	}
}


本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Java中JSON把引用相同的对象变为"$ref":问题的分析与解决 的相关文章

  • 使用cameltestsupport进行Camel单元测试,模板始终为空

    我正在用 Camel 做一个简单的单元测试 我想做的就是从文件 在资源下 读取 JSON 内容 将其发送到 Java 类进行验证 这是我试图测试的路线 无论我做什么 模板 我用来发送正文 json 始终为空 这是我的代码 public cl
  • 如何在 JSP 中导入类?

    我是一个完全的JSP初学者 我正在尝试使用java util List在 JSP 页面中 我需要做什么才能使用除以下类之外的类java lang 使用以下导入语句进行导入java util List 顺便说一句 要导入多个类 请使用以下格式
  • Condition 接口中的 signalAll 与对象中的 notificationAll

    1 昨天我才问过这个问题条件与等待通知机制 https stackoverflow com questions 10395571 condition vs wait notify mechanism 2 我想编辑相同的内容并在我的问题中添加
  • 如何从 Retrofit2 获取字符串响应?

    我正在做 android 正在寻找一种方法来执行超级基本的 http GET POST 请求 我不断收到错误 java lang IllegalArgumentException Unable to create converter for
  • 如何让spring为JdbcMetadataStore创建相应的schema?

    我想使用此处描述的 jdbc 元数据存储 https docs spring io spring integration docs 5 2 0 BUILD SNAPSHOT reference html jdbc html jdbc met
  • 在java中实现你自己的阻塞队列

    我知道这个问题之前已经被问过并回答过很多次了 但我只是无法根据互联网上找到的示例找出窍门 例如this http tutorials jenkov com java concurrency blocking queues html or t
  • 列表应该如何转换为具体的实现?

    假设我正在使用一个我不知道源代码的库 它有一个返回列表的方法 如下所示 public List
  • Git 无法识别重命名和修改的包文件

    我有一个名为的java文件package old myfile java 我已经通过 git 提交了这个文件 然后我将我的包重命名为new所以我的文件在package new myfile java 我现在想将此文件重命名 和内容更改 提交
  • 如何将 Jfreechart(饼图)添加到 netbeans 的面板中

    我正在使用 netbeans gui 编辑器 并且正在尝试添加一个本身位于内部框架中的 Jfreechart 并且这个内部框架我想将其添加到面板中 正如您在此图中看到的那样 抱歉 我无法直接发布图像 因为我新手 http www flick
  • 使用 json_encode() 函数在 PHP 数组中生成 JSON 键值对

    我正在尝试以特定语法获取 JSON 输出 这是我的代码 ss array 1 jpg 2 jpg dates array eu gt 59 99 us gt 39 99 array1 array name gt game1 publishe
  • JSON 数组到 C# 列表

    如何将这个简单的 JSON 字符串反序列化为 C 中的列表 on4ThnU7 n71YZYVKD CVfSpM2W 10kQotV 这样 List
  • 如何通过 Inno Setup for NetBeans 使用自定义 .iss 文件

    我将 Inno Setup 5 与 NetBeans 8 一起使用 并且我已经能够创建一个安装程序来安装该应用程序C users username local appname 但是我希望将其安装在C Programfiles 我如何在 Ne
  • 将图像添加到自定义 AlertDialog

    我制作了一个 AlertDialog 让用户可以从我显示的 4 个选项中选择一个 前 3 个让他们在单击号码时直接拨打号码 第 4 个显示不同的视图 现在看起来是这样的 由于第四个选项的目的是不同的任务 我想让它看起来不同 因为用户可能会感
  • 如何在 Quartz 调度程序中每 25 秒运行一次?

    我正在使用 Java 的 Quartz Scheduling API 你能帮我使用 cron 表达式每 25 秒运行一次吗 这只是一个延迟 它不必总是从第 0 秒开始 例如 序列如下 0 00 0 25 0 50 1 15 1 40 2 0
  • 挂钩 Eclipse 构建过程吗?

    我希望在 Eclipse 中按下构建按钮时能够运行一个简单的 Java 程序 目前 当我单击 构建 时 它会运行一些 JRebel 日志记录代码 我有一个程序可以解析 JRebel 日志文件并将统计信息存储在数据库中 是否可以编写一个插件或
  • Java:多线程内的 XA 事务传播

    我如何使用事务管理器 例如Bitronix http docs codehaus org display BTM Home JBoss TS http www jboss org jbosstm or Atomikos http www a
  • 在android中跟踪FTP上传数据?

    我有一个运行 Android 的 FTP 系统 但我希望能够在上传时跟踪字节 这样我就可以在上传过程中更新进度条 安卓可以实现这个功能吗 现在 我正在使用org apache common net ftp我正在使用的代码如下 另外 我在 A
  • 嵌入式 Jetty - 以编程方式添加基于表单的身份验证

    有没有一种方法可以按如下方式以编程方式添加基于表单的身份验证 我用的是我自己的LdapLoginModule 最初我使用基本身份验证并且工作正常 但现在我想在登录页面上进行更多控制 例如显示徽标等 有没有好的样品 我正在使用嵌入式 jett
  • JAXB - 列表<可序列化>?

    我使用 xjc 制作了一些课程 public class MyType XmlElementRefs XmlElementRef name MyInnerType type JAXBElement class required false
  • 在哪里存储 Java 的 .properties 文件?

    The Java教程 http download oracle com javase tutorial essential environment properties htmlon using Properties 讨论如何使用 Prop

随机推荐

  • 阿里云域名注册流程(全流程详细教程)

    阿里云域名注册流程很简单 先注册阿里云账号 账号必须通过实名认证 然后创建信息模版 个人或企业信息模板必须通过实名认证 然后想好域名名称和域名后缀 最后在阿里云域名注册官网进行新域名的注册 阿里云百科来详细说下阿里云域名注册流程 域名注册官
  • java 遍历String[]的常用两种方法

    初学java 在此记录 public static void main String args String str new String 3 for int i 0 i lt str length i str i i i 方法一 for循
  • Python3之爬虫----retrying模块的使用和处理cookie相关的请求

    1 1 设使用超时参数 requests get url headers headers timeout 3 设置超时参数 若url在三秒内未得到响应 报错 1 2 retrying模块的使用 第三方模块 from retrying imp
  • Tank大战游戏模拟(java版)

    Java小项目 坦克大战 1 任务分析 玩家进入游戏 通过操纵坦克来守卫基地 摧毁全部敌方坦克来取得胜利 如果基地被摧毁 或者玩家坦克被摧毁 则判定游戏失败 2 具体分析 图形用户界面使用GUI技术实现 游戏中坦克的方向转动可以通过四种不同
  • node.js 详解

    目录 一 初始node js 1 为什么 JavaScript 可以在浏览器中被执行 2 node js 简介 3 node js 查看是否安装 4 运行文件 1 在终端中输入 node 文件 2 终端中的快捷键 二 fs 文件系统模块 1
  • Python爬虫学习笔记(十)————Scrapy

    目录 1 scrapy是什么 2 安装scrapy 3 scrapy项目的创建以及运行 1 创建scrapy项目 2 项目组成 3 创建爬虫文件 跳转到spiders文件夹中去创建爬虫文件 scrapy genspider爬虫文件的名字 网
  • SSL证书的作用,可以帮助网站达到什么样的效果

    近年来 企业建站非常普遍 出现了各类网站 随着互联网的发展 网络安全威胁事件也频繁发生 网站入侵 钓鱼网站等这些问题 不仅容易影响了企业的声誉 也会造成了用户的损失 那么如何保护网站数据的安全 让用户甄别出真假网站 我们可以使用SSL证书来
  • Blender建模汇总

    好消息是UE4官方正在完善UE4和Blender之间的工作流 在此之前还是建议扎实打好基础 建模方法基本上是之前总结的 利用插件或利用面片的方式 区别在于资源管理 不再滥用表面细分修改器 1 光剑 未应用修改器 可用于游戏模型 应用修改器后
  • 模块 ““element-plus““ 没有导出的成员 xxx。你是想改用 “import xxx from “element-plus““ 吗?

    项目场景 项目 vue3 ts element plus 很多时候导入element plus中某个api时 总是提示 模块 element plus 没有导出的成员 xxx 你是想改用 import xxx from element pl
  • 头歌:图像识别案例在线实验闯关

    第2关 基于全像素特征的手写体图像识别模型 任务描述 对图像像素数据集和图像标签数据集 按80 训练和20 测试进行随机划分 构建支持向量机分类模型 输出模型的准确率和测试集的预测准确率 编程要求 根据提示 在右侧编辑器补充代码 按照任务要
  • 解决Python中的PermissionError: Permission denied问题

    解决Python中的PermissionError Permission denied问题 在Python编程中 我们时常会遇到PermissionError Permission denied异常错误 这个错误通常出现在我们尝试访问或修改
  • Electron应用图标的配置与生成

    技术选型 1 electron 21 3 3 2 electron vite 1 0 17 3 vue 3 2 45 4 element plus 2 2 32 背景 默认情况下我们使用electron开发的应用使用的图标有四种类型 软件的
  • 【Python】基础内容

    简介 面向对象 解释型的编程语言 使用缩进作为逻辑层次 运行效率较低 单行注释 以 开头 注释内容 多行注释 以一对三个双引号引起来的内容 注释内容 数据类型 type 被查看类型的数据 查看数据类型 数字 Number 整数 int Py
  • java集合UML类图 总览

    Java集合框架主要包括两种类型的容器 一种是集合 Collection 存储一个元素集合 Collection 接口又有 3 种子类型 List Set 和 Queue 另一种是图 Map 存储键 值对映射 Map 接口又有 Abstra
  • unity开发android游戏(一)搭建Unity安卓开发环境

    1 下载安装Java的JDK http www oracle com technetwork java javase downloads index html JDK中 包含JRE 如果是64位的系统 推荐安装64位的java 2 下载An
  • Maven本地仓库有jar包却提示找不到 / 生成.lastUpdated文件

    Maven本地仓库有jar包却提示找不到 生成 lastUpdated文件 Maven仓库 remote repositories文件的作用 存在的问题 使用Maven管理项目时 如果连不到远程仓库 但是明明本地仓库中有对应的jar包 此时
  • 解决AttributeError: module ‘cv2‘ has no attribute ‘CV_HAAR_SCALE_IMAGE‘

    解决AttributeError module cv2 has no attribute CV HAAR SCALE IMAGE 问题描述 代码 frontalFaces faceCascade detectMultiScale image
  • Array.fill()用法

    Arrays fill 用于快速填充数组 但是只适用于一维数组 若是想填充二维数组则需要循环 详细用法 Arrays fill int a from to int var int a 需要填充的数组 from 数组填充的起始位置 包括此位置
  • Python类的构造方法深入剖析:详解与案例分析

    在Python中 类是面向对象编程的重要概念之一 类是对象的蓝图 通过定义类可以创建具有相同属性和方法的多个对象 类中的构造方法 init 方法 在对象创建时被调用 用于初始化对象的属性 本文将深入剖析Python类的构造方法 并通过案例分
  • Java中JSON把引用相同的对象变为"$ref":问题的分析与解决

    Java中JSON把引用相同的对象变为 ref 问题的分析与解决 后台返回给前端的数据一般是JSON格式的 使用com alibaba fastjson时 在把后台的响应数据转化为JSON格式时 具有相同引用的对象会变成 r e f