JSON -- 深拷贝的另类实现

686 阅读3分钟

在开发时,遇到这样一个问题,问题如下:

原始数据如下,

JSONObject jo = new JSONObject();
jo.put("head", "This is ms");
JSONArray ja = new JSONArray();
Student student = new Student(1, "ms", null, new Date(), new Date());
ja.add(student);
jo.put("body", ja);

image.png

需求如下

不改变当前对象的前提下,用它克隆出一个名叫 lm/lx 的学生,当然数据量很大,所以循环去重新给每个键赋值不现实,且对象层层嵌套,还有多个长数组,

如果你有一定的基础,就会知道,如果我们直接按如下操作的话,会导致原数据跟着发生改变

JSONObject lm = new JSONObject();
// 赋值,相当于clone
lm.putAll(jo);
lm.put("head","this is lm");
JSONArray lmbody = lm.getJSONArray("body");
lmbody.forEach((stu) -> {
    Student stu1 = (Student) stu;
    stu1.setName("lm");
});

查看输出

System.out.println(lm.toString());
// {"head":"this is lm","body":[{"birth":1650962965320,"id":1,"name":"lm","workStart":1650962965320}]}
System.out.println(jo.toString());3
// {"head":"This is ms","body":[{"birth":1650962965320,"id":1,"name":"lm","workStart":1650962965320}]}

// 这里head不一样是因为head是不共享地址的,因为他们是临时声明的

这是因为,两对象虽然有自己的地址,但是为了节省空间,他们再赋值时,将对象的地址赋给了新对象,所以改变新对象的值时,源数据也会跟随发生改变。

它们之间的关系就类似下面这样

image.png

所以不管你修改哪个JSONObject对象,另外一个都会跟着发生改变,这也叫浅拷贝,所以目前问题就是,如何复制一份源数据,且不引用源数据地址,即如何实现JSONObject的深拷贝。

解决方案如下

  • 递归复制,这也是最普遍的深拷贝的实现方式,很麻烦。

  • 先将JSONObject转换成String,再new一个String,然后将新的String转换成JSONObject,在修改即可

其实在百度的时候,发现js中的Json也是通过类似的方式实现的(stringify)。

具体实现

// 先将JSONObject转换成String
String s = jo.toString();

JSONObject newjo = JSONObject.parseObject(s);
newjo.put("head","this is lm");
JSONArray body = newjo.getJSONArray("body");
body.forEach((stu) -> {
    JSONObject object = ((JSONObject) stu);
    object.put("name","lm");
});

输出查看

System.out.println(newjo.toString());
// {"head":"this is lm","body":[{"workStart":1650961569176,"name":"lm","birth":1650961569176,"id":1}]}

System.out.println(jo.toString());
{"head":"This is ms","body":[{"birth":1650961569176,"id":1,"name":"lm","workStart":1650961569176}]}

你以为这就完事了嘛?其实也确实差不多,如果你的数据没有涉及以下数据类型的话。

  • 时间 在JSONObject转化成String后,时间类型的数据会转化成时间戳
  • null 在JSONObject转化成String后,null值的key会消失的哦,但是这不影响取值,因为你按照空值的key取值取到的仍然是null,

如下实例

JSONObject jo = new JSONObject();
jo.put("head", "This is ms");
jo.put("time",new Date());
jo.put("null值",null);

// 打印查看
{"head":"This is ms","time":1650963827472}

静态类Student

static final class Student {
    private Integer id;
    private String name;
    private String sex;
    private Date birth;
    private Date workStart;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public Date getBirth() {
        return birth;
    }

    public void setBirth(Date birth) {
        this.birth = birth;
    }

    public Date getWorkStart() {
        return workStart;
    }

    public void setWorkStart(Date workStart) {
        this.workStart = workStart;
    }

    public Student(Integer id, String name, String sex, Date birth, Date workStart) {
        this.id = id;
        this.name = name;
        this.sex = sex;
        this.birth = birth;
        this.workStart = workStart;
    }
}