简体   繁体   English

我如何查看java序列化对象?

[英]How do I peek in a java serialized object?

I have an object from old Java code, and I now changed the serialized object code. 我有一个来自旧Java代码的对象,我现在更改了序列化对象代码。 I want to be able to read both the old files and the new files. 我希望能够读取旧文件和新文件。 I need a branching statement in readObject to do something like: 我需要在readObject中使用分支语句来执行以下操作:

if (next object is int -- just poking, it might be an Object)
{
    // we know we are in version 1
} else {
    // read new version of object
}

is that possible to do? 那可能吗?

if (object instanceof Integer) {
  ... Do stuff
} else {
  ... Do other stuff
}

EDIT: I suppose I should expand on this. 编辑:我想我应该扩展这一点。 You can check object types using instanceof but I'm not sure about being able to work with primatives like int or char . 您可以使用instanceof检查对象类型,但我不确定是否能够使用像intchar这样的原型。

The easiest way to do this is to keep the old member variables with their old types and add new member variables for new types. 最简单的方法是使用旧类型保留旧成员变量,并为新类型添加新成员变量。 also, you must keep the serialVersionUID of the class the same. 此外,您必须保持类的serialVersionUID相同。 then, your readObject() implementation can do any necessary manipulation to transform the old data to new data. 然后,您的readObject()实现可以执行任何必要的操作,以将旧数据转换为新数据。

Original Object: 原始对象:

public class MyObject {
   private static final long serialVersionUID = 1234L;

   private int _someVal;
}

New version: 新版本:

public class MyObject {
   private static final long serialVersionUID = 1234L;

   private int _someVal;  //obsolete
   private String _newSomeVal;

    private void readObject(java.io.ObjectInputStream in)
      throws IOException, ClassNotFoundException
    {
      in.defaultReadObject();

      if(_someVal != 0) {
        // translate _someVal to _newSomeVal
      }
    }
}

I believe there are more complex options available as well using custom ObjectStreamField[] serialPersistentFields , ObjectInputStream.GetField and ObjectOutputStream.PutField . 我相信使用自定义ObjectStreamField[] serialPersistentFieldsObjectInputStream.GetFieldObjectOutputStream.PutField可以使用更复杂的选项。

Ok so basically the question is "How can we check with an ObjectInputStream whether the next field is a primitive or an object?" 好吧基本上问题是“我们如何检查ObjectInputStream下一个字段是基元还是对象?” and the answer as far as I can see is: You can't. 而我所能看到的答案是:你做不到。

Which means the best solution I can see to keep backwards compatibility is to never remove primitives from the original version - keeping useless information blows up the size a bit, but otherwise that's easy. 这意味着我可以看到保持向后兼容性的最佳解决方案是永远不会从原始版本中删除原语 - 保持无用的信息会使规模大大增加,但这样做很容易。

To add new fields, I can see two ways: 要添加新字段,我可以看到两种方式:

  1. Keep the earlier message format identical and only add new objects at the end - you can then easily distinguish different versions by the message size (or more exactly you'll get an IOException when reading data of v2 when you get a v1 object). 保持较早的消息格式相同,只在末尾添加新对象 - 然后您可以通过消息大小轻松区分不同版本(或者更准确地说,当您获取v1对象时,在读取v2的数据时会获得IOException)。 That's much simpler and generally preferred. 这更简单,通常更受欢迎。

  2. You can change objects from v1 to v2 and then do a simple instanceof check. 您可以将对象从v1更改为v2,然后执行简单的instanceof检查。 If you want to add primitives is to store their wrapper versions (ie Integer et al). 如果你想添加原语是存储他们的包装器版本(即Integer等)。 Can save you some bytes, but Java's serialization protocol was never efficient to begin with, so really that's unnecessary complicated. 可以节省一些字节,但Java的序列化协议从一开始就没有效率,所以真的不必要复杂。

ObjectInputStream will load and create an instance of the right class. ObjectInputStream将加载并创建正确类的实例。

object = ois.readObject();

if (object instanceof YourNewShiny ){
    // new style object
} else if (object instanceof YourOldBusted ){
    // convert YourOldBusted to YourNewShiny
} else {
    throw new ToyOutOfPram();
}

This is all great if you have a new class, but if you have changed your class in an incompatible manner such that the ObjectInputStream cannot deserialise the old versions of the class into the new form. 如果您有一个新类,这一切都很棒,但如果您以不兼容的方式更改了类,则ObjectInputStream无法将类的旧版本反序列化为新表单。 If this is the case you are pretty much stuffed. 如果是这种情况你几乎被塞满了。

Options: 选项:

  • Revert your changes, and either do them compatibly ie add a serialVersionId, don't change the order of fields, only add new field, plus don't assume not null constraints 恢复您的更改,并兼容它们,即添加serialVersionId,不要更改字段的顺序,只添加新字段,再加上不假设不为空约束
  • Using an old version of the code read the serialised data, convert it to some intermediate form (xml,csv,etc) and then import this data into the new class definition and serialise it out 使用旧版本的代码读取序列化数据,将其转换为某种中间形式(xml,csv等),然后将此数据导入新的类定义并将其序列化
  • manually re implement the ObjectInputStream to detect your class type (you can use the serialVersionId to stiff the type) 手动重新实现ObjectInputStream以检测您的类类型(您可以使用serialVersionId来强化该类型)

Only the first seems like a good idea to me. 只有第一个对我来说似乎是一个好主意。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM