使用protobuf-net.dll版本1.0.0.280
当我反序列化DateTime
(包裹在对象中)时,日期/时间没问题,但DateTime.Kind
属性为'未指定'
考虑这个测试用例来序列化/反序列化DateTime。
[TestMethod]
public void TestDateTimeSerialization()
{
var obj = new DateTimeWrapper {Date = DateTime.UtcNow};
obj.Date = DateTime.SpecifyKind(obj.Date, DateTimeKind.Utc);
var serialized = obj.SerializeProto();
var deserialized = serialized.DeserializeProto<DateTimeWrapper>();
Assert.AreEqual(DateTimeKind.Utc, deserialized.Date.Kind);
}
public static byte[] SerializeProto<T>(this T item) where T : class
{
using (var ms = new MemoryStream())
{
Serializer.Serialize(ms, item);
return ms.ToArray();
}
}
public static T DeserializeProto<T>(this byte[] raw) where T : class, new()
{
using (var ms = new MemoryStream(raw))
{
return Serializer.Deserialize<T>(ms);
}
}
Assert失败,Kind == Unspecified
由于protobuf-net没有序列化这个属性(见下文),一个解决方案就是假设DateTimeKind
在客户端显示日期时等于Utc(只有你知道的地方) strong>当然应该是UTC):
public static DateTime ToDisplayTime(this DateTime utcDateTime, TimeZoneInfo timezone)
{
if (utcDateTime.Kind != DateTimeKind.Utc)//may be Unspecified due to serialization
utcDateTime = DateTime.SpecifyKind(utcDateTime, DateTimeKind.Utc);
DateTime result = TimeZoneInfo.ConvertTime(utcDateTime, timezone);
return result;
}
这样可以节省您必须分配给接收方的每个DateTime
属性。
答案 0 :(得分:6)
protobuf.net必须保持与protobuf二进制格式的兼容性,该格式是为Java日期/时间数据类型设计的。 Java中没有Kind
字段 - &gt; protobuf二进制格式中没有Kind
支持 - &gt; Kind
未通过网络传输。或者沿着这些方向发展。
事实证明,protobuf.net对Ticks
字段进行编码(仅限),您可以在BclHelpers.cs
中找到代码。
但随意在protobuf消息定义中为此值添加另一个字段。
答案 1 :(得分:5)
作为Ben答案的延伸......严格来说,protobuf 没有时间定义,所以没有什么可以保持与...的兼容性。我很想在v2中添加对此的支持,但遗憾的是每个值会增加2个字节。我还没有考虑这是否可以接受......例如,我可能默认为“未指定”,因此只有明确的本地或UTC日期才有值。
答案 2 :(得分:2)
另一种解决方案是更改DTO的kind属性并始终将其设置为UTC。这可能不适用于所有应用程序,但对我有用
class DateTimeWrapper
{
private DateTime _date;
public DateTime Date
{
get { return _date; }
set { _date = new DateTime(value.Ticks, DateTimeKind.Utc);}
}
}
使用protobuf超过一年并集成C#,Java,Python和Scala后,我得出结论,应该使用DateTime的长表示。例如使用UNIX时间。将C#DateTime protobuf对象转换为其他语言DateTime很痛苦。然而,所有人都理解一个简单的事情。
答案 3 :(得分:1)
这是一种变通方法的实现。如果您能找到更好的解决方案,请与我们联系。谢谢!
[ProtoContract(SkipConstructor = true)]
public class ProtoDateTime
{
[ProtoIgnore]
private DateTime? _val;
[ProtoIgnore]
private DateTime Value
{
get
{
if (_val != null)
{
return _val.Value;
}
lock (this)
{
if (_val != null)
{
return _val.Value;
}
_val = new DateTime(DateTimeWithoutKind.Ticks, Kind);
}
return _val.Value;
}
set
{
lock (this)
{
_val = value;
Kind = value.Kind;
DateTimeWithoutKind = value;
}
}
}
[ProtoMember(1)]
private DateTimeKind Kind { get; set; }
[ProtoMember(2)]
private DateTime DateTimeWithoutKind { get; set; }
public static DateTime getValue(ref ProtoDateTime wrapper)
{
if (wrapper == null)
{
wrapper = new ProtoDateTime();
}
return wrapper.Value;
}
public static DateTime? getValueNullable(ref ProtoDateTime wrapper)
{
if (wrapper == null)
{
return null;
}
return wrapper.Value;
}
public static void setValue(out ProtoDateTime wrapper, DateTime value)
{
wrapper = new ProtoDateTime { Value = value };
}
public static void setValue(out ProtoDateTime wrapper, DateTime? newVal)
{
wrapper = newVal.HasValue ? new ProtoDateTime { Value = newVal.Value } : null;
}
}
用法:
[ProtoContract(SkipConstructor = true)]
public class MyClass
{
[ProtoMember(3)]
[XmlIgnore]
private ProtoDateTime _timestampWrapper { get; set; }
[ProtoIgnore]
public DateTime Timestamp
{
get
{
return ProtoDateTime.getValue(ref _timestampWrapper);
}
set
{
return ProtoDateTime.setValue(out _timestampWrapper, value);
}
}
[ProtoMember(4)]
[XmlIgnore]
private ProtoDateTime _nullableTimestampWrapper { get; set; }
[ProtoIgnore]
public DateTime? NullableTimestamp
{
get
{
return ProtoDateTime.getValueNullable(ref _nullableTimestampWrapper);
}
set
{
return ProtoDateTime.setValue(out _nullableTimestampWrapper, value);
}
}
}
答案 4 :(得分:1)
对于protobuf来说,使用UtcKind自动反序列化DateTime可能更有意义,如果你使用Utc作为你的基础,那我认为这是最佳实践,你不会有任何问题。
答案 5 :(得分:1)
假设您只需要一个DateTimeKind
(即UTC
或Local
),那就是一个简单(但不是很漂亮)的解决方案。
由于内部protobuf-net将DateTime
转换为Unix-Time表示,因此它有一个DateTime
值代表Unix纪元(1970/01/01),它每个都添加相关的delta时间。
如果您使用UTC
或Local
DateTime
值的反射替换该值,则您的所有DateTime
都将具有指定的DateTimeKind
:
typeof (BclHelpers).
GetField("EpochOrigin", BindingFlags.NonPublic | BindingFlags.Static).
SetValue(null, new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc));
您可以阅读有关my blog
的更多信息答案 6 :(得分:0)
从protobuf-net 2.2(请参阅commit)开始,可以选择加入DateTime.Kind的序列化。您可以设置全局标志。 github上的相应issue(仍然打开)。
这是与NServiceBus相关的usage example。
免责声明:这对于OP所指的旧protobuf-net版本无济于事,但这是一个古老的问题,可能对其他人有帮助。