java - protobuf-net 序列化/反序列化 DateTime 和 Guid 类型

标签 java c# protocol-buffers protobuf-net

我在从 protobuf-net 获取 TimeDateGuid 类型的值时遇到一些问题:

我有.Net客户端.Net服务器,它们通过protobuf-net进行通信。现在我必须实现从 java 客户端到此 .Net Server 的通信,并且我无法更改已存在的 .Net Server 通信逻辑,因此我必须使用已存在的protobuf通信,问题如下:

protobuf-net 理解两种 .net 类型:DateTimeGuid 但我无法通过google protobuf解析它:

.Net Server 类示例:

[ProtoContract]
public class SomeClass
{
[ProtoMember(1)]
public DateTime? CurrentDateTime { get; set; }

[ProtoMember(2)]
public Guid CurrentGiud { get; set; }
}

我无法通过google protobuf解析它,因为它对DateTimeGuid类型一无所知,所以我只能得到< em>byte[] 来自这些字段,.proto 示例:

message SomeClass
{
 bytes CurrentDateTime = 1;
 bytes CurrentGiud = 2;
}

因此,在序列化/反序列化流之后,我可以从这些字段中获取byte[],现在我需要以某种方式将其转换为适当的值,所以我需要这样的东西:

var customDateTime = ConvertByteArrayToCustomDateTime(byteArray);
byte[] byteArray = ConvertCustomDateTimeToByteArray(customDateTime);

var customGuid = ConvertByteArrayToCustomGuid(byteArray);
byte[] byteArray = ConvertCustomGuidToByteArray(customGuid);

或者这个:

string strDateTime = ConvertByteArrayToStringDateTime(byteArray); //e.g. "13.08.2019 17:42:31"
byte[] byteArray = ConvertStringDateTimeToByteArray(strDateTime);

string strGuid = ConvertByteArrayToStringGuid(byteArray); // e.g. "{7bb7cdac-ebad-4acf-90ff-a5525be3caac}"
byte[] byteArray = ConvertStringGuidToByteArray(strGuid);

日期时间真实示例:

示例 N1:

DateTime = 13.08.2019 17:42:31
after serialization/deserialization
byte[] = { 8, 142, 218, 151, 213, 11, 16, 3 }

示例 N2:

DateTime = 25.06.2019 20:15:10
after serialization/deserialization
byte[] = { 8, 156, 131, 148, 209, 11, 16, 3 }

指导实例:

示例 N1:

Guid = {7bb7cdac-ebad-4acf-90ff-a5525be3caac}
after serialization/deserialization
byte[] = { 9, 172, 205, 183, 123, 173, 235, 207, 74, 17, 144, 255, 165, 82, 91, 227, 202, 172 }

示例 N2:

Guid = {900246bb-3a7b-44d4-9b2f-1da035ca51f4}
after serialization/deserialization
byte[] = { 9, 187, 70, 2, 144, 123, 58, 212, 68, 17, 155, 47, 29, 160, 53, 202, 81, 244 }

解决方案:

将以下消息添加到您的.proto中:

message CustomDateTime 
{
  sint64 value = 1; // the offset (in units of the selected scale) from 1970/01/01
  CustomTimeSpanScale scale = 2; // the scale of the timespan [default = DAYS]
  CustomDateTimeKind kind = 3; // the kind of date/time being represented [default = UNSPECIFIED]
  enum CustomTimeSpanScale 
  {
    DAYS = 0;
    HOURS = 1;
    MINUTES = 2;
    SECONDS = 3;
    MILLISECONDS = 4;
    TICKS = 5;

    MINMAX = 15; // dubious
  }
  enum CustomDateTimeKind
  {     
     // The time represented is not specified as either local time or Coordinated Universal Time (UTC).
     UNSPECIFIED = 0;
     // The time represented is UTC.
     UTC = 1;
     // The time represented is local time.
     LOCAL = 2;
   }
}

message CustomGuid 
{
  fixed64 lo = 1; // the first 8 bytes of the guid (note:crazy-endian)
  fixed64 hi = 2; // the second 8 bytes of the guid (note:crazy-endian)
}

现在你的 .proto 类应该如下所示:

message SomeClass
{
 CustomDateTime CurrentDateTime = 1;
 CustomGuid CurrentGiud = 2;
}

简单的DateTime解析器:

        public static string ConvertCustomDateTimeToString(CustomDateTime customDateTime)
        {
            var dateTime = DateTime.Parse("01.01.1970 00:00:00");

            if (customDateTime.Scale == CustomDateTime.Types.CustomTimeSpanScale.Seconds)
            {
                dateTime = dateTime.AddSeconds(customDateTime.Value);
            }
            else
            {
                throw new Exception("CustomDateTime supports only seconds");
            }

            return dateTime.ToString();
        }

        public static CustomDateTime ConvertStringToCustomDateTime(string strDateTime)
        {
            var defaultTime = DateTime.Parse("01.01.1970 00:00:00");
            var dateTime = DateTime.Parse(strDateTime);

            var customDateTime = new CustomDateTime
            {
                Kind = CustomDateTime.Types.CustomDateTimeKind.Unspecified,
                Scale = CustomDateTime.Types.CustomTimeSpanScale.Seconds,
                Value = (long) (dateTime - defaultTime).TotalSeconds
            };

            return customDateTime;
        }

简单的Guid解析器:

