管理多个版本的序列化 Java 对象 [英] Managing several versions of serialized Java objects

查看:15
本文介绍了管理多个版本的序列化 Java 对象的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设我有一个程序,由于某种原因需要处理旧版本的序列化对象.

Lets say that I have a program that for some reason need to handle old versions of serialized objects.

例如:反序列化时,可能会遇到这些版本之一.

Eg: when deserializing, one of these versions may be encountered.

class Pet {
    private static final long serialVersionUID = 1L;
    int paws;
}

class Pet {
    private static final long serialVersionUID = 2L;
    long paws; // handle marsian centipedes
    boolean sharpTeeth;
}

让我们假设(逻辑上)可以使用一些巧妙的策略来设置不存在的字段等,将旧对象转换为新对象,但是:

Lets assume that it's (logically) possible to convert an old object to a new object using some clever strategy to set nonexistant fields etc etc, but:

我如何安排我的源代码?在编写转换器时,我可能需要在同一源代码树中使用两个版本,但我如何在 Eclipse 中处理它.

How do I arrange my source code? I would probably need both versions in the same source tree when writing a converter, but how do I handle that in , say, eclipse.

我应该在一个类加载器中进行反序列化,如果失败,请尝试使用另一个使用旧版本(等等)的类加载器,还是有更好的方法?

Should I do deserialization in one class loader, and if that fails try using another class loader that uses an older version (and so on), or are there better ways?

最好的策略是什么?

推荐答案

让我们假设(逻辑上)可以使用一些巧妙的策略来设置不存在的字段等,将旧对象转换为新对象......我如何安排我的源代码?

Lets assume that it's (logically) possible to convert an old object to a new object using some clever strategy to set nonexistant fields etc etc... How do I arrange my source code?

我看到了两种处理方法.首先,除非您希望抛出 InvalidClassException,否则永远不要更改 serialVersionUID.第二条规则是改变字段的类型,而只添加或删除序列化自动处理的字段.例如,如果序列化文件具有具有 booleansharpTeeth; 的类的版本,但该类没有该字段,那么在反序列化期间它将被忽略.如果反序列化的类具有 sharpTeeth 字段但文件没有,那么它将被初始化为其默认值,在这种情况下为 false.

I see two ways of handling this. First off, you should never change the serialVersionUID unless you want InvalidClassException to be thrown. The second rule is to not change the types of fields but to only add or remove fields which serialization handles automagically. For example, if a serialized file has the version of the class which has boolean sharpTeeth; but the class doesn't have that field then it will be ignored during deserialization. If the deserialized class has the sharpTeeth field but the file doesn't then it will get initialized to its default value, false in this case.

这对于您想要尝试处理向前和向后兼容性的分布式系统尤其重要.您不想升级应用程序 A 的版本并破坏依赖于 A 的另一个应用程序 B.通过不更改 serialVersionUID 而只是添加或删除字段,您可以做到这一点.您的实体的更高版本需要支持旧版本而没有新字段中的值,但旧实体不会介意新字段是否可用.这也意味着您不应更改字段的比例.

This is especially important with distributed systems where you want to try to handle both forwards and backwards compatibility. You don't want to upgrade a version of application A and break another application B which depends on A. By not changing the serialVersionUID but just adding or removing fields you can do that. Later versions of your entity need to support older versions without values in newer fields but older entities won't mind if new fields are available. This also means that you shouldn't change a field's scale as well.

序列化非常聪明,但它不处理字段的类型更改.您不应该只是将 pawsint 更改为 long.相反,我建议添加一个 long pawsLong 或类似的代码并编写代码来处理存在 int pawslong pawsLong 的可能性有价值.

Serialization is pretty smart but it does not handle type changes to fields. You shouldn't just change paws from an int to a long. Instead, I'd recommend adding a long pawsLong or some such and writing your code to handle the possibility of there being int paws or long pawsLong having a value.

public long getPaws() {
    if (pawsLong > 0) {
        return pawsLong;
    } else {
        // paws used to be an integer
        return paws;
    }
}

您也可以编写自己的 readObject 方法在反序列化时进行转换:

You could also write your own readObject method to do the conversion at de-serialization time:

private void readObject(java.io.ObjectInputStream in) {
    super.readObject(in);
    // paws used to be an integer
    if (pawsLong == 0 && paws != 0) {
        pawsLong = paws;
    }
}

如果这对您不起作用,那么自定义序列化是可行的方法.但是,您必须从头开始执行此操作,并使用内部版本 ID 定义自定义 readObject(...)writeObject(...) 方法.类似的东西:

If this doesn't work for you then custom serialization is the way to go. You have to start from the beginning doing this however and define custom readObject(...) and writeObject(...) methods with an internal version id. Something like:

// never change this
private static final long serialVersionUID = 3375159358757648792L;
// only goes up
private static final int INTERNAL_VERSION_ID = 2;
...
// NOTE: in version #1, this was an int
private long paws;

private void readObject(java.io.ObjectInputStream in) {
    int version = in.readInt();
    switch (version) {
        case 1 :
            paws = in.readInt();
            ...
        case 2 :
            paws = in.readLong();
            ...

private void writeObject(java.io.ObjectOutputStream out) {
    out.writeInt(INTERNAL_VERSION_ID);
    out.writeLong(paws);
    ...

但是这个方法对你的向前兼容性没有帮助.版本 1 的读者不会理解版本 2 的序列化输入.

But this method does not help you with forwards compatibility. A version 1 reader won't understand version 2 serialization input.

我应该在一个类加载器中进行反序列化,如果失败,请尝试使用另一个使用旧版本(等等)的类加载器,还是有更好的方法?

Should I do deserialization in one class loader, and if that fails try using another class loader that uses an older version (and so on), or are there better ways?

我不会推荐任何这些方法.听起来很难维护.

I would not suggest any of these methods. Sounds very difficult to maintain.

这篇关于管理多个版本的序列化 Java 对象的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