Protobuf-net:序列化包含 Dictionary 的自定义类

本文关键字:object 自定义 序列化 包含 Dictionary Protobuf-net | 更新日期: 2023-09-27 18:31:44

我正在使用Marc Gravell的ProtoBuf-net库(r480,net20)来序列化/反序列化包含已知类型的字典<object, object>的自定义类,该字典用于服务器/客户端场景(均为C#)。
这将取代我们目前使用二进制格式化程序的方法。
作为基础,我遵循这里提出的建议:protobuf-and-listobject-how-to-serialize-deserialize 和 protobuf-and-listobject-how-to-serialize-deserialize.

目前的方法有一些缺点,我希望更熟悉Protobuf-net的人可以给我一个如何改进它的提示。

  1. 在 OnSerialising() 调用中将字典<object, object>复制到字典<ProtoObject, ProtoObject>
  2. 添加新类型的维护开销(每个类型都需要 ProtoInclude 标记和 ProtoObject.Create(object obj)中的相应强制转换逻辑)
  3. ProtoObject 必须知道所有必需的类型。这会导致项目之间的循环引用问题,这只能通过对项目结构进行更大的重构来解决。

理想情况下,我想使用 RuntimeTypeModel 方法,但我看不出如何让客户端知道这些类型(编译 TypeModel dll 并将其传输到客户端?

同样在第一个主题中,Marc Gravell 提到即将推出的"运行时可扩展模式"可能会有所帮助,有谁知道它们是否已经实现以及它们是如何工作的?
我非常感谢我得到的任何回复,如果我能澄清更多的事情,请告诉我。
无论如何,感谢Marc Gravell的精彩图书馆:)。

代码如下:

[Serializable]
[ProtoContract]
public class Attributes : IXmlSerializable, IEnumerable, IEquatable<Attributes>, ICloneable
{
    // Non ProtoBuf-net relevant code was removed
    private Dictionary<object, object> attributes = new Dictionary<object, object>();
    [ProtoMember(1)]
    private Dictionary<ProtoObject, ProtoObject> protoDictionary;
    [OnSerializing]
    public void OnSerializing(StreamingContext context)
    {
        this.protoDictionary = new ProtoDictionary();
        foreach (var attribute in attributes)
        {
            this.protoDictionary.Add(ProtoObject.Create(attribute.Key), ProtoObject.Create(attribute.Value));
        }
    }
    [OnDeserialized]
    public void OnDeserialized(StreamingContext context)
    {
        if (this.protoDictionary != null)
        {
            this.attributes = new SerializableHashtable();
            foreach (var o in this.protoDictionary)
            {
                this.attributes.Add(o.Key.Value, o.Value.Value);
            }
        }
    }
}
[ProtoContract]
[ProtoInclude(1, typeof(ProtoObject<bool>))]
[ProtoInclude(2, typeof(ProtoObject<byte>))]
[ProtoInclude(3, typeof(ProtoObject<sbyte>))]
[ProtoInclude(4, typeof(ProtoObject<ushort>))]
[ProtoInclude(5, typeof(ProtoObject<short>))]
[ProtoInclude(6, typeof(ProtoObject<uint>))]
[ProtoInclude(7, typeof(ProtoObject<int>))]
[ProtoInclude(8, typeof(ProtoObject<ulong>))]
[ProtoInclude(9, typeof(ProtoObject<long>))]
[ProtoInclude(10, typeof(ProtoObject<float>))]
[ProtoInclude(11, typeof(ProtoObject<double>))]
[ProtoInclude(12, typeof(ProtoObject<decimal>))]
[ProtoInclude(13, typeof(ProtoObject<string>))]
[ProtoInclude(20, typeof(ProtoObject<Vector2F>))]
[ProtoInclude(21, typeof(ProtoObject<Vector3F>))]
[ProtoInclude(22, typeof(ProtoObject<Shape>))]
[ProtoInclude(23, typeof(ProtoObject<SharedUser>))]
[ProtoInclude(24, typeof(ProtoObject<SharedShip>))]
//[ProtoInclude(25, typeof(ProtoObject<IVehicleConfiguration>))] // Requires Steering dll -> cyclic reference
[ProtoInclude(26, typeof(ProtoObject<DroneState>))]
[ProtoInclude(27, typeof(ProtoObject<BuffCode>))]
[ProtoInclude(28, typeof(ProtoObject<ItemAttribute>))]
[ProtoInclude(40, typeof(ProtoObject<List<int>>))]
public abstract class ProtoObject
{
    protected ProtoObject()
    {
    }
    // Replaces public static ProtoObject<T> Create<T>(T value)
    // in order to use the actual type of the object
    public static ProtoObject Create(object obj)
    {
        if (obj is bool)
        {
            return new ProtoObject<bool>((bool)obj);
        }
        if (obj is byte)
        {
            return new ProtoObject<byte>((byte)obj);
        }
        // etc. for all required types
        return null;
    }
    public static ProtoObject Create(bool obj)
    {
        TypeModel.Add(obj.GetType(), true);
        return new ProtoObject<bool>(obj);
    }
    public static ProtoObject Create(byte obj)
    {
        return new ProtoObject<byte>(obj);
    }
    // ... public static ProtoObject Create(type obj) -> for all required types
    public object Value
    {
        get { return ValueImpl; }
        set { ValueImpl = value; }
    }
    protected abstract object ValueImpl { get; set; }   
}
[ProtoContract]
public sealed class ProtoObject<T> : ProtoObject
{
    public ProtoObject()
    {
    }
    public ProtoObject(T value)
    {
        Value = value;
    }
    [ProtoMember(1)]
    public new T Value { get; set; }
    protected override object ValueImpl
    {
        get { return Value; }
        set { Value = (T)value; }
    }
    public override string ToString()
    {
        return Value.ToString();
    }
}

Protobuf-net:序列化包含 Dictionary<object、object> 的自定义类

序列化

Dictionary<object,object>根本不是受支持的用例...就个人而言,我认为您应该更多地考虑使用特定于用途的DTO模型,例如使用XmlSerializerDataContractSerializerJavascriptSerializer。protobuf-net最终仍然是一个合约序列化器,DTO模型是理想的用例。通常它适用于非DTO模型,但这并不是一个公开的保证,即它可以与可能设计的每个模型一起使用。