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

探讨Java中使用fastjson处理具有相同引用对象的JSON转化问题,分析循环引用原因并提供禁用检测与深度复制两种解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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); 
	}
}


评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值