常见类型

到目前为止,如果你查看了配套的代码,你会看到我们定义了许多 “无聊” 的类型,这些类型只是围绕一个字段的包装器。需要注意的是,我们手动编写了这些类型,仅仅是为了给出一个如何检查特定数据序列化的示例。大多数情况下,你将能够使用已经定义的类型,它们做的事情是一样的。

常见类型(Well-known Types)

Protobuf 本身提供了一些已经定义好的类型,我们称之为 “知名类型”(well-known types)。虽然它们中的大多数在 Protobuf 库本身之外或在高级用例中很少使用,但其中一些是很重要的,我们将在本书中使用其中的一些。

我们可以很容易理解的是 “包装器”(wrappers)。我们之前手动编写了一些包装器。它们通常以它们所包装的类型的名称开头,最后以 Value 结尾。以下是包装器类型的列表:

  • BoolValue

  • BytesValue

  • DoubleValue

  • EnumValue

  • FloatValue

  • Int32Value

  • Int64Value

  • StringValue

  • UInt32Value

  • UInt64Value

这些类型可能在调试用例中很有用,比如我们之前看到的用例,或者仅仅用于序列化简单的数据,如数字、字符串等。

然后,Protobuf 还定义了表示时间的类型,比如 DurationTimestamp。这两种类型的定义完全相同([Duration | Timestamp] 并不是有效的 Protobuf 语法,它表示我们可以选择其中任何一个术语):

message [Duration | Timestamp] {
  // 表示自 Unix 纪元(1970-01-01T00:00:00Z)以来的 UTC 时间秒数。
  // 必须在 0001-01-01T00:00:00Z 到 9999-12-31T23:59:59Z 之间。
  int64 seconds = 1;

  // 秒的非负小数部分,精确到纳秒。负秒值与小数部分必须具有非负的纳秒值,
  // 并且时间应该向前推进。必须在 0 到 999,999,999 之间。
  int32 nanos = 2;
}

然而,正如它们的名字所示,它们表示不同的概念。Duration 类型表示开始时间和结束时间之间的差异,而 Timestamp 类型表示一个简单的时间点。

最后,一个重要的知名类型是 FieldMask。这个类型表示一组字段,指示在序列化另一个类型时应包含哪些字段。为了更好理解这一点,我们来看一个例子。假设我们有一个 API 端点返回一个账户,其中包含 id、用户名和电子邮件。如果你只想获取账户的电子邮件地址,以便准备发送促销邮件给一部分人,你可以使用 FieldMask 类型告诉 Protobuf 只序列化电子邮件字段。这样,我们就可以减少序列化和反序列化的额外开销,因为现在我们只处理一个字段,而不是三个。

Google 公共类型

除了知名类型之外,还有一些由 Google 定义的类型。这些类型在 googleapis/api-common-protos GitHub 仓库的 google/type 目录下定义,并且在 Golang 代码中很容易使用。我鼓励你查看所有这些类型,但我想提到其中一些有趣的类型:

  • LatLng:一个纬度/经度对,将值存储为双精度浮点数(double)。

  • Money:一个金额及其货币,货币类型按照 ISO 4217 标准定义。

  • Date:由 int32 类型表示的年月日。

再次提醒,建议你访问该仓库查看所有其他类型。这些类型经过了大量的实际应用和测试,通常比我们自己编写的简单类型更优化。然而,也要注意,这些类型可能并不适合你的所有用例。没有一种类型能适用于所有场景。