【问题标题】:How to exclude field from class serialization in runtime?如何在运行时从类序列化中排除字段?
【发布时间】:2013-01-12 23:54:12
【问题描述】:

如何在运行时从序列化过程中排除类字段? 编译时间有 transient 修饰符,但运行时呢? 我的意思是使用 ObjectOutputStream 进行常见的 java 序列化,而不是 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 类。新版本有新领域。 当我将瞬态添加到新字段时错误消失,但另一个文件开始抛出异常(最新文件)。

如果我检测到遗留的序列化文件,我可以在运行时向这些新文件添加瞬态吗? 也许是反射或什么?

【问题讨论】:

  • 您需要自己实现writeObject()readObject(),或者可能使用Externalizable 而不是Serializable,这应该可以让您完全控制该过程:docs.oracle.com/javase/7/docs/platform/serialization/spec/…(和该规范的其他部分。)但请注意,您可能必须以这种方式正确反序列化整个类,并且您必须在反序列化时处理确定在序列化期间编写的字段。 (通过在数据之前写一堆标志等。)
  • 为什么?另一端会发生什么?
  • 查看编辑:我认为您已经完成了工作。如果您想持久化数据并发展其架构,内置序列化是一个非常非常笨拙的选择。
  • 我以简单的方式找到了解决方案stackoverflow.com/a/14608062/1085787

标签: java serialization


【解决方案1】:

documentation of ObjectOutputStream 说:

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

因此,当您将变量声明为瞬态时,ObjectOutputStream 应将其忽略。确保使用 transient 关键字而不是 @Transient 注释。一些 ORM 框架使用此类注释来标记不应保存在数据库中的字段。它们对于内置序列化框架毫无意义。

private transient String foo; // Field gets ignored by ObjectOutputStream
@Transient private String bar; // Treated normally by ObjectOutputStream (might mean something for some other framework)

【讨论】:

  • 示例似乎是错误的。应忽略瞬态字段而不是非瞬态字段。
  • @BlueM 这就是我写的:“当你将一个变量声明为瞬态时,它应该被 ObjectOutputStream 忽略”
【解决方案2】:

【讨论】:

    【解决方案3】:

    特定类的序列化表示取决于类本身,您无法在外部更改它,最接近的方法是定义具有自定义序列化行为的子类,但这只会影响该子类的对象,而不影响对象父类型的。

    如果您根本无法修改相关类,那么您唯一的选择是继承 ObjectOutputStream 并覆盖 replaceObject 以在写入时将问题对象替换为仅包含您想要的数据的不同对象,并且读取时的镜像进程(子类 ObjectInputStream 并覆盖 resolveObject)。

    【讨论】:

    • 并非如此。您可以随时使用writeObject()writeReplace()
    【解决方案4】:

    你在这里挖错了坑。与其纠结于要序列化哪些字段并覆盖 readClassDescriptor() 的运行时决策,不如考虑覆盖 readResolve()

    【讨论】:

    • 这是否意味着我必须将两个类都添加到项目中,旧版本和新版本?对于哪个类 readResolve 应该被覆盖?关于修改类的反序列化的整张票(被移动到不同的包并重命名)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-12-29
    • 2012-06-10
    • 2017-06-01
    相关资源
    最近更新 更多