Узлы нельзя сериализовать (за исключением, возможно, пользовательских, которые вы реализовали сами), слишком много внутренних состояний, которые было бы слишком сложно восстановить. Отсутствие возможности добавления методов / интерфейсов к узлам (без их расширения) делает невозможным добавление интерфейса Serializable
и методов для сохранения тех частей данных, которые необходимы для восстановления состояния и правильного чтения этих данных.
Лучше всего создать сериализуемый класс-обертку, который позволит вам восстановить свойства, которые вам действительно интересны. Имхо, лучше не пытаться сериализовать узлы; Создайте новый узел при загрузке данных и заполните его загруженными данными.
В следующем примере показано, как вы можете сделать это с TreeItem<? extends Serializable>
; Отсутствуют такие данные, как расширенные свойства, но вы сможете восстановить свойство value
и дочерние элементы. (Реализация немного сложнее, чем необходимо для TreeItem
структур с небольшой глубиной, но на некоторых глубинах вам необходимо знать, что более простой рекурсивный подход может привести к StackOverflowError
с.)
В этом случае каждый элемент сериализуется путем записи числа дочерних элементов, его собственного значения свойства, а затем выполняется то же самое для каждого дочернего элемента. В результате получается последовательность пар int и value, которые можно использовать для восстановления данных:
public class TreeItemSerialisation {
public static void main(String[] args) throws IOException, ClassNotFoundException {
TreeItem<String> root = new TreeItem<>("root");
TreeItem<String> c1 = new TreeItem<>("root.1");
TreeItem<String> c3 = new TreeItem<>("root.3");
root.getChildren().addAll(c1, new TreeItem<>("root.2"), c3);
TreeItem<String> c3_1 = new TreeItem<>("root.3.1");
c3_1.getChildren().add(new TreeItem<>("root.3.1.1"));
c3.getChildren().add(c3_1);
c1.getChildren().addAll(new TreeItem<>("root.1.1"), new TreeItem<>("root.1.2"));
// serialize
ByteArrayOutputStream bos = new ByteArrayOutputStream();
try (ObjectOutputStream oos = new ObjectOutputStream(bos)) {
oos.writeObject(new TreeItemSerialisationWrapper(root));
}
// unserialize
TreeItem<String> root2;
try (ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray()))) {
root2 = (TreeItem<String>) ois.readObject();
}
// TODO do something with root2
}
}
public class TreeItemSerialisationWrapper<T extends Serializable> implements Serializable {
private static final long serialVersionUID = 1L;
private transient TreeItem<T> item;
public TreeItemSerialisationWrapper(TreeItem<T> item) {
if (item == null) {
throw new IllegalArgumentException();
}
this.item = item;
}
/**
* Custom way of writing the TreeItem structure
*/
private void writeObject(ObjectOutputStream out)
throws IOException {
Stack<TreeItem<T>> stack = new Stack<>();
stack.push(item);
out.defaultWriteObject();
do {
TreeItem<T> current = stack.pop();
int size = current.getChildren().size();
out.writeInt(size);
// write all the data that needs to be restored here
out.writeObject(current.getValue());
// "schedule" serialisation of children.
// the first one is inserted last, since the top one from the stack is
// retrieved first
for (int i = size - 1; i >= 0; --i) {
stack.push(current.getChildren().get(i));
}
} while (!stack.isEmpty());
}
/**
* happens before readResolve; recreates the TreeItem structure
*/
private void readObject(ObjectInputStream in)
throws IOException, ClassNotFoundException {
class Container {
int count;
final TreeItem<T> item;
Container(ObjectInputStream in) throws ClassNotFoundException, IOException {
// read the data for a single TreeItem here
this.count = in.readInt();
this.item = new TreeItem<>((T) in.readObject());
}
}
in.defaultReadObject();
Container root = new Container(in);
this.item = root.item;
if (root.count > 0) {
Stack<Container> stack = new Stack<>();
stack.push(root);
do {
Container current = stack.peek();
--current.count;
if (current.count <= 0) {
// we're done with this item
stack.pop();
}
Container newContainer = new Container(in);
current.item.getChildren().add(newContainer.item);
if (newContainer.count > 0) {
//schedule reading children of non-leaf
stack.push(newContainer);
}
} while(!stack.isEmpty());
}
}
/**
* We're not actually interested in this object but the treeitem
* @return the treeitem
* @throws ObjectStreamException
*/
private Object readResolve() throws ObjectStreamException {
return item;
}
}
Описание работы readObject
, readResolve
и writeObject
см. Джавадо c из Serializable