65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 AnySerializer 进行二进制序列化

starIconstarIconstarIconstarIconstarIcon

5.00/5 (3投票s)

2018年12月3日

CPOL

3分钟阅读

viewsIcon

10968

如何二进制序列化您的类,而无需修改它们。

引言

最近,我需要序列化大量类并在不同时间点检测它们的更改。这实际上包含两个任务:序列化和计算对象的差异。通常,我会直接使用ProtoBuf并开始向所有类添加契约属性。这需要一些时间,并且最终会很麻烦,因为我需要更改大量的类和属性。由于ProtoBuf不喜欢序列化接口类型而不定义所有实现它的具体类,所以这也使得事情变得困难。我想要一种方法来直接序列化对象,并且在属性引用接口或包含具有接口引用或循环引用的泛型集合(例如 Entity Framework 数据模型)时不会抛出异常。

替代方案

我首先尝试使用ProtoBuf并用ProtoContracts修饰所有类。这也需要在任何接口上使用ProtoInclude,以便ProtoBuf知道实现它们的具体类。我还发现自己不得不排除许多它不喜欢的成员字段(委托、锁等),我的代码库开始变得杂乱无章。

最后我放弃了,我想尝试一下微软的BinaryFormatter,即使它生成的数据量大得多,而且性能远不如前者。至少在这里,我只需要添加[Serializable]属性,对吧?好吧,在遍历几百个类并用[NonSerializable]修饰字段以及用[IgnoreDataMember]修饰属性之后,它也开始变得杂乱无章了。我设法让它工作了,但过程很痛苦,而且有时会崩溃,尤其是我不得不开始修改 Entity Framework 类的时候。

Using the Code

开始使用AnySerializer非常非常简单。使用程序包管理器控制台将 nuget 包安装到您的项目中:Install-Package AnySerializer

像这样序列化任何你选择的對象

using AnySerializer.Extensions;

var originalObject = new SomeComplexTypeWithDeepStructure();

// serialize to binary data
var bytes = originalObject.Serialize();

// restore the object from binary data
var restoredObject = bytes.Deserialize<SomeComplexTypeWithDeepStructure>();

这就是序列化你的类所需做的全部工作!

高级控制

在某些情况下,简单的用法可能不适用于您的应用程序。这种情况可能是具有多个具体类型的接口。序列化器需要知道您想要恢复哪个具体类型,但这并不意味着您需要在编译时进行修饰!让我们尝试另一个示例

using AnySerializer.Extensions;

var originalObject = new VehicleContainer(new Car 
{ Color = "Red", Make = "Dodge", Model = "Challenger", DoorCount = 4});

// serialize to binary data
var bytes = originalObject.Serialize(SerializerOptions.EmbedTypes);

// restore the object from binary data
var restoredObject = bytes.Deserialize<VehicleContainer>(originalObject.GetType());

Assert.AreEqual(typeof(Car), restoredObject.Vehicle);

public class VehicleContainer
{
  public IVehicle Vehicle { get; set; }
  public VehicleContainer(IVehicle vehicle)
  {
    Vehicle = vehicle;
  }
}

public interface IVehicle
{
  string Color { get; }
  string Make { get; }
  string Model { get; }
}

public class Car : IVehicle
{
  string Color { get; }
  string Make { get; }
  string Model { get; }
  int DoorCount { get; }
}

public class Truck : IVehicle
{
  string Color { get; }
  string Make { get; }
  string Model { get; }
  string Is4x4 { get; } 
}

看到区别了吗?我们需要传递SerializerOptions来告诉序列化器嵌入任何需要它的类型的类型信息。这只会包含关于需要具体类型信息才能恢复的类型的相关信息。

还有很多其他场景有时很难序列化。以下是一些示例!

序列化匿名类型

using AnySerializer.Extensions;

var originalObject = new { Id = 1, Name = "John Doe" };

// serialize to binary data
var bytes = originalObject.Serialize();

// restore the object from binary data
var restoredObject = bytes.Deserialize(originalObject.GetType());

