背景
有一个后端的 Java/Kotlin 项目需要与同事的模块通信,协议是 protobuf。
这块我想用 Kotlin 来写,当然 Java 也不是不行。没想到踩了些坑,故以本文作记录。
文档
protobuf 官方是有 Kotlin 的教程的,但是并不全面,这是出现各种坑的根源。
另外,我原以为 protobuf 对 Kotlin 的专门支持是多年前就有的(指的是支持 Kotlin 的各种方便写法),然而我后来才发现是2021年才有,见 Announcing Kotlin support for protocol buffers。
方法
1. 从 .proto 生成 .java .kt
首先找同事得到 .proto 文件,然后下载 protoc,解压后得到一个可执行文件。
然后按照官方教程,在命令行中执行
protoc --java_out=$DST_DIR --kotlin_out=$DST_DIR xxxxxx.proto
注意--java_out
和--kotlin_out
都是需要的,当前版本的 protoc 对 Kotlin 的支持是在原来 Java 生成的基础上多了 Kotlin 的增强。
然后就得到了一些 .java .kt 文件。
2. Maven
把生成的文件放进项目中,可以发现编译是不能通过的,因为缺少 protobuf 相关库。我的项目是 Maven 的,所以在 pom.xml 中加上
<!-- https://mvnrepository.com/artifact/com.google.protobuf/protobuf-kotlin -->
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-kotlin</artifactId>
<version>3.19.4</version> <!-- version 要与之前 protoc 的版本对应 -->
</dependency>
这个 pom 的 artifactId (protobuf-kotlin) 是关键!!我看到的文档写的都是protobuf-java
,就会导致生成的 .kt 里面的
@kotlin.OptIn(com.google.protobuf.kotlin.OnlyForUseByGeneratedProtoCode::class)
报找不到的错误,必须使用protobuf-kotlin
。
3. 使用
这里参照 Announcing Kotlin support for protocol buffers 举的例子,用了 Kotlin 之后,写法更简洁了。假设原来 Java 的是这样写:
DiceSeries series = DiceSeries.newBuilder()
.addRoll(DiceRoll.newBuilder()
.setValue(5))
.addRoll(DiceRoll.newBuilder()
.setValue(20)
.setNickname("critical hit"))
.build()
而 Kotlin 可以改成:
val series = diceSeries {
rolls = listOf(
diceRoll { value = 5 },
diceRoll {
value = 20
nickname = "critical hit"
}
)
}
至于如何序列化和反序列化,继续用刚才的例子,序列化是这样的:
series.toByteArray()
反序列化:
DiceSeries.parseFrom(message)