protobuf datetimeoffset

时间:2012-08-17 15:00:51

标签: c# protobuf-net datetimeoffset

只想分享一下关于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参数?

4 个答案:

答案 0 :(得分:2)

  

是否有任何方法可以将C#中的dateTimeOffset参数序列化,然后在反序列化后成为java中的dateTimeOffset参数?

对于任何语言,都没有针对DateTimeDateTimeOffset进行基于.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
        };
    }
}