【问题标题】:Is it possible to do .NET binary serialization of an object when you don't have the source code of the class?当您没有类的源代码时,是否可以对对象进行 .NET 二进制序列化?
【发布时间】:2012-10-31 19:24:01
【问题描述】:

我正在使用BinaryFormatter 对 C# 中的某些对象进行二进制序列化。但是,一些对象包含我通过 DLL 访问的类并且没有源代码,因此我无法使用 Serializable 属性标记它们。有没有一种直接的方法来序列化它们?我有一个解决方法,其中涉及采用NoSource 类并创建一个新类SerializableNoSource,构造函数为此采用NoSource 对象并从中提取我需要的所有信息,但它很hacky。有没有更好的选择?

【问题讨论】:

  • 在我看来,将对象封装在另一个确实知道如何序列化自身以及与封装对象相互转换的对象中并不难,这是完全合适的。现在,可能会有一些更巧妙或更好的东西,但我不会认为你目前的方法存在根本缺陷。

标签: c# .net serialization binary binary-serialization


【解决方案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;
    }
}

然后您需要通过定义和初始化SurrogateSelector 并将其分配给您的IFormatter 来让您的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,如果类的作者没有预料到它会被序列化,你不应该尝试直接序列化它。因此,从架构的角度来看,您正在做正确的事情。为了使您当前的方法不那么“hacky”,请考虑为包含对不可序列化对象的引用的类实现ISerializable

    【讨论】:

      【解决方案4】:

      新建一个类,继承现有的没有序列化属性的类,实现ISerializable接口。

      如果类是密封的,那么您可以使用 Json.NET,然后将其转换为二进制,反之亦然(很糟糕,如果没有其他方法可以帮助,请使用它:))。

      【讨论】:

        【解决方案5】:

        我知道这是一个老问题,但我最近发现需要序列化/反序列化 DTO,由于性能原因,我们无法控制其二进制格式的源代码。设法找到了很多替代二进制序列化程序,例如 ZeroFormatter 和 Protobuf,但它们都需要用属性装饰 DTO 或定义模式。

        这使我走上了创建自己的二进制序列化程序的道路,它可以快速替代二进制格式的 JSON 序列化程序,您可能会发现它很有用:https://github.com/zachsaw/Binaron.Serializer

        【讨论】:

          【解决方案6】:

          我认为更简洁的方法是实现 ISerializable 接口并自行管理序列化和反向过程。 在 MSDN 我们可以找到:

          序列化后不能添加到类中 编译....

          【讨论】:

          • "但是,有些对象包含我通过 DLL 访问的类,并且没有源代码"
          • 我认为您可以创建自己的类,它可以与您没有源的对象具有相同的属性吗?还是扩展它??
          • 扩展不行,复制属性……和他现在的一样
          猜你喜欢
          • 1970-01-01
          • 2011-01-21
          • 2022-09-28
          • 2013-08-02
          • 1970-01-01
          • 2010-12-17
          • 2011-10-18
          • 1970-01-01
          • 2016-09-25
          相关资源
          最近更新 更多