Сериализация Java с несериализуемыми частями

У меня есть:

class MyClass extends MyClass2 implements Serializable {
  //...
}

В MyClass2 есть свойство, которое нельзя сериализовать. Как я могу сериализовать (и десериализовать) этот объект?

Исправление: MyClass2 - это, конечно, не интерфейс, а класс.

Можете ли вы изменить MyClass2 или нет? Нужно ли сохранять значение несериализуемого свойства посредством сериализации? От этого зависит правильный подход.

erickson 18.09.2008 22:30

MyClass2 можно изменить. Однако было бы лучше не делать этого, так как я строю на нем и он может быть изменен другими.

Burkhard 19.09.2008 15:04
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
51
2
97 255
10
Перейти к ответу Данный вопрос помечен как решенный

Ответы 10

MyClass2 - это просто интерфейс, поэтому технически он не имеет свойств, только методы. При этом, если у вас есть переменные экземпляра, которые сами по себе не сериализуемы, единственный известный мне способ обойти это - объявить эти поля временными.

бывший:

private transient Foo foo;

Когда вы объявляете временное поле, оно будет проигнорировано в процессе сериализации и десериализации. Имейте в виду, что когда вы десериализуете объект с временным полем, значение этого поля всегда будет значением по умолчанию (обычно нулевым).

Обратите внимание, что вы также можете переопределить метод readResolve () вашего класса, чтобы инициализировать временные поля на основе другого состояния системы.

Вам нужно будет внедрить writeObject() и readObject() и выполнить сериализацию / десериализацию этих полей вручную. См. Подробности на странице javadoc для java.io.Serializable. В Эффективная Java Джоша Блоха также есть несколько хороших глав, посвященных реализации надежной и безопасной сериализации.

Вы можете начать с изучения ключевого слова преходящий, которое помечает поля как не являющиеся частью постоянного состояния объекта.

Если вы можете изменить MyClass2, самый простой способ решить эту проблему - объявить свойство временным.

Это не отвечает на вопрос - в вопросе указано, что MyClass2 содержит свойство, которое не может быть сериализовано, и в идеале MyClass2 не следует изменять.

Scott Bale 07.09.2012 22:20

Вы правы, я не очень внимательно прочитал вопрос, когда ответил на него. Я изменил ответ.

ykaganovich 07.09.2012 22:46

XStream - отличная библиотека для быстрой сериализации Java в XML для любого объекта, независимо от того, является ли он сериализуемым или нет. Даже если целевой формат XML вам не подходит, вы можете использовать исходный код, чтобы узнать, как это сделать.

привет, не могли бы вы объяснить, как использовать XStream с несериализуемым объектом?

SWAppDev 26.11.2015 16:33

Документация говорит сама за себя, руководство начинается с классов, которые не реализуют Serializable, и показывает, как их сериализовать: x-stream.github.io/tutorial.html

Boris Terzic 27.11.2015 18:00

Зависит от того, почему этот член MyClass2 не сериализуем.

Если есть веская причина, по которой MyClass2 не может быть представлен в сериализованной форме, то, скорее всего, та же причина применима к MyClass, поскольку это подкласс.

Можно написать настраиваемую сериализованную форму для MyClass путем реализации readObject и writeObject таким образом, чтобы состояние данных экземпляра MyClass2 в MyClass можно было соответствующим образом воссоздать из сериализованных данных. Это было бы подходящим вариантом, если API MyClass2 исправлен и вы не можете добавить Serializable.

Но сначала вы должны выяснить, почему MyClass2 не сериализуемый, и, возможно, изменить его.

Ответ принят как подходящий

Как заметил кто-то другой, глава 11 книги Джоша Блоха Эффективная Java является незаменимым ресурсом по сериализации Java.

