跳到内容

Wire 对比 Protoc

非原始类型

Protoc 为 Proto3 中的所有新类型(如 emptystruct 等)生成字面等价物。Wire 尝试在可能的情况下重用相应语言中的现有类型。Wire 引入的唯一新类型是用于 google.protobuf.Any proto 类型的 AnyMessage

Any

Any 类型通过持有一个字段来标识其类型,并持另一个字段来存储被包装消息的序列化表示,从而包装任意 protobuf 消息。Wire 有自己的 AnyMessage 类型来表示 google.protobuf.Any

class AnyMessage(
  val typeUrl: String,
  val value: okio.ByteString
)

它提供了一些方法来包装或解包嵌入的消息。

// Wire
val anyMessage: AnyMessage = AnyMessage.pack(person)
val person: Person = anyMessage.unpack(Person.ADAPTER)

// Protoc
val any: Any = Any.pack(foo)
val person: Person = any.unpack(Person.class)

Duration & Timestamp

google.protobuf.Durationgoogle.protobuf.Timestamp 类型都将通过使用其 JVM 等价物生成:java.time.Durationjava.time.Instant。对于非 JVM 平台,我们提供了两个具有相同 API 的新 Wire 类型

class com.squareup.wire.Duration {
  fun getSeconds(): Long
  fun getNano(): Int
}
fun durationOfSeconds(seconds: Long, nano: Long): Duration

class com.squareup.wire.Instant {
  fun getEpochSecond(): Long
  fun getNano(): Int
}
fun ofEpochSecond(epochSecond: Long, nano: Long): Instant
// Wire
val duration: java.time.Duration = Duration.standardMinutes(15)
val instant: java.time.Instant = Instant.now()

// Protoc
val duration: google.protobuf.Duration =
    Duration.newBuilder()
      .setSeconds(60 * 15)
      .build()
val instant: google.protobuf.Timestamp =
    Timestamps.fromMillis(System.currentTimeMillis())

Struct

google.protobuf.Struct 主要用于在代码中表示 JSON 对象。Wire 没有构建新类型,而是重用 Java/Kotlin 原生类型来表示所有 Struct 类型。

Google Protobuf 类型 Wire 的 Java 等价物 Wire 的 Kotlin 等价物
Struct Map<String, ?> Map<String, ?>?
ListValue List<?> List<?>?
Value Object Any?
NullValue Void Nothing?

Protoc 和 Wire 之间一个值得注意的区别是,Protoc 可以区分缺失值和 null 值,而 Wire 不能。除了 JSON 对象的根部,Wire 总是会在其中写入 null

// Wire
val struct = mapOf("a" to 1.0)
val list = listOf(b, 2.0)
val boolValue = true
val nullValue = null

// Protoc
val struct: Struct =
    Struct.newBuilder().apply {
        putFields(a, Value.newBuilder.setNumberValue(1.0).build())
    }
    .build()
val list: List =
    ListValue.newBuilder().apply {
        addValues(Value.newBuilder.setStringValue(a).build())
        addValues(Value.newBuilder.setNumberValue(2.0).build())
    }
    .build()
val boolValue = Value.newBuilder.setBoolValue(true).build()
val nullValue = Value.newBuilder().setNullValue(NullValue.NULL_VALUE).build()

包装类型

Wire 也没有为包装类型创建新类型,每个包装类型都将由其定义的原始类型的可空版本表示。例如,google.protobuf.FloatValue 在 Java 中将由浮点数包装类型 @Nullable Float 表示,在 Kotlin 中由 Float? 表示。

// Wire
val floatValue = 33.3f

// Protoc
val floatValue = FloatValue.newBuilder().setValue(33.3f).build()

JSON

虽然 Proto2 没有,但 Proto3 定义了基于 JSON 的 Protobuf 序列化。Wire 和 Protoc 可以互操作,但它们的 API 相当不同。Wire 通过 MoshiGson 提供 JSON 序列化。Protoc 带来了自己的 JsonFormatter。请注意,Protoc 会对未知字段抛出错误,你需要配置它来取消这种行为!

// Wire & Moshi
val moshi = Moshi.Builder()
      .add(WireJsonAdapterFactory())
      .build()
val adapter = moshi.adapter(Pizza::class.java)
val pizza: Pizza = ...
val json = adapter.toJson(pizza)
val parsedPizza = adapter.fromJson(json)

// Protoc
val pizza: PizzaOuterClass.Pizza = 
val json = JsonFormat.printer().print(value)
val jsonParser = JsonFormat.parser().ignoringUnknownFields()
val parsedBuilder = PizzaOuterClass.Pizza.newBuilder()
jsonParser.merge(json, parsedBuilder)
val parsedPizza = parsedBuilder.build()