Подтвердить что ты не робот

Пользовательская сериализация 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

Как я могу это сделать?

4b9b3361

Ответ 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 и добавить его в свой путь к классам. Да, вам придется делать это снова, когда вы обновляете библиотеку, но это может быть лучше, чем альтернатива...