只想分享一下关于R561版protobuf-net的观察结果。使用DateTimeOffSet
时,例外
InvalidOperationException(没有为类型定义的序列化程序: System.DateTimeOffset)
出现:
我添加了一个带有getProto()
和StreamWriter
的方法来编写一个原型文件,现在它可以工作了!(并且原型文件也完全可用)。但是,如果我评论此方法,则会发生相同的异常。我真的不明白。
希望这种观察可能有用。
我会尽量让自己更清楚。我有一个C#客户端,其中包含一些使用DateTimeOffset参数的对象。我用protobuf-net(r561)对它们进行了序列化,并添加了一个writeProtoFile()方法来编写一个带有getProto()方法的proto文件。序列化似乎完美无缺,原型文件也可以。所以,因为我现在有我的proto文件,我可以评论或压制方法writeProtoFile():我不需要其他原型文件。所以这是我的第一个问题:
- >当这个方法(只是写一个调用getProto()方法的原型文件)被注释或者压缩时,为什么序列化不再起作用?这是我得到的例外:
没有为类型定义序列化程序:System.DateTimeOffset。
当我取消注释writeProtoFile()注释时,它可以工作。这是方法:
public static void writeProtoFile(String proto)
{
StreamWriter file = new StreamWriter("c:\\MyprotoFiles\\MyProtoFile.proto");
file.Write(proto);
file.Close();
}
我需要这个对象被java客户端使用。使用proto编译器生成的java类似乎没问题,但是当我反序列化它时,我得到了一个例外:
com.google.protobuf.InvalidProtocolBufferException:在解析协议消息时,输入意外地在字段中间结束。这可能意味着要么输入被截断,要么嵌入的消息误报了自己的长度。
我认为,原因是生成了DateTimeOffset类(在proto中,dateTimeOffset什么都不包含)
message DateTimeOffset {
}
DateTimeOffset类型存在于Java中,所以这是我的第二个问题: - >是否有任何方法可以将C#中的dateTimeOffset参数序列化,然后在反序列化后成为java中的dateTimeOffset参数?
答案 0 :(得分:2)
是否有任何方法可以将C#中的dateTimeOffset参数序列化,然后在反序列化后成为java中的dateTimeOffset参数?
对于任何语言,都没有针对DateTime
或DateTimeOffset
值进行基于.proto的定义处理,因此没有甚至没有远程保证的传输方式平台之间的数据通过protobuf(或任何特定的实现,如protobuf-net)。此外,虽然在2个不同的平台上有一个名为DateTimeOffset
的类型,但这本身并不足以保证它们具有相似的语义/范围/等。
对于任何跨平台的场景,我建议只使用非常基本的数据,甚至可能只是像整数(64位)那样将偏移量存储在1970年代的毫秒内。或类似的东西。
为什么当这个方法(只是写一个调用getProto()方法的原型文件)被注释或压缩时,序列化不再起作用了?
protobuf-net不使用任何getProto
方法或writeProtoFile
方法。我会非常谨慎地怀疑评论/取消评论这是改变一些内部行为,并需要一个具体的责任来调查。说实话,这听起来极不可能。警告:有一个Serializer.GetProto<T>
方法,但是它做了一些非常不同的事情(并且在v2 API中被GetSchema(Type)
取代了。)
然而,完全正确地说:
没有为类型定义序列化程序:System.DateTimeOffset。
原因很简单,我还没有为这种类型定义标准的序列化器。如果您可以为此定义某些标准处理,您可以使用SetSurrogate
将其连接到您选择的任何线表示。
回复:
在解析协议消息时,输入意外地在字段中间结束
这不应该以任何方式发生。这听起来像一个无关的问题,很可能是在传输数据时数据损坏(例如,数据的错误编码),或者在不截断它的情况下覆盖预先存在的文件(最后留下垃圾)。如果您可以说明完全如何在平台之间传输二进制文件,我可能会建议更多,但是这里要研究的第一件事是:您发送的二进制数据与二进制数据相同吗?收到了(与protobuf无关 - 只是一个简单的问题:你是否正确地传输了我的BLOB?它的长度是否相同?每个字节是否相同?)
答案 1 :(得分:0)
这是java的反序列化方法:
public MyObjectProto.MyObject deserialize(byte[] array) {
try {
CodedInputStream stream = CodedInputStream.newInstance(array);
return MyObjectProto.MyObject.parseFrom(stream);
} catch (IOException ex) {
displayLogs("Error while deserializing the content of message : " + ex);
return null;
}
}
这种反序列化方法可能会破坏它吗?
答案 2 :(得分:0)
我使用此方法在C#中序列化:
MemoryStream ms = new MemoryStream();
Serializer.Serialize<MyObjectType>(ms, myObject);
byte[] array = ms.ToArray();
ms.Close();
然后我将它发送到ActiveMQ主题(pub-sub模型) Java以异步方式接收它,并使用前面的方法对其进行反序列化(在我的第一个答案中)。 我读了数组,看起来它没有被破坏,但是一个是签名而另一个没有。 在C#中,我有10个值高于126.而这些相同的值在Java中成为负数:
byte #58 = 144 in C# , -112 in Java
byte #67 = 160 in C# , -96 in Java
经过多次测试后,当我不对dataTimeOffset参数进行序列化时,似乎这些值会消失。
答案 3 :(得分:0)
我遇到了同样的问题,并为 proto 序列化编写了一个包装对象(请参阅代码的答案结尾)。它有单独的 Ticks 和 Offset 字段。为了更轻松地使用助手,我添加了一些运算符。
您可以将 DateTimeOffset
对象直接分配给辅助类:
[ProtoContract]
public class ExampleContract {
[ProtoMember(1)]
public ProtoDateTimeOffset LastLoginTime { get; set; }
}
var contract = new ExampleContract
{
LastLoginTime = DateTimeOffset.Now
};
反序列化时,您可以直接使用 ProtoDateTimeOffset 实例,也可以在 DTO 中创建重载:
public DateTimeOffset LastLogin { get; set; }
[ProtoMember(4)]
public ProtoDateTimeOffset? ProtoLastLogin
{
get => LastLogin;
set => LastLogin = value;
}
这是一个特定于 C# 的实现,我不知道您可以使用哪些语言进行刻度和偏移,但在这些语言中的转换应该是微不足道的。
helper 类的实际代码:
/// <summary>
/// Proto contract that represents a <see cref="DateTimeOffset"/>
/// </summary>
[ProtoContract]
public class ProtoDateTimeOffset
{
/// <summary>
/// Utc ticks
/// </summary>
[ProtoMember(1)]
public long Ticks { get; set; }
/// <summary>
/// The UTC offset in minutes
/// </summary>
[ProtoMember(2)]
public double OffsetMinutes { get; set; }
/// <summary>
/// Operator to cast <see cref="ProtoDateTimeOffset"/> to <see cref="DateTimeOffset"/>
/// </summary>
/// <param name="other"></param>
/// <returns></returns>
public static implicit operator DateTimeOffset?(ProtoDateTimeOffset? other)
{
if (other == null)
return null;
return new DateTimeOffset(other.Ticks, TimeSpan.FromMinutes(other.OffsetMinutes));
}
/// <summary>
/// Operator to cast <see cref="ProtoDateTimeOffset"/> to <see cref="DateTimeOffset"/> or default
/// </summary>
/// <param name="other"></param>
/// <returns></returns>
public static implicit operator DateTimeOffset(ProtoDateTimeOffset? other)
{
if (other == null)
return default;
return new DateTimeOffset(other.Ticks, TimeSpan.FromMinutes(other.OffsetMinutes));
}
/// <summary>
/// Operator to cast <see cref="DateTimeOffset"/> to <see cref="ProtoDateTimeOffset"/>
/// </summary>
/// <param name="other"></param>
/// <returns></returns>
public static implicit operator ProtoDateTimeOffset? (DateTimeOffset? other)
{
if (other == null)
return null;
return new ProtoDateTimeOffset
{
OffsetMinutes = other.Value.Offset.TotalMinutes,
Ticks = other.Value.Ticks
};
}
}