我在我的应用程序中使用protobuf-net进行序列化/反序列化。我正面临一个问题。
[ProtoContract()]
ClsTest
{
private bool _isPeriodic
[ProtoMember(1)]
public bool IsPeriodic
{
get
{
return _isPeriodic;
}
set
{
isPeriodic = value;
}
}
}
我在我的碰撞对象中使用这个类。
序列化过程工作正常,但在反序列化后,默认情况下属性IsPeriodic的值为true,尽管在某些情况下它是假的。任何人都可以帮助我吗?
答案 0 :(得分:6)
我的猜测是,您的代码正在为默认IsPeriodic
个实例设置true
到new
,可能是:
private bool _isPeriodic = true;
或者,在构造函数中:
_isPeriodic = true; // or IsPeriodic = true
基本上,有一个隐式默认值(遵循protobuf语言指南)where-by bool假定默认值为false
。它不会发送被认为是默认值的数据。如果此默认值不正确,请告诉它:
[ProtoMember(1), DefaultValue(true)]
或IIRC您可以尝试将IsRequired
设置为true
:
[ProtoMember(1, IsRequired = true)]
并且有一些其他方法可以告诉它始终发送值:
private bool ShouldSerializeIsPeriodic() { return true;}
(它使用核心.NET支持的PropertyGrid
,XmlSerializer
,PropertyDescriptor
等相同的模式 - 这不是我发明的随机模式)
请注意,在“v2”中,我进行了两次更改以帮助消除这种奇怪现象:
答案 1 :(得分:1)
我发现使用bool和enum类型与protobuf-net有一些技巧。 关于bool和enum类型的默认值的第一个问题: 这是我的Linq代码段:
[ProtoContract]
public class MyOption
{
[ProtoMember(2)]
public View m_printListView = View.Details; (A)
[ProtoMember(5) ]
public bool m_bool = true ; (B)
}
void Main()
{
string fname = @"D:/test.dat";
if (File.Exists(fname) )
{
File.Delete(fname);
}
using(FileStream fs= new FileStream(fname, FileMode.OpenOrCreate, FileAccess.Write) )
{
MyOption opt = new MyOption();
opt.m_printListView = View.LargeIcon; // (1)
opt.m_bool = false; // (2)
Serializer.SerializeWithLengthPrefix(fs, opt, PrefixStyle.Fixed32);
}
using(FileStream fs= new FileStream(@"D:/test.dat", FileMode.Open, FileAccess.Read) )
{
MyOption opt;
opt = Serializer.DeserializeWithLengthPrefix<MyOption>(fs, PrefixStyle.Fixed32);
Console.WriteLine(opt.m_printListView);
Console.WriteLine(opt.m_bool);
}
}
现在,猜猜输出。它&#39; S:
Details
True
请注意,在(A)和(B)中,我将默认值设置为View.Details和true。 在(1)和(2)中,在proto-buf的序列化之后,我明确地将值设置为View.LargeIcon和false。 反序列化,我们得到了错误的值。
原因是:对于bool值,默认值为false,根据proto-buf的设计原则,它会尽可能节省空间,因此默认值不保存在文件中,只有一个标志保存以指示应使用默认值(我猜,未经验证)。
当反序列化时,首先调用默认构造函数,并且行(B)实际上是CLR运行时的默认构造函数的一部分,然后proto-buf反序列化过程启动,并发现成员m_bool有一个默认值标志,然后使用默认值false设置m_bool,它覆盖(B)处的默认值。
对于枚举类型,原因类似,在上面的示例中,View.LargeIcon是默认值,其数值为0(由Reflected验证)。
使用bool和enum成员的DefaultValueAttribute修复它:
[ProtoContract]
public class MyOption
{
[ProtoMember(2), DefaultValue(View.Details)]
public View m_printListView = View.Details; (A)
[ProtoMember(5), DefaultValue(true) ]
public bool m_bool = true ; (B)
}
对于枚举类型,还有其他问题: 第一个是所有枚举器都应该有不同的值,否则proto-buf会在序列化时抛出异常,例如System.Drawing.RotateFlip枚举类型具有以下定义:
public enum RotateFlipType
{
Rotate180FlipNone = 2,
Rotate180FlipX = 6,
Rotate180FlipXY = 0,
Rotate180FlipY = 4,
Rotate270FlipNone = 3,
Rotate270FlipX = 7,
Rotate270FlipXY = 1,
Rotate270FlipY = 5,
Rotate90FlipNone = 1,
Rotate90FlipX = 5,
Rotate90FlipXY = 3,
Rotate90FlipY = 7,
RotateNoneFlipNone = 0,
RotateNoneFlipX = 4,
RotateNoneFlipXY = 2,
RotateNoneFlipY = 6
}
从图像处理的角度来看,RotateNoneFlipNone和Rotate180FlipXY具有相同的效果,所以他们有 相同的潜在价值,它是一个合理的设计,然而,这样的枚举不能与proto-buf一起使用:
The enum System.Drawing.RotateFlipType has conflicting values RotateNoneFlipNone and RotateNoneFlipNone
Serializer.ThrowInner (Exception exception)
at ProtoBuf.Serializer.ThrowInner(Exception exception)
at ProtoBuf.Serializer.Serialize[T](Stream destination, T instance)
at ProtoBuf.Serializer.SerializeWithLengthPrefix[T](Stream destination, T instance, PrefixStyle style, Int32 tag)
我的解决方法是创建自己的枚举,并使用My_RotateFlipType和System.Drawing.RotateFlipType之间的一对一映射。只有My_RotateFlipType将由proto-buf序列化。
public enum RotateFlipType public enum My_RotateFlipType
{ {
Rotate180FlipNone = 2, Rotate180FlipNone,
Rotate180FlipX = 6, Rotate180FlipX,
Rotate180FlipXY = 0, Rotate180FlipXY,
Rotate180FlipY = 4, Rotate180FlipY,
Rotate270FlipNone = 3, Rotate270FlipNone,
Rotate270FlipX = 7, Rotate270FlipX,
Rotate270FlipXY = 1, Rotate270FlipXY,
Rotate270FlipY = 5, Rotate270FlipY,
Rotate90FlipNone = 1, Rotate90FlipNone,
Rotate90FlipX = 5, Rotate90FlipX,
Rotate90FlipXY = 3, Rotate90FlipXY,
Rotate90FlipY = 7, Rotate90FlipY,
RotateNoneFlipNone = 0, RotateNoneFlipNone,
RotateNoneFlipX = 4, RotateNoneFlipX,
RotateNoneFlipXY = 2, RotateNoneFlipXY,
RotateNoneFlipY = 6 RotateNoneFlipY
} }
为避免手动与两个数据成员同步,我使用ProtoBeforeSerialization和OnProtoAfterDeserialization功能自动化它:
[ProtoAfterDeserialization()]
public void OnProtoAfterDeserialization()
{
Console.WriteLine("called OnProtoAfterDeserialization");
bool ret = Enum.TryParse(m_rotate.ToString(), out m_rotate_protobuf);
}
[ProtoBeforeSerialization()]
public void OnProtoBeforeSerialization()
{
Console.WriteLine("called OnProtoBeforeSerialization");
bool ret = Enum.TryParse(m_rotate_protobuf.ToString(), out m_rotate);
}
关于枚举的第二个问题是0值枚举器。如果枚举没有值为0的枚举器,那么 protobuf很容易在运行时抛出异常。
使用protobuf-net时我会遵守规则: 1.只要默认构造函数设置了默认值以外的值,就使用DefaultValueAttribute。 2.对于系统或第三方枚举类型,在将其添加到protobuf之前,请使用反射器(静态)或linq(运行时)检查它是否存在上述问题。如果发生冲突,请使用上述解决方法。
答案 2 :(得分:0)
以下对我来说很好:
[ProtoContract]
class ClsTest
{
[ProtoMember(1)]
public bool IsPeriodic { get; set; }
}
反序列化:
// stream is a NetworkStream object
ClsTest clsTestObj = Serializer.DeserializeWithLengthPrefix<ClsTest>(stream, PrefixStyle.Fixed32);
bool value = clsTestObj.IsPeriodic;
答案 3 :(得分:0)
我遇到了类似的问题,默认为bool
的{{1}}成员未从配置文件中读取其值。
true