Пара моментов из этой главы, имеющих отношение к вашему вопросу:

  • предполагая, что вы хотите сериализовать состояние несериализуемого поля в MyClass2, это поле должно быть доступно для MyClass либо напрямую, либо через геттеры и сеттеры. MyClass должен будет реализовать настраиваемую сериализацию, предоставив методы readObject и writeObject.
  • Класс несериализуемого поля должен иметь API, позволяющий получить его состояние (для записи в поток объектов), а затем создать экземпляр нового экземпляра с этим состоянием (при последующем чтении из потока объектов).
  • согласно пункту 74 Эффективной Java, MyClass2 должен имеет конструктор без аргументов, доступный для MyClass, иначе MyClass не сможет расширить MyClass2 и реализовать Serializable.

Ниже я написал небольшой пример, иллюстрирующий это.


class MyClass extends MyClass2 implements Serializable{

  public MyClass(int quantity) {
    setNonSerializableProperty(new NonSerializableClass(quantity));
  }

  private void writeObject(java.io.ObjectOutputStream out)
  throws IOException{
    // note, here we don't need out.defaultWriteObject(); because
    // MyClass has no other state to serialize
    out.writeInt(super.getNonSerializableProperty().getQuantity());
  }

  private void readObject(java.io.ObjectInputStream in)
  throws IOException {
    // note, here we don't need in.defaultReadObject();
    // because MyClass has no other state to deserialize
    super.setNonSerializableProperty(new NonSerializableClass(in.readInt()));
  }
}

/* this class must have no-arg constructor accessible to MyClass */
class MyClass2 {

  /* this property must be gettable/settable by MyClass.  It cannot be final, therefore. */
  private NonSerializableClass nonSerializableProperty;

  public void setNonSerializableProperty(NonSerializableClass nonSerializableProperty) {
    this.nonSerializableProperty = nonSerializableProperty;
  }

  public NonSerializableClass getNonSerializableProperty() {
    return nonSerializableProperty;
  }
}

class NonSerializableClass{

  private final int quantity;

  public NonSerializableClass(int quantity){
    this.quantity = quantity;
  }

  public int getQuantity() {
    return quantity;
  }
}

Ой, я надеялся на какую-то аннотацию вроде @NonSerialization: - /

Oliver Dixon 17.10.2015 12:36

Полезный подход для сериализации экземпляров несериализуемых классов (или, по крайней мере, подклассов) известен как последовательный прокси. По сути, вы реализуете writeReplace для возврата экземпляра совершенно другого сериализуемого класса, который реализует readResolve для возврата копии исходного объекта. Я написал пример сериализации java.awt.BasicStroke на Usenet

Выскочило несколько возможностей, и я возобновляю их здесь:

  • Реализуйте writeObject () и readObject () в соответствии с предложением sk
  • объявить свойство переходным, и оно не будет сериализовано, как было впервые заявлено моток
  • используйте XStream, как указано в борис-терзич
  • использовать последовательный прокси, как указано в Tom-hawtin-tackline

Если возможно, несерийные части можно установить как временные.

private transient SomeClass myClz;

В противном случае вы можете использовать Крио. Kryo - это быстрая и эффективная платформа сериализации графов объектов для Java (например, для сериализации JAVA java.awt.Color требуется 170 байт, Kryo только 4 байта), которая может сериализовать также несериализуемые объекты. Kryo также может выполнять автоматическое глубокое и поверхностное копирование / клонирование. Это прямое копирование от объекта к объекту, а не object->bytes->object.

Вот пример того, как использовать kryo

Kryo kryo = new Kryo();
// #### Store to disk...
Output output = new Output(new FileOutputStream("file.bin"));
SomeClass someObject = ...
kryo.writeObject(output, someObject);
output.close();
// ### Restore from disk...
Input input = new Input(new FileInputStream("file.bin"));
SomeClass someObject = kryo.readObject(input, SomeClass.class);
input.close();

Сериализованные объекты также можно сжимать, зарегистрировав точный сериализатор:

kryo.register(SomeObject.class, new DeflateCompressor(new FieldSerializer(kryo, SomeObject.class)));

Другие вопросы по теме