Generate kotlin data classes from protobuf files that supports Kotlin Native that can be serialized and
deserialized to protobuf using kotlinx.serialization.
- Supports
proto2,proto3andeditionsup to2023. - Generates
kotlinx.serializationannotations for proto field numbers and serialization format. - Generates Kotlin code for primitive fields such as
int32,string,bytes. - Generates Kotlin code for
message,enum,repeated,map,oneoftypes. - Generates Kotlin code that includes imports and uses nested types.
- Supports common well-known types such as
Timestamp,StringValueand serializes them to kotlin primitives or standards. - Support Well-Known Types deserialization to Well-Known Kotlin types such as
google.protobuf.Durationtokotlin.time.Durationandgoogle.protobuf.Timestamptokotlinx.datetime.Instant.- An option is added to code generator to enable this feature.
- More WKT additions will be added.
plugins {
kotlin("jvm") version "2.0.0"
id("org.jetbrains.kotlin.plugin.serialization") version "2.0.0"
id("com.google.protobuf") version "0.9.4"
}
var protobufVersion = "4.30.0"
dependencies {
// Runtime libraries include WKT conversion utilities and other libraries that are required such
// as kotlinx.datetime, kotlinx.coroutines, kotlinx.serialization, etc.
implementation("io.github.dogacel:kotlinx-protobuf-gen:0.1.0")
}protobuf {
protoc {
artifact = "com.google.protobuf:protoc:$protobufVersion"
}
plugins {
id("kotlinx-protobuf-gen") {
artifact = "io.github.dogacel:kotlinx-protobuf-gen:0.1.0:jvm8@jar"
}
}
// Enable Kotlin generation
generateProtoTasks {
all().forEach {
it.builtins {
remove("java") // Optionally you can keep the java generated files.
}
it.plugins {
id("kotlinx-protobuf-gen") {
option("package_prefix=custom.pkg") // Set a custom package prefix
}
}
}
}
}Add your proto files to a known proto file path such as src/main/proto.
syntax = "proto3";
package demo;
message Task {
int32 id = 1;
optional string description = 2;
Status status = 3;
enum Status {
WIP = 0;
DONE = 1;
}
}The following class will be generated and added to your classpath.
@Serializable
public data class Task(
@ProtoNumber(number = 1)
public val id: Int = 0,
@ProtoNumber(number = 2)
public val description: String? = null,
@ProtoNumber(number = 3)
public val status: Status = testgen.demo.Task.Status.WIP,
) {
@Serializable
public enum class Status {
@ProtoNumber(number = 0)
WIP,
@ProtoNumber(number = 1)
DONE,
}
}To customize the code generated, you can pass command line arguments or gradle options. For example,
protobuf {
protoc {
artifact = "com.google.protobuf:protoc:$protobufVersion"
}
plugins {
id("kotlinx-protobuf-gen") {
artifact = "io.github.dogacel:kotlinx-protobuf-gen:0.1.0jvm8@jar"
}
}
// Enable Kotlin generation
generateProtoTasks {
all().forEach {
it.builtins {
remove("java") // Optionally you can keep the java generated files.
}
it.plugins {
id("kotlinx-protobuf-gen") {
option("package_prefix=custom.pkg") // Set a custom package prefix
}
}
}
}
}| Option | Description | Default |
|---|---|---|
package_prefix |
Prefix for the generated package names. Appended to the start of each class | "" |
useCamelCase |
Whether to use the original snake_case for proto fields or camelCase. Can be either true or false. |
true |
generateServices |
Whether to generate abstract gRPC stubs or not. Can be either true or false. |
true |
The goal is to eventually support all features of Protobuf in Kotlin without depending on the Java library. Here is a list of features we are working on that are required to release first stable version:
- Proper serialization / deserialization of all types. Check "Known Issues" section below to see all major issues.
- Run full conformance tests on the generated code.
- Support Protobuf JSON format.
- Support various options such as
deprecated,default,json_name. - Auto-generated comments from
.protofiles in the generated code. - gRPC support.
- Stub generation is completed but it does not include any functionality to call or receive gRPC yet.
- It is tricky to support gRPC without depending on the Java library.
- Plugin and more option support for customizing the generated code. (Such as non-enforced nullability to gimmick proto2 required fields based on certain rules)
For the full list, check issues
An issue to track kotlinx.serialization: Kotlin/kotlinx.serialization#2401
Focusing on core functionality, here is a list of known major issues:
- Generated
oneoffields are flattened and not serialized correctly.- A flat list of oneof fields is generated. Validation happen in
initblock to make sure at most one field is set. One caveat is overlapping names which we can consider later. - Will consider sealed traits in the future.
- A flat list of oneof fields is generated. Validation happen in
- Generated
repeatedfields withfixedXX,sfixedXXanduintXXtypes can't be serialized. - Generated
repeatedfields withsintXXdeserializes incorrectly. - Generated
mapfields withfixedXXandsfixedXXkeys can't be serialized. - Generated
enumfields with negative values can't be serialized / deserialized. - Make data classes with
ByteArrayimplement equals and hashcode correctly.
Note
This section is applicable to official maintainers only.
- Update
versionunder rootbuild.gradle.kts. - Make sure you set
SONATYPE_USERNAME,SONATYPE_PASSWORD,GPG_SIGNING_KEYandGPG_SIGNING_PASSPHRASE. ./gradlew publishToSonatype./gradlew findSonatypeStagingRepository closeSonatypeStagingRepository./gradlew findSonatypeStagingRepository releaseSonatypeStagingRepository
For any errors, visit https://s01.oss.sonatype.org/#stagingRepositories.
After you release a -SNAPSHOT version, you need the following block to import it.
repositories {
maven {
this.url = uri("https://s01.oss.sonatype.org/content/repositories/snapshots/")
}
}For starters, start by checking issues.
There are two main components to this project. One is the code generator and the other is the generated code tests.
- Code generator can be found under app folder.
- There are several smaller classes / objects that are used to help manage the complexity of the code.
- Entry point is in
App.kt. Start reading the code by inspecting the entry point, it should be fairly straight-forward to understand.
- Generated code tests can be found under generated-code-tests folder.
- This is a separate subproject makes sure we are not breaking the compilation of our main app when the generated code is not compiling after making some changes.
- We store the generated code in version control showcase and review the generated code.
- Make sure you run
./gradlew buildafter you modify the code to generate the newest files. If newest files are not committed, the CI check will fail.
Linting can be done via
./gradlew ktlintFormatBuilding the whole project,
./gradlew buildCheck coverage of the code,
./gradlew koverHtmlReportPlease feel free to open issues and PRs.
