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

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

Как исключить поле класса из процесса сериализации во время выполнения? Модификатор переходный для времени компиляции, но как насчет времени выполнения? Я имею в виду обычную сериализацию 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<String, Class<?>> MIGRATION_MAP = new HashMap<String, Class<?>>();

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

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

4b9b3361

Ответ 1

Документация 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)

Ответ 3

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

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

Ответ 4

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