Подтвердить что ты не робот

Можно ли выполнять бинарную сериализацию .NET в случае, если у вас нет исходного кода класса?

Я использую BinaryFormatter для выполнения двоичной сериализации некоторых объектов в С#. Однако некоторые объекты содержат классы, к которым я обращаюсь через DLL, и у меня нет исходного кода, поэтому я не могу пометить их атрибутом Serializable. Есть ли простой способ их сериализации? У меня есть обходное решение, которое включает в себя принятие класса NoSource и создание нового класса SerializableNoSource, для которого конструктор принимает объект NoSource и извлекает всю необходимую мне информацию, но он взломан. Есть ли лучшие альтернативы?

4b9b3361

Ответ 1

Вы можете создать суррогат сериализации.

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

public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
    public DriversLicense License;
}


// An instance of this type will be part of the object graph and will need to be 
// serialized also.
public class DriversLicense
{
    public string Number { get; set; }
}

Чтобы сериализовать этот объект, вам необходимо определить суррогат сериализации для каждого типа в графе объектов.

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

public class PersonSurrogate : ISerializationSurrogate
{
    /// <summary>
    /// Manually add objects to the <see cref="SerializationInfo"/> store.
    /// </summary>
    public void GetObjectData(object obj, SerializationInfo info, StreamingContext context)
    {
        Person person = (Person) obj;
        info.AddValue("Name", person.Name);
        info.AddValue("Age", person.Age);
        info.AddValue("License", person.License);
    }

    /// <summary>
    /// Retrieves objects from the <see cref="SerializationInfo"/> store.
    /// </summary>
    /// <returns></returns>
    public object SetObjectData(object obj, SerializationInfo info, StreamingContext context, ISurrogateSelector selector)
    {
        Person person = (Person)obj;
        person.Name = info.GetString("Name");
        person.Age = info.GetInt32("Age");
        person.License = (DriversLicense) info.GetValue("License", typeof(DriversLicense));
        return person;
    }
}

public class DriversLicenseSurrogate : ISerializationSurrogate
{
    /// <summary>
    /// Manually add objects to the <see cref="SerializationInfo"/> store.
    /// </summary>
    public void GetObjectData(object obj, SerializationInfo info, StreamingContext context)
    {
        DriversLicense license = (DriversLicense)obj;
        info.AddValue("Number", license.Number);
    }

    /// <summary>
    /// Retrieves objects from the <see cref="SerializationInfo"/> store.
    /// </summary>
    /// <returns></returns>
    public object SetObjectData(object obj, SerializationInfo info, StreamingContext context, ISurrogateSelector selector)
    {
        DriversLicense license = (DriversLicense)obj;
        license.Number = info.GetString("Number");
        return license;
    }
}

Затем вам нужно сообщить свой IFormatter о суррогатах, указав и инициализируя SurrogateSelector и назначив его вашему IFormatter.

private static void SerializePerson(Person person)
{
    if (person == null)
        throw new ArgumentNullException("person");

    using (var memoryStream = new MemoryStream())
    {
        //Configure our surrogate selectors.
        var surrogateSelector = new SurrogateSelector();
        surrogateSelector.AddSurrogate(typeof (Person), new StreamingContext(StreamingContextStates.All),
                                       new PersonSurrogate());
        surrogateSelector.AddSurrogate(typeof (DriversLicense), new StreamingContext(StreamingContextStates.All),
                                       new DriversLicenseSurrogate());

        //Serialize the object
        IFormatter formatter = new BinaryFormatter();
        formatter.SurrogateSelector = surrogateSelector;
        formatter.Serialize(memoryStream, person);

        //Return to the beginning of the stream
        memoryStream.Seek(0, SeekOrigin.Begin);

        //Deserialize the object
        Person deserializedPerson = (Person) formatter.Deserialize(memoryStream);
    }
}

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

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

Ответ 2

Возможно, вы сможете использовать Mono.Cecil, чтобы добавить [SerializableAttribute] в классы, но я бы этого не сделал, если существует еще один способ достижения желаемого результата.

Ответ 3

Я согласен с @Servy, если автор класса не ожидал, что он будет сериализован, вы не должны пытаться его сериализовать напрямую. Таким образом, вы делаете правильные вещи с архитектурной точки зрения. Чтобы сделать ваш текущий подход меньше, "взломанный", рассмотрите возможность использования ISerializable для классов, содержащих ссылки на несериализуемые объекты.

Ответ 4

Создайте новый класс, наследуйте существующий класс, который не помечен атрибутом сериализации и не реализует интерфейс ISerializable.

Если класс запечатан, вы можете использовать Json.NET, а затем преобразовать его в двоичный и наоборот (Sucks большое время, используйте его, если ничего не может помочь:)).

Ответ 5

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

Сериализация

не может быть добавлена ​​в класс после того, как она была скомпилирован....