Wire 对比 Protoc¶
非原始类型¶
Protoc 为 Proto3 中的所有新类型(如 empty
、struct
等)生成字面等价物。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.Duration
和 google.protobuf.Timestamp
类型都将通过使用其 JVM 等价物生成:java.time.Duration
和 java.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 通过 Moshi 或 Gson 提供 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()