public static string ConvertCustomGuidToString(CustomGuid customGuid)
        {
            var str = string.Empty;

            var array = BitConverter.GetBytes(customGuid.Lo);

            var newArray = new byte[8];
            newArray[0] = array[3];
            newArray[1] = array[2];
            newArray[2] = array[1];
            newArray[3] = array[0];
            newArray[4] = array[5];
            newArray[5] = array[4];
            newArray[6] = array[7];
            newArray[7] = array[6];

            str += BitConverter.ToString(newArray).Replace("-", "");

            str += BitConverter.ToString(BitConverter.GetBytes(customGuid.Hi)).Replace("-", "");

            return str;
        }

        public static CustomGuid ConvertStringToCustomGuid(string strGuid)
        {
            strGuid = strGuid.Replace(" ", "");
            strGuid = strGuid.Replace("-", "");
            strGuid = strGuid.Replace("{", "");
            strGuid = strGuid.Replace("}", "");

            if (strGuid.Length != 32)
            {
                throw new Exception("Wrong Guid format");
            }

            byte[] array = new byte[16];

            for (int i = 0; i < 32; i += 2)
                array[i / 2] = Convert.ToByte(strGuid.Substring(i, 2), 16);

            var newArrayLo = new byte[8];
            newArrayLo[0] = array[3];
            newArrayLo[1] = array[2];
            newArrayLo[2] = array[1];
            newArrayLo[3] = array[0];
            newArrayLo[4] = array[5];
            newArrayLo[5] = array[4];
            newArrayLo[6] = array[7];
            newArrayLo[7] = array[6];

            var newArrayHi = new byte[8];
            newArrayHi[0] = array[8];
            newArrayHi[1] = array[9];
            newArrayHi[2] = array[10];
            newArrayHi[3] = array[11];
            newArrayHi[4] = array[12];
            newArrayHi[5] = array[13];
            newArrayHi[6] = array[14];
            newArrayHi[7] = array[15];

            var customGuid = new CustomGuid
            {
                Lo = BitConverter.ToUInt64(newArrayLo, 0),
                Hi = BitConverter.ToUInt64(newArrayHi, 0)
            };

            return customGuid;
        }

最佳答案

我们需要分别讨论这两种类型。每个都有背景故事!


DateTime/TimeSpan - 所以:追溯到历史上,.NET 人们一直希望 protobuf-net 能够往返 DateTime/TimeSpan 。 Google 没有为此目的定义任何内容,因此 protobuf-net编造了一些东西。详情见bcl.proto ,但我不建议担心这一点。简短的版本是:“如果你不是 protobuf-net,那么与他们合作会有点尴尬”。

向前推进 5 年多,Google终于定义了众所周知的 DurationTimestamp类型。不幸的是,它们与 protobuf-net 决定如何实现它们并不是 1:1 匹配的,而且我无法在不破坏现有消费者的情况下更改默认布局。但!对于新代码或跨平台目的,protobuf-net 知道如何交谈 Duration/Timestamp ,如果甚至有可能,我强烈建议更改您的布局。好消息是:这真的很简单:

[ProtoMember(1, DataFormat = DataFormat.WellKnown)]
public DateTime? CurrentDateTime { get; set; }

现在将使用 .google.protobuf.Timestamp而不是.bcl.DateTime ;这也适用于 TimeSpan/.google.protobuf.Duration .

这里的关键点:有一个简单的选项,您可以切换到该选项,这将使这个“正常工作”;默认值是为了与 protobuf-net 在 Google 决定布局之前必须发明的东西兼容。

请注意,更改为 DataFormat.WellKnown是数据重大更改;布局不同。如果有一种方法可以自动检测和补偿,它已经会了;没有。


Guid - 这应该要简单得多;这里明智的想法是将其序列化为 bytes用 .proto 术语来说,但是......我对此感到遗憾,我做了一件愚蠢的事并试图做一些聪明的事情。结果适得其反,我很后悔。它的作用是......有点愚蠢,尽管它在内部确实有意义。它访问 Guid连续两次fixed64字段(再次查看 bcl.proto ),其中这些是 Microsoft 疯狂字节序布局中的低/高字节。我所说的疯狂端序,是指 guid 00112233-4455-6677-8899-AABBCCDDEEFF 在哪里由字节 33-22-11-00-55-44-77-66-88-99-AA-BB-CC-DD-EE-FF 表示(强调:这不是我说的;这是 Microsoft 和 .NET 在内部使用 guid 所做的事情)。所以;以您的 N1 为例,您看到的两个半片段是:

  • 0x09 =“字段1,类型fixed64”
  • 0x AC-CD-B7-7B-AD-EB-CF-4A - 前半部分采用疯狂字节序
  • 0x11(十进制17)=“字段2,类型fixed64”
  • 0x 90-FF-A5-52-5B-E3-CA-AC - 后半部分采用疯狂字节序

坦率地说,对于跨平台工作,我建议 Guid ,将其公开为 stringbyte[]相反,对于给您带来的不便,我深表歉意!


在这两种情况下:如果您无法更改布局,请查看 bcl.proto 以了解实际发生的情况。如果您使用Serializer.GetProto<T>() ,它应该生成一个自动导入 bcl.proto 的模式。

关于java - protobuf-net 序列化/反序列化 DateTime 和 Guid 类型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57493463/

相关文章:

java - 如何在 List<List<Integer>> 中迭代并设置它们的值,就像我们在普通 int a[i][j] 矩阵类型中所做的那样

java:重载时返回类型不同

c# - 玩家在 Y 轴上不需要的旋转

c++ - 无法生成 gRPC 服务类

javascript - 如何为 Protobuf 生成的 JavaScript 文件设置 VSCode 智能感知?

java - DrawerLayout 后退箭头功能

Java 鼠标 - 图形字符串 - 获取相对于背景图像的鼠标 X 和 Y

c# - Html.CheckBoxFor 类型转换错误

c# - .Net AsyncStateMachine 自定义实现行为

ios - 多个完成 block