protobuf-net serialize/deserialize 日期时间和 Guid 类型
protobuf-net serialize/deserialize DateTime & Guid types
我在从 TimeDate 和 Guid 类型中获取值时遇到一些问题 protobuf-net:
我有 .Net Client 和 .Net Server,它们通过 protobuf-net[=87 进行通信=].现在我必须实现从 java 客户端到此 .Net Server 的通信,我无法更改已经存在的 .Net Server 通信逻辑,所以我必须使用已经存在的 protobuf 通信,问题如下:
protobuf-net 理解两种 .net 类型:DateTime 和 Guid 但我无法通过 google protobuf:
解析它
.Net 服务器 class 示例:
[ProtoContract]
public class SomeClass
{
[ProtoMember(1)]
public DateTime? CurrentDateTime { get; set; }
[ProtoMember(2)]
public Guid CurrentGiud { get; set; }
}
我无法通过 google protobuf 解析它,因为它对 DateTime 和 [=59 一无所知=]Guid 类型,所以我只能从这些字段中获取 byte[],.proto 示例:
message SomeClass
{
bytes CurrentDateTime = 1;
bytes CurrentGiud = 2;
}
所以在 serialization/deserialization 流之后我可以从这些字段中得到 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);
DateTime实例:
示例 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 class 应该是这样的:
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 中,但我不建议为此担心。简短的版本是:"they're kinda awkward to work with if you're not protobuf-net".
前滚 5 年多,Google最终 定义了众所周知的 Duration
和 Timestamp
类型。不幸的是,它们与 protobuf-net 决定实现它们的方式 1:1 不匹配,而且我无法在不破坏现有消费者的情况下更改默认布局。但!对于新代码或跨平台目的,protobuf-net 知道如何与 Duration
/ Timestamp
交谈,如果它是 even极有可能,我强烈建议更改您的布局。好消息是:这真的很简单:
[ProtoMember(1, DataFormat = DataFormat.WellKnown)]
public DateTime? CurrentDateTime { get; set; }
现在将使用 .google.protobuf.Timestamp
而不是 .bcl.DateTime
;这也适用于 TimeSpan
/ .google.protobuf.Duration
.
这里的关键点:有一个简单的选项,您可以切换到该选项,这将使 "just work";默认值是为了与 protobuf-net 在 Google 决定布局之前必须发明的东西兼容。
请注意,更改为 DataFormat.WellKnown
是一项数据破坏性更改;布局不同。如果有一种自动检测和补偿的方法,它早就会了;没有。
Guid
- 这个应该简单得多;这里明智的想法是将它序列化为 .proto 术语中的 bytes
,但是......我对此感到遗憾,我做了一个愚蠢的事情并试图做一些聪明的事情。结果适得其反,我很后悔。它的作用是......有点傻,虽然它在内部确实有意义。它访问 Guid
作为两个连续的 fixed64
字段(再次,查看 bcl.proto
),其中这些是 low/high 字节 在微软的 craz-endian 布局中。我所说的 crazy-endian 是指 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 = "field 1, type fixed64"
- 0x AC-CD-B7-7B-AD-EB-CF-4A - 疯狂字节序的上半部分
- 0x11(十进制 17)="field 2, type fixed64"
- 0x 90-FF-A5-52-5B-E3-CA-AC - 疯狂字节序的下半部分
坦率地说,对于跨平台工作,我建议使用 Guid
,将其公开为 string
或 byte[]
,对于给您带来的不便,我深表歉意!
在这两种情况下:如果您无法更改布局,请在 bcl.proto 中查看实际发生的情况。如果您使用 Serializer.GetProto<T>()
,它应该会生成一个自动导入 bcl.proto 的模式。
我在从 TimeDate 和 Guid 类型中获取值时遇到一些问题 protobuf-net:
我有 .Net Client 和 .Net Server,它们通过 protobuf-net[=87 进行通信=].现在我必须实现从 java 客户端到此 .Net Server 的通信,我无法更改已经存在的 .Net Server 通信逻辑,所以我必须使用已经存在的 protobuf 通信,问题如下:
protobuf-net 理解两种 .net 类型:DateTime 和 Guid 但我无法通过 google protobuf:
解析它.Net 服务器 class 示例:
[ProtoContract]
public class SomeClass
{
[ProtoMember(1)]
public DateTime? CurrentDateTime { get; set; }
[ProtoMember(2)]
public Guid CurrentGiud { get; set; }
}
我无法通过 google protobuf 解析它,因为它对 DateTime 和 [=59 一无所知=]Guid 类型,所以我只能从这些字段中获取 byte[],.proto 示例:
message SomeClass
{
bytes CurrentDateTime = 1;
bytes CurrentGiud = 2;
}
所以在 serialization/deserialization 流之后我可以从这些字段中得到 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);
DateTime实例:
示例 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 class 应该是这样的:
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 中,但我不建议为此担心。简短的版本是:"they're kinda awkward to work with if you're not protobuf-net".
前滚 5 年多,Google最终 定义了众所周知的 Duration
和 Timestamp
类型。不幸的是,它们与 protobuf-net 决定实现它们的方式 1:1 不匹配,而且我无法在不破坏现有消费者的情况下更改默认布局。但!对于新代码或跨平台目的,protobuf-net 知道如何与 Duration
/ Timestamp
交谈,如果它是 even极有可能,我强烈建议更改您的布局。好消息是:这真的很简单:
[ProtoMember(1, DataFormat = DataFormat.WellKnown)]
public DateTime? CurrentDateTime { get; set; }
现在将使用 .google.protobuf.Timestamp
而不是 .bcl.DateTime
;这也适用于 TimeSpan
/ .google.protobuf.Duration
.
这里的关键点:有一个简单的选项,您可以切换到该选项,这将使 "just work";默认值是为了与 protobuf-net 在 Google 决定布局之前必须发明的东西兼容。
请注意,更改为 DataFormat.WellKnown
是一项数据破坏性更改;布局不同。如果有一种自动检测和补偿的方法,它早就会了;没有。
Guid
- 这个应该简单得多;这里明智的想法是将它序列化为 .proto 术语中的 bytes
,但是......我对此感到遗憾,我做了一个愚蠢的事情并试图做一些聪明的事情。结果适得其反,我很后悔。它的作用是......有点傻,虽然它在内部确实有意义。它访问 Guid
作为两个连续的 fixed64
字段(再次,查看 bcl.proto
),其中这些是 low/high 字节 在微软的 craz-endian 布局中。我所说的 crazy-endian 是指 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 = "field 1, type fixed64"
- 0x AC-CD-B7-7B-AD-EB-CF-4A - 疯狂字节序的上半部分
- 0x11(十进制 17)="field 2, type fixed64"
- 0x 90-FF-A5-52-5B-E3-CA-AC - 疯狂字节序的下半部分
坦率地说,对于跨平台工作,我建议使用 Guid
,将其公开为 string
或 byte[]
,对于给您带来的不便,我深表歉意!
在这两种情况下:如果您无法更改布局,请在 bcl.proto 中查看实际发生的情况。如果您使用 Serializer.GetProto<T>()
,它应该会生成一个自动导入 bcl.proto 的模式。