使用循环引用序列化对象

Serializing objects with circural references

我有 2 个简单的 classes,class 代表一个公共汽车站:

import java.util.List;

public class Stop {
    /**
     * lines operating this stop
     */
    public List<Line> lines;
    public String label;

    public Stop(String label, List<Line> lines) {
        this.label = label;
        this.lines = lines;
    }

    public String getLabel() {
        return label;
    }

    public List<Line> getLines() {
        return lines;
    }

    @Override
    public String toString() {
        return "Stop{" +
                "label='" + label + '\'' +
                '}';
    }
}

和一条class象征公交线路:

import java.util.ArrayList;
import java.util.List;

public class Line {
    public String label;
    public int id;
    public List<Stop> stops;

    public Line(String label, int id, List<Stop> stops) {
        this.label = label;
        this.id = id;
        this.stops = stops;
    }

    public Line(String label, int id) {
        this(label, id, new ArrayList<>());
        System.out.println("used second constructor");
    }

    public String getLabel() {
        return label;
    }

    public int getId() {
        return id;
    }

    public List<Stop> getStops() {
        return stops;
    }

    @Override
    public String toString() {
        return "Line{" +
                "label='" + label + '\'' +
                ", id=" + id +
                ", stops=" + stops +
                '}';
    }
}

我不想使用 java.beans.XMLEncoder 序列化这些,然后使用 XMLDecoder 反序列化。

在这种情况下(当行持有对其停止点的引用时,反之亦然),反序列化失败。解码器忽略带有附加 List<Stop> stops 参数的 Line 构造函数并使用 Line(String label, int id) 构造函数,留下此消息:

java.lang.IllegalStateException: Could not add argument to evaluated element
Continuing ...

当我完全删除 class Stop 中的操作行列表时,反序列化工作完美。

我做错了什么?

主要class:

import java.beans.DefaultPersistenceDelegate;
import java.beans.XMLDecoder;
import java.beans.XMLEncoder;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.util.ArrayList;

public class App {
    public static void main(String[] args) {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        try (XMLEncoder encoder = new XMLEncoder(baos)) {
            encoder.setPersistenceDelegate(Line.class, new DefaultPersistenceDelegate(new String[] {"label", "id", "stops"}));
            encoder.setPersistenceDelegate(Stop.class, new DefaultPersistenceDelegate(new String[] {"label", "lines"}));
            ArrayList<Stop> stops = new ArrayList<>();
            stops.add(new Stop("Porte Maillot", new ArrayList<>()));
            stops.add(new Stop("Auber", new ArrayList<>()));
            Line line = new Line("A", 0, stops);
            stops.get(0).lines.add(line);
            stops.get(1).lines.add(line);
            encoder.writeObject(line);
        }
        System.out.println("xml:");
        System.out.println(baos);
        try (XMLDecoder decoder = new XMLDecoder(new ByteArrayInputStream(baos.toByteArray()))){
            Line line = (Line) decoder.readObject();
            System.out.printf("line: %s\n", line);
        }
    }
}

控制台输出:

xml:
<?xml version="1.0" encoding="UTF-8"?>
<java version="15.0.1" class="java.beans.XMLDecoder">
 <object class="Line" id="Line0">
  <string>A</string>
  <int>0</int>
  <object class="java.util.ArrayList">
   <void method="add">
    <object class="Stop">
     <string>Porte Maillot</string>
     <object class="java.util.ArrayList">
      <void method="add">
       <object idref="Line0"/>
      </void>
     </object>
    </object>
   </void>
   <void method="add">
    <object class="Stop">
     <string>Auber</string>
     <object class="java.util.ArrayList">
      <void method="add">
       <object idref="Line0"/>
      </void>
     </object>
    </object>
   </void>
  </object>
 </object>
</java>

used second constructor
java.lang.IllegalStateException: Could not add argument to evaluated element
Continuing ...
line: Line{label='A', id=0, stops=[]}

循环引用导致 XMLDecoder 无法处理的循环序列化。使引用之一成为瞬态并在反序列化时恢复它们将解决问题,就像使用二进制序列化而不是 xml 一样。