Пользовательская сериализация Java
У меня есть объект, содержащий несколько несериализуемых полей, которые я хочу сериализовать. Они из отдельного API, который я не могу изменить, поэтому сделать их Serializable не является вариантом. Основная проблема - класс Location. Он содержит четыре вещи, которые могут быть сериализованы, которые мне нужны, все ints. Как я могу использовать read/writeObject для создания настраиваемого метода сериализации, который может сделать что-то вроде этого:
// writeObject:
List<Integer> loc = new ArrayList<Integer>();
loc.add(location.x);
loc.add(location.y);
loc.add(location.z);
loc.add(location.uid);
// ... serialization code
// readObject:
List<Integer> loc = deserialize(); // Replace with real deserialization
location = new Location(loc.get(0), loc.get(1), loc.get(2), loc.get(3));
// ... more code
Как я могу это сделать?
Ответы
Ответ 1
Java поддерживает Пользовательскую сериализацию. Прочтите раздел Настройка протокола по умолчанию.
Цитата:
Однако есть странное, но коварное решение. Используя встроенный функции механизма сериализации, разработчики могут нормальный процесс, предоставляя два метода внутри своих файлов классов. Эти методы:
-
- private void writeObject (ObjectOutputStream out) бросает IOException;
- private void readObject (ObjectInputStream in) бросает IOException, ClassNotFoundException;
В этом методе, что вы можете сделать, это сериализовать его в другие формы, если вам нужно, например, ArrayList для местоположения, которое вы проиллюстрировали, или JSON или другой формат данных/метод и восстановить его на readObject()
В вашем примере вы добавляете следующий код:
private void writeObject(ObjectOutputStream oos)
throws IOException {
// default serialization
oos.defaultWriteObject();
// write the object
List loc = new ArrayList();
loc.add(location.x);
loc.add(location.y);
loc.add(location.z);
loc.add(location.uid);
oos.writeObject(loc);
}
private void readObject(ObjectInputStream ois)
throws ClassNotFoundException, IOException {
// default deserialization
ois.defaultReadObject();
List loc = (List)ois.readObject(); // Replace with real deserialization
location = new Location(loc.get(0), loc.get(1), loc.get(2), loc.get(3));
// ... more code
}
Ответ 2
Аналогично @momo, но без использования значений List и auto-boxed int, которые сделают его более компактным.
private void writeObject(ObjectOutputStream oos) throws IOException {
// default serialization
oos.defaultWriteObject();
// write the object
oos.writeInt(location.x);
oos.writeInt(location.y);
oos.writeInt(location.z);
oos.writeInt(location.uid);
}
private void readObject(ObjectInputStream ois) throws ClassNotFoundException, IOException {
// default deserialization
ois.defaultReadObject();
location = new Location(ois.readInt(), ois.readInt(), ois.readInt(), ois.readInt());
// ... more code
}
Ответ 3
Если это должна быть сериализация Java, единственный способ, которым я знаю, - переопределить readObject()
и writeObject()
во всех классах, имеющих ссылку на экземпляр Location
, как показано в ответе Momo. Обратите внимание, что это не позволит вам сериализовать Location[]
и требует, чтобы вы подклассифицировали все Collection<Location>
, появляющиеся в вашем коде. Более того, для этого требуется, чтобы поля типа Location
были отмечены переходными процессами, что исключает возможность их определения записывать в поток сериализации, что может препятствовать обнаружению несовместимых изменений класса.
Лучше всего было бы просто переопределить ObjectOutputStream.writeObject
. Увы, этот метод final
. Вместо этого вы можете переопределить ObjectOutputStream.writeObjectOverride()
, но этот метод не может делегировать реализацию по умолчанию, ObjectOutputStream.writeObject0()
, потому что этот метод private
. Конечно, вы можете вызвать частный метод, используя отражение, но...
Поэтому я рекомендую проверить ваши ограничения. Должна ли быть сериализация Java? Можете ли вы действительно не изменить определение класса Location
?
Если у вас есть исходный код для класса Location
, довольно просто добавить implements Serializable
и добавить его в свой путь к классам. Да, вам придется делать это снова, когда вы обновляете библиотеку, но это может быть лучше, чем альтернатива...