【问题标题】:Protobuf-Net serialize object with unaccessible to proto typeProtobuf-Net 序列化对象无法访问原型
【发布时间】:2014-11-03 17:23:55
【问题描述】:

假设我有以下课程:

public class Test {
  int x { get; set; }
  int y { get; set; }
  Vector3 coords { get; set; }
}

如果我不能在来自外部程序集的Vector3 类上使用[ProtoContract][ProtoMember(x)] 属性,我该如何序列化这个对象。

我已阅读How can I serialize a 3rd party type using protobuf-net or other serializers?,但它含糊不清(例如,我不知道是否可以混合使用 TypeModel 和属性方法,或者如果我选择仅使用,如何将未知类型成员作为字段添加到已知类型成员TypeModel 方法等),所以我需要一个具体的例子来说明我的情况。

例如,我这样声明 TypeModel:

RuntimeTypeModel.Default.Add(typeof(Vector3), false).Add(1, "x").Add(2, "y").Add(3, "z");
RuntimeTypeModel.Default.Add(typeof(SerializableTestClass), false).Add(1, "_x").Add(2, "_y").Add(3, "_coords");

序列化/反序列化:

if (GUILayout.Button("Serialize")) {
    SerializableTestClass testClass = new SerializableTestClass();
    testClass.changeMembers();
    RuntimeTypeModel.Default.Serialize(_serializedObject, testClass);
}

if (GUILayout.Button("Deserialize")) {
    SerializableTestClass test = (SerializableTestClass) RuntimeTypeModel.Default.Deserialize(_serializedObject, null, typeof(SerializableTestClass));
    Debug.Log("Deserialized object: " + test.ToString());
}

当我尝试序列化时,我得到一个错误:

InvalidOperationException: Duplicate field-number detected; 1 on: SerializableTestClass

更新 =============================

现在,我更改了我的代码,所以一切看起来像这样: 可序列化类:

[ProtoContract]
public class SerializableTestClass {
    [ProtoMember(1)]
    int _x { get; set; }
    [ProtoMember(2)]
    int _y { get; set; }
    [ProtoMember(3)]
    Vector3 _coords { get; set; }

    public SerializableTestClass() {
        Debug.Log("SerializableTestClass.ctor()");
        _x = 10;
        _y = 20;
        _coords = Vector2.one * 2;
    }

    public void changeMembers() {
        _x += -3;
        _y += 134;
        _coords *= 3;
    }

    public override string ToString() {
        return _x.ToString() + " " + _y + " " + _coords;
    }
}

型号:

_model = TypeModel.Create();
_model.Add(typeof(Vector3), false).Add(1, "x").Add(2, "y").Add(3, "z");
_model.Add(typeof(SerializableTestClass), true);

序列化/反序列化:

if (GUILayout.Button("Serialize")) {
    SerializableTestClass testClass = new SerializableTestClass();
    _serializedObject = new MemoryStream();
    testClass.changeMembers();
    _model.Serialize(_serializedObject, testClass);
}

if (GUILayout.Button("Deserialize")) {
    SerializableTestClass test = (SerializableTestClass) _model.Deserialize(_serializedObject, null, typeof(SerializableTestClass));
    Debug.Log("Deserialized object: " + test.ToString());
}

输出:10 20 (2.0, 2.0, 2.0)

应该是:7 154 (6.0, 6.0, 6.0)

【问题讨论】:

  • 你读过这个article吗?我认为这对你来说是一个很好的例子。
  • @Rotem 我读过它,但我需要的是确切的例子,因为我不知道我应该只向模型添加未知类型,还是应该将所有内容添加到模型中并一起省略属性。文档并不清楚。我尝试了不同的方法,但它们似乎不起作用。
  • 可以,可以混搭类型;所以你可以在Test/SerializableTestClass上使用属性,并使用RuntimeTypeModel来配置Vector3。重新“重复字段编号”...您确定您只使用一次Add(每个字段)吗?
  • @MarcGravell 是的,我确定。但是,我将它添加到 RuntimeTypeModel.Default。但是当我将类描述添加到通过 TypeModel.Create() 创建的类型模型时,序列化至少可以工作。不是我期望的那样,但至少它在调试中显示了一些东西。我在原始帖子中添加了更多说明。

标签: c# serialization protobuf-net


【解决方案1】:

这听起来很傻,但以下是我意外复制的方式:检查你没有在本地定义自己的ProtoContractAttribute;基本上,检查将光标放在[ProtoContract] 上然后按f12 或右键单击并转到显示定义时会发生什么。您应该看到的内容类似于:

然而,,可能在解析类型时,您不小心选择了“在(...各种选项...)中为 'ProtoContract' 生成类” - 如果您当时实际上没有参考,只需按 ctrl+.,enter (添加使用的快速方法)。这会生成一个文件,例如:

using System;

internal class ProtoContractAttribute : Attribute
{
}

这里的重点是它在错误的命名空间中,所以protobuf-net不会把它当作相关属性。

所以:如果你和我一样笨拙,这可能是原因......


不相关,但如果你在构造函数中有非默认值,你可能应该在反序列化过程中跳过构造函数;你可以通过:

[ProtoContract(SkipConstructor=true)]

以下是我使用常规 .NET 和伪造的 Vector3 的模型;它工作正常:

using ProtoBuf;
using ProtoBuf.Meta;
using System;
using System.IO;

[ProtoContract(SkipConstructor=true)]
public class SerializableTestClass
{
    [ProtoMember(1)]
    int _x { get; set; }
    [ProtoMember(2)]
    int _y { get; set; }
    [ProtoMember(3)]
    Vector3 _coords { get; set; }

    public SerializableTestClass()
    {
        _x = 10;
        _y = 20;
        _coords = Vector3.one * 2;
    }

    public void changeMembers()
    {
        _x += -3;
        _y += 134;
        _coords *= 3;
    }

    public override string ToString()
    {
        return _x.ToString() + " " + _y + " " + _coords;
    }
}

struct Vector3
{
    public int x, y, z;
    public static Vector3 one = new Vector3 { x = 1, y = 1, z = 1 };
    public static Vector3 operator *(Vector3 value, int times)
    {
        return new Vector3
        {
            x = value.x * times,
            y = value.y * times,
            z = value.z * times
        };
    }
    public override string ToString()
    {
        return string.Format("({0}, {1}, {2})", x, y, z);
    }
}
class Program
{
    static RuntimeTypeModel _model;
    static void Main()
    {
        _model = TypeModel.Create();
        _model.Add(typeof(Vector3), false).Add(1, "x").Add(2, "y").Add(3, "z");
        _model.Add(typeof(SerializableTestClass), true);

        SerializableTestClass testClass = new SerializableTestClass();
        var _serializedObject = new MemoryStream();
        testClass.changeMembers();
        Console.WriteLine("Original object: " + testClass.ToString());
        _model.Serialize(_serializedObject, testClass);

        _serializedObject.Position = 0;
        Console.WriteLine(_serializedObject.Length);
        SerializableTestClass test = (SerializableTestClass)_model.Deserialize(_serializedObject, null, typeof(SerializableTestClass));
        Console.WriteLine("Deserialized object: " + test.ToString());
    }
}

【讨论】:

  • 我的问题是序列化后我没有将内存流的位置设置为零。如果有某种异常就好了。
  • @GuardianX 0 是 protobuf 中完全有效的数据长度
  • 我明白了。把这么多时间花在这种微不足道的事情上还是很令人沮丧=(
猜你喜欢
  • 2011-09-11
  • 1970-01-01
  • 1970-01-01
  • 2023-03-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多