如何在protobuf消息中表示UUID?

80

我想将UUID附加到我的protobuf用户消息示例中的字段。

message User {
  // field containing id as UUID type
  required string email;
  optional string name;
}

我知道protobuf消息还不支持UUID类型。我已经阅读过最佳方法是使用UUID消息类型。

因此,我猜我的User消息将导入我的UUID消息proto定义,并像这样将其用作字段类型:

import "myproject/UUID.proto";

message User {
  required UUID id;
  required string email;
  optional string name;
}

我的问题是,UUID消息会长成什么样子,我该如何对它进行编码/解码?我希望在Java/Scala和C#中都能兼容。

4个回答

76

你应该使用stringbytes来表示UUID。如果将UUID以人类可读的形式(例如"de305d54-75b4-431b-adb2-eb6b9e546014")保留最方便,则使用string,否则使用bytes来存储128位原始值。(如果不确定,你可能需要选择string。)

将值包装在名为UUID的消息类型中可以帮助使代码更易于自我记录,但会有一些性能开销,并不是必须的。如果想要这样做,请定义类型如下:

message UUID {
  required string value = 1;
}
或:
message UUID {
  required bytes value = 1;
}

1
@Hyperreal:顺便说一下,要转换为字节,您需要删除连字符,然后将其余部分解释为十六进制字符串--每两个数字成为一个字节。但是,储存一个字符串可能更容易。 - Kenton Varda
1
所以我查了一下你正在开发的新协议缓冲区(Cap'N Proto),也许我会放弃谷歌的 Protobuff,转而使用你的... 我看到有人正在开发 Java 实现(希望将来也能有 Scala 的实现!)。 - Edward Maxedon
1
@Hyperreal 哈哈,注意,在Cap'n Proto中同样适用这个问题,答案基本相同。 :) - Kenton Varda
2
我同意protobuf团队的决定。使用stringbytes来表示UUID很容易。添加内置类型将需要在每种语言的protobuf实现中添加复杂性。这不值得。 - Kenton Varda
1
@KingKong 看起来答案是扩展竞技场以支持字符串和字节。 - Kenton Varda
显示剩余4条评论

13

1
当某人仅仅是“懒惰”或只是“忘记”正确实现逻辑并没有考虑字节序(endianness)而导致的时,会出现“不稳定行为”。这不是 UUIDs 表示问题,而是仅仅以“规范文本格式”“呈现”UUIDs 的逻辑实现可能存在错误。UUIDs 的值本身始终只是一个包含完全128位的序列。 - Victor Yarema
7
抱歉,我真的无法理解您的评论。 - sw1337

12

如果想避免字节序问题,建议使用 string。请注意,具有相同字符串表示形式(因此是相同的“id”)的UUID和MS GUID在字节流顺序上是不同的(大端序与小端序)。如果在Java使用UUID和在C#使用System.Guid之间通信的协议中使用 bytes,可能会导致ID被翻转。


33
字节序问题仅影响多字节值,例如16、32和64位整数和浮点数,在这些情况下必须选择哪个端包含高阶字节。对于一个字节数组,不存在字节序问题,因此使用 bytes 不会导致任何字节序问题。如果另一方面,UUID 存储在两个 64 位整数中,则必须处理字节序问题。 - Doug Richardson
6
GUID/UUID 不仅仅是字节数组。它具有4/2/2/2/6个字节的结构,而字节序也很重要。 - Robert Taylor
19
当你生成UUID以符合RFC 4122时,这是正确的。但一旦生成了UUID,你可以将其视为一个不透明的字节数组。要了解我的意思,请查看Go UUID库。版本1和版本4均考虑字节顺序创建UUID,但编组将两个版本都视为简单的字节数组。由于此问题涉及protobufs,我们只关心编组方面。 - Doug Richardson
1
@RobertTaylor,您提供的链接(格式部分)仅定义了“规范文本表示”,而UUID本身只是一个“128位数字”,没有任何其他特殊约束。我甚至会从该定义中删除“数字”一词,并将其定义为“128位序列”。 - Victor Yarema

10

如果您想确保直接互操作性,我建议使用字符串编码而不是字节编码:

message UUID {
  required string value = 1;
}

问题在于字节编码:不同的UUID库在字节编码/解码方案上使用不同的方案,而它们在如何编码/解码字符串方面达成了一致。
例如,看看C#的System.guid.toBytesArray返回的是混合字节序格式:前三个组件采用小端编码,而最后两个组件采用大端编码。
在Java中,Apache Commons库的Uuid.toRawBytes以大端编码返回uuid。
"String": 35918bc9-196d-40ea-9779-889d79b753f0
"C#"    : C9 8B 91 35 6D 19 EA 40 97 79 88 9D 79 B7 53 F0
"Java"  : 35 91 8B C9 19 6D 40 EA 97 79 88 9D 79 B7 53 F0

作为一则附注:Python 3的Uuid提供了两种编码方式:bytes用于大端编码,bytes_le用于混合端编码。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接