C# Protobuf Net序列化对象,对象的原型不可访问

C# Protobuf Net序列化对象,对象的原型不可访问,c#,serialization,protobuf-net,C#,Serialization,Protobuf Net,假设我有一个下面的类: public class Test { int x { get; set; } int y { get; set; } Vector3 coords { get; set; } } 如果我不能使用[ProtoContract]和[ProtoMember(x)]来自外部程序集的Vector3类的属性,如何序列化此对象 我读过,但它很模糊(例如,我不知道我是否可以混合使用TypeModel和attributes方法,或者如果我选择只使用TypeModel方法,如

假设我有一个下面的类:

public class Test {
  int x { get; set; }
  int y { get; set; }
  Vector3 coords { get; set; }
}
如果我不能使用
[ProtoContract]
[ProtoMember(x)]
来自外部程序集的
Vector3
类的属性,如何序列化此对象

我读过,但它很模糊(例如,我不知道我是否可以混合使用TypeModel和attributes方法,或者如果我选择只使用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());
}
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());
}
当我尝试序列化时,我得到一个错误:

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();
    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());
}
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());
}
输出:
1020(2.0、2.0、2.0)


应该是:
7154(6.0,6.0,6.0)

这听起来很傻,但以下是我无意中复制的方式:检查您没有本地定义自己的
ProtoContractAttribute
;基本上,检查将光标放在
[ProtoContract]
上,然后按f12键或右键单击并转到“显示定义”时发生的情况。您应该看到的是:

但是,在解析类型时,您可能意外地在(…各种选项…)中选择了“为“ProtoContract”生成类”-如果您当时实际上没有引用,只需按ctrl+,enter(添加usings的快速方式),则可以很容易地完成此操作。这会生成一个类似以下内容的文件:

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());
    }
}

可能的副本你读过这个吗?我认为这对你来说是一个很好的例子。@Rotem我读了它,但我需要的是一个确切的例子,因为我不知道是应该只向模型中添加未知类型,还是应该向模型中添加所有内容并同时省略属性。文件对此并不清楚。我尝试了不同的方法,但它们似乎不起作用;因此,您可以使用
Test
/
SerializableTestClass
上的属性,并使用
RuntimeTypeModel
配置
Vector3
。关于“重复字段编号”。。。你确定你只使用
添加
一次(每个字段)?@marcGravel是的,我确定。但是,我将其添加到RuntimeTypeModel.Default中。但是,当我将类描述添加到通过TypeModel.Create()创建的类型模型中时,序列化至少起作用。不是我期望的那样,但至少它在调试中显示了一些东西。我在原来的帖子中添加了更多的澄清。我的问题是在序列化之后我没有将内存流的位置设置为零。如果出现某种异常,那就太好了。@GuardianX 0是protobufof中完全有效的数据长度。把这么多时间花在这件无关紧要的事情上还是很令人沮丧的=(