Как исключить поле из сериализации классов во время выполнения?

Как исключить поле класса из процесса сериализации во время выполнения? Модификатор переходный для времени компиляции, но как насчет времени выполнения? Я имею в виду обычную сериализацию java с ObjectOutputStream, а не gson или что-то еще.

Извините, я думаю, что не объяснил это правильно. Это не совсем о сериализации, а о de -сериализации. У меня есть пакет старых файлов и обрабатываю их следующим образом:

public class Deserialize {

/**
 * @param args
 * @throws IOException 
 * @throws ClassNotFoundException 
 */
public static void main(String[] args) throws ClassNotFoundException, IOException {
    File file = new File("/home/developer/workspace/DDFS/some.ddf");
    HackedObjectInputStream in = new HackedObjectInputStream(new GZIPInputStream(new FileInputStream(file)));

    System.out.println("Attempt to open " + file.getAbsolutePath());
    Object obj = in.readObject();
    in.close();


}

 static class HackedObjectInputStream extends ObjectInputStream
    {

        /**
         * Migration table. Holds old to new classes representation.
         */
        private static final Map MIGRATION_MAP = new HashMap();

        static
        {
            MIGRATION_MAP.put("DBOBExit", Exit.class);
        }

        /**
         * Constructor.
         * @param stream input stream
         * @throws IOException if io error
         */
        public HackedObjectInputStream(final InputStream stream) throws IOException
        {
            super(stream);
        }

        @Override
        protected ObjectStreamClass readClassDescriptor() throws IOException, ClassNotFoundException
        {
            ObjectStreamClass resultClassDescriptor = super.readClassDescriptor();

            for (final String oldName : MIGRATION_MAP.keySet())
            {
                if (resultClassDescriptor.getName().equals(oldName))
                {
                    resultClassDescriptor = ObjectStreamClass.lookup(MIGRATION_MAP.get(oldName));   
                }
            }

            return resultClassDescriptor;
        }

    }

}

Этот код отлично подходит для большинства файлов, но некоторые файлы бросают

Exception in thread "main" java.lang.ClassCastException: cannot assign instance of java.awt.Polygon to field Exit.msgbackPt of type java.awt.Point in instance of Exit
at java.io.ObjectStreamClass$FieldReflector.setObjFieldValues(ObjectStreamClass.java:2053)

из-за разных версий класса Exit. Новая версия имеет новые поля. Ошибка исчезает, когда я добавляю переход к новым полям, но другие файлы начинают бросать исключение (последние файлы).

Могу ли я добавить переход к этим новым файлам во время выполнения, если я обнаруживаю старый файл с сериализацией? Может быть, отражение или что-то еще?

21
nl ja de
Зачем? Что должно произойти на другом конце?
добавлено автор EJP, источник
Вам нужно либо реализовать writeObject() и readObject() самостоятельно, либо использовать Externalizable вместо Serializable , который должен предоставить вам полный контроль над процессом: docs.oracle.com/javase/7/docs/platform/serialization/spec/… (и другие части этой спецификации). Обратите внимание, что вам, вероятно, придется правильно де-сериализовать весь ваш класс, и вам придется разобраться, какие поля вы написали во время сериализации при десериализации. (Написав кучу флагов или таких данных перед данными.)
добавлено автор millimoose, источник
Глядя на редактирование: я думаю, что у вас есть ваша работа, вырезанная из вас. Встроенная сериализация - действительно очень неуклюжий выбор, если вы хотите сохранить свои данные и разработать свою схему.
добавлено автор millimoose, источник
Я нашел решение простым способом stackoverflow.com/a/14608062/1085787
добавлено автор gaponov, источник

4 ответы

документация ObjectOutputStream гласит:

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

Поэтому, когда вы объявляете переменную как переходную, ее следует игнорировать ObjectOutputStream. Убедитесь, что вы используете ключевое слово transient , а не аннотация @Transient . Такие аннотации используются некоторыми структурами ORM для отметки полей, которые не должны быть сохранены в базах данных. Они не имеют смысла для каркаса сериализации buildin.

private transient String foo;//Field gets ignored by ObjectOutputStream
@Transient private String bar;//Treated normally by ObjectOutputStream (might mean something for some other framework)
38
добавлено
Пример кажется неправильным. Переходные поля следует игнорировать, а не переходные.
добавлено автор BlueM, источник
@BlueM Вот что я написал: «Когда вы объявляете переменную как переходную, ее следует игнорировать ObjectOutputStream»
добавлено автор Philipp, источник

Сериализованное представление определенного класса соответствует самому классу, вы не можете его изменить извне, ближайшим к вам может быть определение подкласса с пользовательским поведением сериализации, но это касается только объектов этого подкласса, а не объектов родительского тип.

Если вы вообще не можете модифицировать рассматриваемый класс, единственным вариантом является подкласс ObjectOutputStream и переопределить replaceObject , чтобы заменить объект проблемы во время записи другим, который содержит только нужные вам данные, и процесс зеркального отображения во время чтения (подкласс ObjectInputStream и переопределить resolveObject ).

1
добавлено
На самом деле, нет. Вы всегда можете использовать writeObject() или writeReplace() .
добавлено автор millimoose, источник

Вы можете использовать модификатор «переходный»:

http://docs.oracle .com/JavaSE/функции/JLS/SE7/html/JLS-8.html # ПСБ-8.3.1.3

1
добавлено

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

0
добавлено
Означает ли это, что мне нужно добавить оба класса в проект, старую и новую? Для какого класса readResolve следует переоценить? Весь билет о десериализации модифицированного класса (был перенесен в другой пакет и переименован).
добавлено автор gaponov, источник
pro.jvm
pro.jvm
3 503 участник(ов)

Сообщество разработчиков Java Scala Kotlin Groovy Clojure Чат для нач-их: @javastart Наш сайт: projvm.com projvm.ru Наш канал: @proJVM Вакансии: @jvmjobs Конфы: @jvmconf

Java & Co
Java & Co
2 370 участник(ов)

Можно обсуждать с матом и без всё, что касается жабы, вплоть до холиваров. НЕ ИМЕЕТ ОТНОШЕНИЯ К САЙТУ JAVARUSH.RU ПРАВИЛА - https://t.me/javarush/75723 Вакансии сюда - https://telegram.me/joinchat/B7IzvUCnfo6d8t3yIxKguQ По вопросам - @thedude

learn.java
learn.java
1 888 участник(ов)

Чат для начинающих и не только Статистика: https://combot.org/chat/-1001083535868 Основной чат - @jvmchat

Java Underground
Java Underground
169 участник(ов)

https://vk.com/javatutorial

Javanese Questions
Javanese Questions
109 участник(ов)

Чат предназначен для обмена знаниями строго в формате в вопрос-ответ. Тема — Java, Kotlin и Android. Вопрос должен быть предварительно прогуглен, понятно и грамотно сформулирован, помечен хэштегами. Ответ — тем более. Куски кода размером в несколько строк можно писать прямо здесь, для больших кусков кода стоит использовать http://gist.github.com/, http://pastebin.com/, https://codeshare.io/ или любой аналогичный сервис. В некоторых случаях можно прикреплять скриншоты. Стикеры и гифки запрещены. Дополнять и уточнять вопросы и ответы — редактированием исходного сообщения. Обсуждения должны приводить к редактированию вопроса/ответа и удаляться. По хештегам можно искать существующие вопросы и овтеты: #вопрос #ответ #git #generics #java #server #awt #javafx #swing #kotlin #anko #tornadofx #ktor #android #recyclerView #performance #arch #network #permissions #storage #async