如何在运行时从类序列化中排除字段?

时间:2022-01-13 07:45:26

How to exclude class field from serialization process in runtime ? There is transient modifier for compilation time but what about runtime ? I mean common java serialization with ObjectOutputStream, not gson or something.

如何在运行时从序列化过程中排除类字段?编译时有瞬态修饰符,但运行时呢?我指的是使用ObjectOutputStream的常见java序列化,而不是gson或其他东西。

Sorry I think I didn't explain right. This is not exactly about serialization , but about de-serialization. I have batch of legacy files and handle them like this:

对不起,我想我没解释得对。这不完全是关于序列化,而是关于反序列化。我有一批遗留文件并像这样处理它们:

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;
        }

    }

}

}

This code works fine for most of files , but some files throws

此代码适用于大多数文件,但有些文件会抛出

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)

because of different versions of Exit class . New version has new fields. Error disappearing when I add transient to new fields, but another files starts to throwing an exception (latest files).

因为Exit类的版本不同。新版本有新字段。当我向新字段添加瞬态时,错误消失,但另一个文件开始抛出异常(最新文件)。

So can I add transient to these new fileds in runtime if I detect legacy serilized file ? Maybe reflection or something ?

因此,如果我检测到传统的加强文件,我可以在运行时向这些新文件添加瞬态吗?也许反思或什么?

4 个解决方案

#1


36  

The documentation of ObjectOutputStream says:

ObjectOutputStream的文档说:

The default serialization mechanism for an object writes the class of the object, the class signature, and the values of all non-transient and non-static fields. References to other objects (except in transient or static fields) cause those objects to be written also.

对象的默认序列化机制会写入对象的类,类签名以及所有非瞬态和非静态字段的值。对其他对象的引用(瞬态或静态字段除外)也会导致这些对象被写入。

So when you declare a variable as transient, it should be ignored by ObjectOutputStream. Make sure that you use the transient keyword and not a @Transient annotation. Such annotations are used by some ORM frameworks to mark fields which are not supposed to be saved in databases. They are meaningless for the buildin serialization framework.

因此,当您将变量声明为瞬态时,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)

#2


1  

The serialized representation of a particular class is up to the class itself, you can't change it externally, the nearest you can get is to define a subclass with custom serialization behaviour but that only affects objects of that subclass, not objects of the parent type.

特定类的序列化表示由类本身决定,你不能在外部更改它,你可以得到的最接近的是定义一个具有自定义序列化行为的子类但只影响该子类的对象,而不是父类的对象类型。

If you can't modify the class in question at all then your only option is to subclass ObjectOutputStream and override replaceObject to replace the problem object at write time with a different one that contains only the data you want, and the mirror image process at read time (subclass ObjectInputStream and override resolveObject).

如果您根本无法修改有问题的类,那么您唯一的选择是继承ObjectOutputStream并覆盖replaceObject以在写入时将问题对象替换为仅包含您想要的数据的另一个问题对象,以及读取时的镜像处理过程time(子类ObjectInputStream和override resolveObject)。

#3


1  

You can use the 'transient' modifier:

您可以使用'transient'修饰符:

http://docs.oracle.com/javase/specs/jls/se7/html/jls-8.html#jls-8.3.1.3

http://docs.oracle.com/javase/specs/jls/se7/html/jls-8.html#jls-8.3.1.3

#4


0  

You're digging the wrong hole here. Instead of messaging around with runtime decisions about which fields to serialized and overriding readClassDescriptor(), you should look into overriding readResolve().

你在这里挖错了洞。您应该研究覆盖readResolve(),而不是通过运行时决定有关哪些字段要序列化和覆盖readClassDescriptor()的消息传递。

#1


36  

The documentation of ObjectOutputStream says:

ObjectOutputStream的文档说:

The default serialization mechanism for an object writes the class of the object, the class signature, and the values of all non-transient and non-static fields. References to other objects (except in transient or static fields) cause those objects to be written also.

对象的默认序列化机制会写入对象的类,类签名以及所有非瞬态和非静态字段的值。对其他对象的引用(瞬态或静态字段除外)也会导致这些对象被写入。

So when you declare a variable as transient, it should be ignored by ObjectOutputStream. Make sure that you use the transient keyword and not a @Transient annotation. Such annotations are used by some ORM frameworks to mark fields which are not supposed to be saved in databases. They are meaningless for the buildin serialization framework.

因此,当您将变量声明为瞬态时,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)

#2


1  

The serialized representation of a particular class is up to the class itself, you can't change it externally, the nearest you can get is to define a subclass with custom serialization behaviour but that only affects objects of that subclass, not objects of the parent type.

特定类的序列化表示由类本身决定,你不能在外部更改它,你可以得到的最接近的是定义一个具有自定义序列化行为的子类但只影响该子类的对象,而不是父类的对象类型。

If you can't modify the class in question at all then your only option is to subclass ObjectOutputStream and override replaceObject to replace the problem object at write time with a different one that contains only the data you want, and the mirror image process at read time (subclass ObjectInputStream and override resolveObject).

如果您根本无法修改有问题的类,那么您唯一的选择是继承ObjectOutputStream并覆盖replaceObject以在写入时将问题对象替换为仅包含您想要的数据的另一个问题对象,以及读取时的镜像处理过程time(子类ObjectInputStream和override resolveObject)。

#3


1  

You can use the 'transient' modifier:

您可以使用'transient'修饰符:

http://docs.oracle.com/javase/specs/jls/se7/html/jls-8.html#jls-8.3.1.3

http://docs.oracle.com/javase/specs/jls/se7/html/jls-8.html#jls-8.3.1.3

#4


0  

You're digging the wrong hole here. Instead of messaging around with runtime decisions about which fields to serialized and overriding readClassDescriptor(), you should look into overriding readResolve().

你在这里挖错了洞。您应该研究覆盖readResolve(),而不是通过运行时决定有关哪些字段要序列化和覆盖readClassDescriptor()的消息传递。