Проблемы с загрузкой классов при десериализации типа из другой сборки - PullRequest
0 голосов
/ 16 июня 2009

Есть две сборки:
1) Сборка, содержащая сериализатор. Это место, откуда начинается сериализация и десериализация.
2) Сборка, содержащая сериализованные типы. Это место, которое вызывает сериализатор с 1-й сборки.
Идея сериализатора в Assembly1 проста. У него есть два метода, которые используются для преобразования объектов из байтовых массивов и в них. Код клиента для этого сериализатора может выглядеть так:

    ISerializer serializer = ...

    MyClass my = new MyClass();
    byte[] data = serializer.Serialize(my);
    Console.WriteLine(Encoding.ASCII.GetString(data)); // dump serialized form
    MyClass another = (MyClass)serializer.Deserialize(data);

MyClass определен в Assembly2, поэтому Assembly1 ничего не знает об этом. Этот сценарий будет работать, если сериализатор реализован со стандартными классами .Net, например:

public class DotNetSerializer : ISerializer
{
    public byte[] Serialize(object obj)
    {
        BinaryFormatter formatter = new BinaryFormatter();
        using (MemoryStream stream = new MemoryStream())
        {
            formatter.Serialize(stream, obj);
            byte[] result = stream.GetBuffer();
            Array.Resize(ref result, (int)stream.Length);
            return result;
        }
    }

    public object Deserialize(byte[] data)
    {
        BinaryFormatter formatter = new BinaryFormatter();
        using (Stream stream = new MemoryStream(data))
        {
            return formatter.Deserialize(stream);
        }
    }
}

Сериализованная форма MyClass будет содержать информацию о сборке, в которой определен MyClass. Однако, если сериализатор будет реализован с использованием классов Java (преобразованных с помощью IKVM), то при десериализации возникнет исключение ClassNotFound. Это реализация сериализатора с использованием классов Java:

public class JavaSerializer : ISerializer
{
    public object Deserialize(byte[] data)
    {
        ByteArrayInputStream stream = new ByteArrayInputStream(data);
        ObjectInputStream ois = new ObjectInputStream(stream);
        return ois.readObject();
    }

    public byte[] Serialize(object obj)
    {
        ByteArrayOutputStream stream = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(stream);
        oos.writeObject(obj);
        stream.flush();
        return stream.toByteArray();
    }
}

Это не будет работать в .Net, но будет нормально работать в Java, если загружено из Eclipse с дополнительными записями в манифестах плагинов, таких как BuddyPolicy и RegisterBuddy. Я не могу просто переключиться с JavaSerializer на DotNetSerializer, поскольку в моем приложении (которое в основном написано на Java) есть много readObject, writeObject, readResolve и т. Д. Но мне нужно как-то решить эту проблему, поэтому я ищу решение , В настоящее время я вижу некоторые гипотетические пути решения:

  • Перегрузка некоторого метода ObjectOutputStream, поэтому в сериализованной форме MyClass также будет содержаться имя сборки, например «MyClass, MyAssembly, ...».
  • Перегрузка некоторого метода в ObjectInputStream, поэтому класс будет загружен другим способом, возможно, его нужно искать в другой сборке и т. Д.
  • Добавление некоторой информации в манифесты сборок позволяет IKVM знать, где искать MyClass. Что-нибудь из этого реально? Как решить эту проблему?

1 Ответ

0 голосов
/ 17 июня 2009

Ребята из команды IKVM дали мне ответы:

1) Вы можете создать подкласс ObjectOutputStream и переписать annotateClass для записи название сборки и подкласс ObjectInputStream и переопределить resolClass для чтения сборки имя.

2) Добавьте следующий пользовательский атрибут к сборке, которая делает десериализация: [сборка: IKVM.Attributes.CustomAssemblyClassLoader (TypeOf (ikvm.runtime.AppDomainAssemblyClassLoader))]

Я также обнаружил, что могу переопределить загрузчик классов для любой сборки с явным значением в файле конфигурации приложения:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <appSettings>
    <add key="ikvm-classloader:MyExampleAssembly" value="ikvm.runtime.AppDomainAssemblyClassLoader, IKVM.OpenJDK.ClassLibrary, Version=0.37.0.0, Culture=neutral, PublicKeyToken=null" />
  </appSettings>
</configuration>

И это также возможно можно установить во время преобразования из java jar, когда используется параметр командной строки -classloader.

...