Suppose I have a following class:
public class Test {
int x { get; set; }
int y { get; set; }
Vector3 coords { get; set; }
}
How can I serialize this object if I canont use [ProtoContract]
and [ProtoMember(x)]
attributess on Vector3
class, which comes from external assembly.
I have read How can I serialize a 3rd party type using protobuf-net or other serializers? but it is vague (for example I don't know if I can mix TypeModel and attributes approach, or how to add unknowd type member as a field to known type member if I choose to use only TypeModel approach etc.), so I need a concrete example for my situation.
For example, I declare TypeModel like this:
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");
Serialization/deserialization:
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());
}
And when I try to serialize, I get an error:
InvalidOperationException: Duplicate field-number detected; 1 on: SerializableTestClass
UPDATE ============================
Now, I changed my code so everything looks like this: Serializable class:
[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:
_model = TypeModel.Create();
_model.Add(typeof(Vector3), false).Add(1, "x").Add(2, "y").Add(3, "z");
_model.Add(typeof(SerializableTestClass), true);
Serialization/Deserialization:
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());
}
Output: 10 20 (2.0, 2.0, 2.0)
Should be: 7 154 (6.0, 6.0, 6.0)
This is going to sound silly, but the following is how I reproduced this accidentally: check you don't have your own ProtoContractAttribute
defined locally; basically, check what happens when you put the cursor on [ProtoContract]
and then either press f12, or right-click and go to show definition. What you should see is something like:
It is, however, possible that when resolving types, you accidentally selected "Generate class for 'ProtoContract' in (...various options...)" - this is done very easily if you don't actually have the reference at the time and just press ctrl+.,enter (the quick way of adding usings). This generates instead a file like:
using System;
internal class ProtoContractAttribute : Attribute
{
}
The important point here is that it is in the wrong namespace, so protobuf-net doesn't treat it as a relevant attribute.
So: if you're as clumsy as me, this could be the reason...
Unrelated, but if you have non-default values in the constructor, you should probably skip the constructor during deserialization; you do that via:
[ProtoContract(SkipConstructor=true)]
The following is my mockup using regular .NET and a faked Vector3
; it works fine:
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());
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With