使用 AnySerializer 进行二进制序列化





5.00/5 (3投票s)
如何二进制序列化您的类,而无需修改它们。
引言
最近,我需要序列化大量类并在不同时间点检测它们的更改。这实际上包含两个任务:序列化和计算对象的差异。通常,我会直接使用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());
序列化任何集合类型(IEnumerable
、ICollection
、List
、Dictionary
、Hashtable
等)
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日:初始版本