【问题标题】:Replacing class name in serialized data替换序列化数据中的类名
【发布时间】:2011-01-12 12:16:42
【问题描述】:

我想在序列化数据流中将字符串“com.oldpackage.className”替换为“com.newPackage.className”。这个序列化的数据是从数据库中读取的,并在替换字符串后更新。

我在做同样的事情时遇到了一些问题。如果您已经猜到了,这是重构练习的一部分。是否有任何库可以帮助我处理序列化数据?如果您还可以评论任何预防措施或警告,那将有很大帮助。

非常感谢, 克里斯。 P.S:旧类和新类都没有将序列版本 ID 声明为其字段的一部分。

【问题讨论】:

  • 如果枚举的名称是 com.oldPackage.Person 并且新名称是 com.newPack.Person 我还应该在序列化数据中替换什么:1) 将 com.oldPackage.Person 替换为com.newPack.Person 2) 将 com/oldPackage/Person 替换为 com/newPack/Person
  • 由于还有一个名为 com.oldPackage.PersonDetails 的类并且会受到上述操作的影响,所以我执行以下操作: 3) 将 com.newPack.PersonDetails(result of 1) 替换为 com .oldPackage.PersonDetails 4) 用 com/oldPackage/PersonDetails 替换 com/newPack/PersonDetails((result of 2)) 如果我遗漏了什么,你能帮我吗?

标签: java serialization refactoring


【解决方案1】:

为了准确地回答 Tom Hawtin 的回答,我已经设法使用以下代码来实现它:

public static ObjectInputStream getSwapOIS( InputStream in ,String fromClass,String toClass)
    throws IOException ,ClassNotFoundException {
    final String from="^"+fromClass,fromArray="^\\[L"+fromClass,toArray="[L"+toClass;
    return new ObjectInputStream(in) {
        protected Class<?> resolveClass(ObjectStreamClass desc)
            throws IOException, ClassNotFoundException
        {
            String name = desc.getName().replaceFirst(from, toClass);
            name = name.replaceFirst(fromArray, toArray);
            return Class.forName(name);
        }
        protected ObjectStreamClass readClassDescriptor()
            throws IOException, ClassNotFoundException
        {
            ObjectStreamClass cd = super.readClassDescriptor();
            String name = cd.getName().replaceFirst(from, toClass);
            name = name.replaceFirst(fromArray, toArray);
            if(!name.equals(cd.getName())) {
                cd = ObjectStreamClass.lookup(Class.forName(name));
            }
            return cd;
        }
    };
}

请注意,您还需要重写 readClassDescriptor()。它适用于标准类型和数组,您甚至可以更改类名而不仅仅是包名。做吧:

 InputStream in = new ByteArrayInputStream(classBytes);
 ObjectInputStream ois = getSwapOIS(    in,
                                        "com.oldpackage.className",
                                        "com.newpackage.newClassName");
 Object myObject= ois.readObject();

【讨论】:

    【解决方案2】:

    我不知道该怎么做,但我可以建议您“合法”的解决方案。实现在类路径中同时包含新旧包的转换器,并执行以下操作:从 DB 读取数据,使用旧包对其进行反序列化,在 java 中将旧实例转换为新实例,然后再次将新数据存储在 DB 中。

    此解决方案需要执行一些肮脏的工作,但它是安全的。此外,这些结果将来可能会被重复使用。但我祝你好运找到只替换序列化数据中的类名的解决方案。

    【讨论】:

    • Alex,那是我的第一个方法,现在还活着。但问题是,序列化数据是一个休眠代理,第一种方法我面临太多问题。我发现在序列化数据中替换包名称的方法很简单,理论上可行,但是,我正在寻找其他人知道的任何警告或技巧。
    • 好的,祝你好运。我只是认为如果旧名称和新名称具有相同的长度,则名称替换可能会起作用。否则我会遇到问题。
    • 当场,我在 sql 和 Java 端(使用 JDBC)中都遇到了替换问题。在 Java 方面,这可能是因为与结果集之间的转换。在我所依赖的 DB 中,正是出于您提到的原因给出了问题。
    【解决方案3】:

    IIRC 您可以(具有足够的权限)覆盖ObjectInputStream.resolveClass(和resolveProxyClass)以返回具有不同包名称的Class。虽然,IIRC,你不能改变简单的类名。

    【讨论】:

    • 这就是 Java 文档关于枚举的说法:“无法自定义枚举常量的反序列化过程:在反序列化过程中,枚举类型定义的任何特定于类的 readObject、readObjectNoData 和 readResolve 方法都将被忽略。”
    • @ChrisOdney 我不是建议使用这些方法。
    【解决方案4】:

    我可以建议另一种方法吗? java 序列化格式在用于长期存储时存在一些重大问题。

    由于您正在重构并且很可能必须以一种或另一种方式反序列化所有存储的数据,您可能希望将Google Protocol Buffers 视为另一种序列化格式。

    这可能需要更多的工作,但它是专为长期存储和版本控制而设计的。

    祝你好运

    【讨论】:

    • Gareth,在我反序列化当前数据之前,我想不出替代的序列化机制。
    • 确定只在一次性批处理作业中使用原始类吗?
    【解决方案5】:

    如果要重构的数据以字符串形式存储在数据库中,一种选择是使用 SQL 来简单地更新该数据,例如(在伪 MySQL 中)

    update mydata 
    set serialized_data = 
    replace(serialized_data, "com.oldpackage", "com.newpackage")
    

    当然,您会在尝试从数据库中重新读取这些数据之前重构您的 Java 代码。

    【讨论】:

    • 他存储可序列化的 java 对象。您确定简单的字符串替换会起作用,即不会破坏 java 序列化吗?
    • 它应该可以正常工作 - 序列化形式中根本没有其他信息会与更新相矛盾,这是关键问题,以及在重新阅读之前重构实际代码的假设数据。在这种形式下,数据是自我描述的。我会推荐 XStream 作为序列化器/反序列化器,尽管还有其他的。
    • 我的类名类似于 com.oldpackageName.Person,我正在尝试将其替换为 com.newpackageName.Person。不幸的是,甚至 com.oldpackageName.PersonDetails 也被替换为 com.newpackageName.PersonDetails。我正在努力。而且我还需要查看序列化数据是否在其数据中包含 com/oldpackage/ 的格式(而不是 VM 的 /)。
    • 当我尝试从 DB 读取它时,它给了我一个 java.io.UTFDataFormatException
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-03-31
    • 1970-01-01
    • 2021-01-03
    • 2021-12-12
    • 2022-09-30
    • 2012-07-16
    相关资源
    最近更新 更多