【问题标题】:protobuf datetimeoffsetprotobuf 日期时间偏移量
【发布时间】:2021-07-29 16:50:36
【问题描述】:

只是想分享一个关于 protobuf-net 的 R561 版本的观察。使用DateTimeOffSet时出现异常

InvalidOperationException(没有为类型定义序列化程序: System.DateTimeOffset)

出现:

我添加了一个带有getProto()StreamWriter 的方法来编写一个原始文件,现在它可以工作了!(并且原始文件也完全可用)。但是如果我评论这个方法,就会发生同样的异常。我真的不明白。

希望这个观察有用。


我会尽量说得更清楚。我有一个 C# 客户端,其中包含一些使用 DateTimeOffset 参数的对象。我用 protobuf-net (r561) 对它们进行了序列化,并添加了一个 writeProtoFile() 方法来使用 getProto() 方法编写一个 proto 文件。序列化似乎工作得很好,原始文件也可以。所以因为我现在有我的 proto 文件,我可以评论或禁止 writeProtoFile() 方法:我不需要其他 proto 文件。所以这是我的第一个问题:

-> 为什么当这个方法(只是写一个调用 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 类(在原型中,dateTimeOffset 不包含任何内容)

message DateTimeOffset {
}

Java 中存在 DateTimeOffset 类型,所以这是我的第二个问题:-> 有什么方法可以将 C# 中的 dateTimeOffset 参数序列化,然后在反序列化后成为 java 中的 dateTimeOffset 参数?

【问题讨论】:

  • 例外很简单:确实,我没有添加对此的支持。我不明白你的最后一段
  • 你能澄清一下这里是否有问题吗?否则我应该将其关闭为“不是一个真正的问题”......

标签: c# protobuf-net datetimeoffset


【解决方案1】:

有什么方法可以将C#中的dateTimeOffset参数序列化,然后反序列化后成为java中的dateTimeOffset参数?

对于任何语言都没有定义基于 .proto 的 DateTimeDateTimeOffset 值处理,因此不存在在平台之间通过protobuf(或任何特定的实现,例如 protobuf-net)。此外,虽然在 2 个不同的平台上有一个名为 DateTimeOffsettype,但这本身并不足以保证它们具有相似的语义/范围等。

对于任何跨平台场景,我建议只使用非常基本的数据,甚至可能只是像整数(64 位)这样的东西,以毫秒为单位存储 1970 纪元的偏移量。或类似的东西。

为什么当这个方法(只是写一个调用 getProto() 方法的 proto 文件)被注释或抑制时,序列化不再起作用?

protobuf-net 零使用任何getProto 方法或writeProtoFile 方法。我会非常谨慎地怀疑评论/取消评论这会改变一些内部行为,并且需要具体的再现来调查。老实说,这听起来极不可能。警告:有一个 Serializer.GetProto<T> 方法,但它的作用非常不同(并且在 v2 API 中被 GetSchema(Type) 取代)。

然而,它的说法是完全正确的:

没有为类型定义序列化程序:System.DateTimeOffset。

原因很简单,我没有为这种类型定义标准序列化程序。如果您可以为此定义一些标准处理,您可能可以使用SetSurrogate 将其连接到您选择的任何电线表示。

回复:

解析协议消息时,输入意外在字段中间结束

这不应该以任何方式发生。这听起来像是一个不相关的问题,很可能是传输数据时数据损坏(例如,数据的错误编码),或者覆盖预先存在的文件而不截断它(最后留下垃圾)。如果您能准确地说明如何在平台之间传输二进制文件,我可能会提供更多建议,但首先要调查的是:您发送的二进制数据是否与您发送的二进制数据相同收到(与 protobuf 无关 - 只是一个简单的问题:您是否正确传输了我的 BLOB?它的长度是否相同?每个字节都相同吗?)

【讨论】:

  • 基本上,我将 byteMessage 发送到包含序列化对象的 activeMQ。然后 java 客户端使用它。我比较了字节数组,它在 C# 和 java 中是相同的。这是在java中反序列化它的方法(发布在我的消息的回答中以使其更精确)
【解决方案2】:

我遇到了同样的问题,并为原型序列化编写了一个包装器对象(请参阅代码答案的结尾)。它有单独的 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# 的实现,我不知道您可以在哪些语言上使用刻度和偏移量,但在这些语言中的转换应该是微不足道的。


帮助类的实际代码:

/// <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
        };
    }
}

【讨论】:

    【解决方案3】:

    我用这个方法在C#中序列化:

    MemoryStream ms = new MemoryStream();
    Serializer.Serialize<MyObjectType>(ms, myObject);
    byte[] array = ms.ToArray();
    ms.Close();
    

    然后我将它发送到 ActiveMQ 主题(发布-订阅模型) Java 以异步方式接收它,并使用以前的方法对其进行反序列化(在我的第一个答案中)。 我阅读了数组,它似乎没有损坏,但一个已签名,另一个未签名。 在 C# 中,我有 10 个大于 126 的值。这些相同的值在 Java 中变为负数:

    byte #58 =  144 in C# , -112 in Java
    byte #67 =  160 in C# , -96 in Java
    

    经过多次测试,当我不序列化 dataTimeOffset 参数时,这些值似乎消失了。

    【讨论】:

      【解决方案4】:

      这是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;
          }
      }
      

      这种反序列化方法会损坏它吗?

      【讨论】:

      • 好的,Java 中接收到的字节数组肯定已损坏。我要检查所有的运输过程...
      • 这真的归结为:你的 C# 代码中有一个 byte[]...你做了什么来让它在你的 java 代码中变成一个 byte[]?你是直接写到socket的吗?一份文件?您是否将其转换为字符串并发送 that?如果是这样如何?你使用了什么编码/解码过程?
      猜你喜欢
      • 2011-05-18
      • 2018-02-20
      • 2013-09-13
      • 1970-01-01
      • 2018-03-15
      • 2016-10-29
      • 2012-12-17
      • 2020-10-24
      • 1970-01-01
      相关资源
      最近更新 更多