序列化任何集合类型(IEnumerableICollectionListDictionaryHashtable 等)

using AnySerializer.Extensions;

var colorsList = new List<string> { "Red", "Green", "Blue" };
var foodsDictionary = new Dictionary<string, string> 
{ { "Tomato", "Red" }, { "Banana", "Yellow" } };

// serialize to binary data
var colorsBytes = colorsList.Serialize();
var foodsDictionary = colorsList.Serialize();

// restore the object from binary data
var restoredColorsList = bytes.Deserialize<List<string>>();
var restoredFoodsDictionary = bytes.Deserialize<Dictionary<string,string>>();

序列化元组

using AnySerializer.Extensions;

var originalObject = new Tuple<int, string>(1, "John Doe");
var bytes = originalObject.Serialize();
var restoredObject = bytes.Deserialize<Tuple<int,string>>();

// or use C# 7 ValueTuples
var originalObject = (1, "John Doe");
var bytes = originalObject.Serialize();
var restoredObject = bytes.Deserialize<(int, string)>();

// C# 7 named Tuples
var originalObject = (id: 1, name: "John Doe");
var bytes = originalObject.Serialize();
var restoredObject = bytes.Deserialize<(int, string)>();

无构造函数或带参数的构造函数对象

using AnySerializer.Extensions;

var originalObject = ConstructorlessObject.Create();
var bytes = originalObject.Serialize();
var restoredObject = bytes.Deserialize<ConstructorlessObject>();

public class ConstructorlessObject
{
  private ConstructorlessObject() { }
  public static ConstructorlessObject Create()
  {
    return new ConstructorlessObject();
  }
}

var originalObject = new ConstructorWithParametersObject(1, "John Doe");
var bytes = originalObject.Serialize();
var restoredObject = bytes.Deserialize<ConstructorlessObject>();

public class ConstructorWithParametersObject
{
  public ConstructorlessObject(int id, string name) { }
}

自定义集合

using AnySerializer.Extensions;

var originalObject = new CustomCollectionObject(1, 2);
originalObject.Add("test1", 1);
originalObject.Add("test2", 2);
originalObject.Add("test3", 3);
var bytes = originalObject.Serialize();
var restoredObject = bytes.Deserialize<CustomCollectionObject>();

public class CustomCollectionObject : IEquatable<CustomCollectionObject>
{
  private readonly Dictionary<string, int> _innerStorage = new Dictionary<string, int>();
  private int _a;
  private int _z;
  public ICollection<string> Keys
  {
    get { return _innerStorage.Keys; }
  }

  public ICollection<int> Values
  {
    get { return _innerStorage.Values; }
  }

  public void Add(string key, int value)
  {
    if (!_innerStorage.ContainsKey(key))
    {
       _innerStorage.Add(key, value);
    }
  }

  public int this[string key]
  {
    get
    {
      if (!_innerStorage.TryGetValue(key, out int item))
      {
        _innerStorage.Add(key, item);
      }
      return item;
    }
    set
    {
       _innerStorage[key] = value;
    }
  }

  public CustomCollectionObject(int a, int z)
  {
    _a = a;
    _z = z;
  }
}

它与其他序列化器相比如何?

功能 AnySerializer ProtoBuf BinaryFormatter ZeroFormatter
无需声明 X      
循环引用 X X    
接口 X x    
无构造函数 X x    
.Net Core X X X X
.Net Framework X X X X
压缩 X      
图支持 X x X X
匿名 X      
Private X X X X
忽略属性 X      
忽略字段 X      
大小 59b 32b 414b 414b
速度* 序列化/反序列化 3 / 2 2 / 1 10 / 8 1 / 2

X - 支持的功能
x - 表示对功能的声明式支持
* - 速度参考(序列化/反序列化)排名为 1-10,数值越低越好。

许多其他序列化器难以处理的场景,使用AnySerializer都可以轻松实现。查看描述性的Wiki,了解如何完成几乎所有事情的详细信息!

历史

  • 2018年12月3日:初始版本
© . All rights reserved.