【问题标题】:de/serialize java objects across different applications using different package names使用不同的包名在不同的应用程序中反序列化 java 对象
【发布时间】:2014-11-25 08:26:29
【问题描述】:

我想在不同的应用程序之间共享 java 对象。

只要我在不同的项目中使用相同的包名,它就可以正常工作。 但是,如果我更改包名称,它就不再起作用了。

我尝试通过扩展 ObjectInputStream 类并覆盖 readClassDescriptor 方法来解决此问题。

但是这样做我得到以下错误:

java.io.StreamCorruptedException: invalid type code: 00

...不知道如何解决这个问题。

这是我用于扩展 ObjectInputStream 类的代码:

public class MyObjectInputStream extends ObjectInputStream {

    public static Map<String, Class> classNameMapping = initclassNameMapping(); 

    private static Map<String, Class> initclassNameMapping(){
        Map<String, Class> res = new HashMap<String, Class>();
        //ipxTest is the name of the package where the objects got serialized
        res.put("ipxTest.IPX", interprojectxchangeTest.IPX.class); 
        res.put("ipxTest.A", interprojectxchangeTest.A.class);
        return Collections.unmodifiableMap(res);
    }

    public MyObjectInputStream(InputStream in) throws IOException {
        super(in);
        enableResolveObject(true);
    }


    protected MyObjectInputStream() throws IOException, SecurityException {
        super();
        enableResolveObject(true);
    }

    @Override
    protected java.io.ObjectStreamClass readClassDescriptor() 
            throws IOException, ClassNotFoundException {
        ObjectStreamClass desc = super.readClassDescriptor();
        if (classNameMapping.containsKey(desc.getName()))
            return ObjectStreamClass.lookup(classNameMapping.get(desc.getName()));
        return desc;
    }
}

IPX 和 A 类在不同的项目中看起来相同,并且都具有相同的 serialID。

【问题讨论】:

标签: java serializable


【解决方案1】:

我的第一个建议是让您的实现变得简单,不要再与框架抗争——跨应用程序使用相同的包名称。我建议从可序列化的类中创建一个库,并在实现之间共享它。

如果您必须跨具有不同包名的应用程序进行序列化/反序列化,那么我的建议是放弃与类名和包名紧密相关的内置 Java 序列化,并使用 Gson 之类的东西来序列化 /反序列化。

Gson 允许你指定一个 TypeAdaper。您可以为要序列化/反序列化的每个类创建和注册一个 TypeAdapter,并在序列化时将类名(不包括包名)指定为“类型”,如下例所示,但使用 getSimpleName() 而不是 getCanonicalName( )

反序列化时,您必须将正确的包名称添加到“类型”

您必须为每个应用程序单独执行 TypeAdapter。

public class GsonTypeAdapter<T> implements JsonSerializer<T>, JsonDeserializer<T> {
    @Override
    public JsonElement serialize(T src, Type typeOfSrc, JsonSerializationContext context) {
        JsonObject result = new JsonObject();
        result.add("type", new JsonPrimitive(src.getClass().getCanonicalName()));
        result.add("properties", context.serialize(src, src.getClass()));

        return result;
    }

    @Override
    public T deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
        throws JsonParseException {
        JsonObject jsonObject = json.getAsJsonObject();
        String type = jsonObject.get("type").getAsString();
        JsonElement element = jsonObject.get("properties");

        try {
            return context.deserialize(element, Class.forName(type));
        } catch (ClassNotFoundException cnfe) {
            throw new JsonParseException("Unknown element type: " + type, cnfe);
        }
    }
}

【讨论】:

    【解决方案2】:

    类的包名是其全名的基本部分。

    如果 matthiasboesinger 是你的名字,那么 matthias 只是你用来识别你的名字,但 boesinger 部分是你唯一的名字标识符。

    类似地,在类中,编译器用它们的全名而不是名字来标识类及其序列化对象。

    如果你更改包名,你会失去类的完整性,因为不同的类可以存在于同名的不同包中。

    所以你正在尝试的是不可能的。

    除非你编写一个适配器类,它从带有包名的原始类中提取数据并将数据泵入你的新包名类。

    但这只是拐弯抹角。

    【讨论】:

    • 我的问题是:为什么覆盖的'readClassDescriptor'方法不能解决所描述的问题。也就是说:jvm 无法再以其原始名称访问原始类...
    猜你喜欢
    • 2018-12-05
    • 2020-08-21
    • 2017-11-29
    • 1970-01-01
    • 2019-05-30
    • 1970-01-01
    • 2015-10-08
    • 1970-01-01
    相关资源
    最近更新 更多