对象(通用)的 Protobuf-net 序列化抛出错误没有为类型定义序列化程序:System.Object

Posted

技术标签:

【中文标题】对象(通用)的 Protobuf-net 序列化抛出错误没有为类型定义序列化程序:System.Object【英文标题】:Protobuf-net serialization of object (generic) throws error No serializer defined for type: System.Object 【发布时间】:2020-11-24 00:56:48 【问题描述】:

如何使用 Protobuf-net 序列化一个 generic(T) 对象,该对象可以保存任何类型的数据(int / string / DateTime)。以下是我的代码

[ProtoContract]
public class E1DataRow

  [ProtoMember(1)]
  public List<NameValue> NameValues  get; set; 

  public E1DataRow()
  
    NameValues = new List<NameValue>();
  

  public void AddNameValue(string name, object obj, Type type)
  
      NameValues.Add(new NameValue  Name = name, Value = obj, ValueType = type );
  



[ProtoContract]
public class NameValue

  [ProtoMember(1)]
  public string Name  get; set; 
  [ProtoMember(2)]
  public object Value  get; set; 
  [ProtoMember(3)]
  public Type ValueType  get; set; 

序列化代码

var e1DataRows = new List<E1DataRow>();
/*
Code to add Data rows to e1DataRows
e1DataRow.AddNameValue(column.ColumnName, value, column.TypeOfColumn);
*/
using (var stream = File.OpenWrite(path))

  Serializer.Serialize(stream, e1DataRows);

[ProtoMember(2, DynamicType = true)]
public object Value  get; set; 

以上代码抛出以下错误(DynamicType = true) ProtoMemberAttribute.DynamicType' 已过时:'此版本中当前未实现引用跟踪和动态类型;他们可能会在以后恢复;这部分是由于对这些功能是否可行的怀疑,部分是由于对测试所有场景的信心(这需要时间;那个时间还没有发生);邀请反馈'

如果您能帮助了解如何使用 Protobug-net 序列化列表,那就太好了。谢谢...

【问题讨论】:

看起来 Protobuf-net 可以很好地处理泛型类型。尝试将public class NameValue 转换为public class NameValue&lt;T&gt; 或改用现有的KeyValuePair&lt;TKey,TValue&gt; @schnitz77 感谢您的 cmets。公共类NameValue&lt;T&gt; 的问题是我不能拥有不同类型的List&lt;NameValue&gt;(我猜)。 KeyValuePair&lt;TKey, TValue&gt; 是个好主意,但每次我添加新密钥时都会花费我进行散列操作。基本上,我正在尝试将数据集(SqlReader)转换为简单的 POCO 类进行序列化。 【参考方案1】:

您可能想查看 https://github.com/dotarj/protobuf-net-data - 这与 protobuf-net 无关(不同的作者等),但它使用 protobuf-net 来执行 DataTable 和数据的序列化-读者,所以它可能做你想要的现成的。


至于自己实现:

protobuf-net 根本不支持object(或dynamic,这只是object 的一种奇特的拼写方式)。有一些方法可以解决这个问题,基本上类似于 protobuf 中的 oneof 处理 - 即类似于(在 protobuf 术语中):

message Foo 
    oneof payload 
        string payload_string = 1;
        bool payload_bool = 2;
        int32 payload_int32 = 3;
        float payload_float = 4;
        // etc
    

由于“条件序列化”,这很容易在 protobuf-net 中组合在一起,这意味着您可以执行以下操作:

public object Value  get; set; 

[ProtoMember(1)]
public string ValueString

    get => (string)Value;
    set => Value = value;

public bool ShouldSerializeValueString()
    => Value is string;

[ProtoMember(2)]
public string ValueBoolean

    get => (bool)Value;
    set => Value = value;

public bool ShouldSerializeValueBoolean()
    => Value is string;

// etc

【讨论】:

感谢 Marc 在 protobuf-net 上所做的工作。我在使用 protobuf-net-data 时遇到了严重错误,只是意识到它可以与 protobuf-net 包的 2.3.2 版本一起使用。我了解 protobuf-net-data 的作者是不同的,但您是否知道是否有任何工作可以使其与您的最新版本的 protobuf-net一起使用> 包。无论如何,再次感谢您的出色工作。【参考方案2】:

如果在 c# >= 4 上,您可能需要尝试以下操作:

[ProtoContract]
public class E1DataRow

    [ProtoMember(1)]
    public List<NameValue<dynamic>> NameValues  get; set; 

    public E1DataRow()
    
        NameValues = new List<NameValue<dynamic>>();
    

    public void AddNameValue(string name, dynamic obj)
    
        NameValues.Add(new NameValue<dynamic>  Name = name, Value = obj );
    


public class NameValue<T>

    [ProtoMember(1)]
    public string Name  get; set; 
    [ProtoMember(2)]
    public T Value  get; set; 
    [ProtoMember(3)]
    public Type ValueType  get  return Value.GetType();  


不确定 Protobuf-net 是否喜欢 List&lt;NameValue&lt;dynamic&gt;&gt;,目前无法对其进行测试。

ProtoMember(3)ValueType 可能没有必要,因为无论如何都是只读的。

【讨论】:

以上是关于对象(通用)的 Protobuf-net 序列化抛出错误没有为类型定义序列化程序:System.Object的主要内容,如果未能解决你的问题,请参考以下文章

使用 protobuf-net 序列化数组时如何处理空值?

为啥我不能使用 ProtoBuf-Net 正确反序列化我的对象?

protobuf-net 中通用集合的序列化

Protobuf-net“反序列化期间引用跟踪对象更改引用”错误(2)

使用知道架构的 protobuf-net 反序列化未知对象

使用 protobuf-net 反序列化具有某些字段的派生类型的对象