scala-protobuf-java has supported scala 3.
scala-protobuf-java is a conversion tool between scala types and protobuf-java types. It can help impatient you save a lot of codes.
scala-protobuf-java use scala macro to generate what you need, so it's type safe at compile time. Now you just keep free to write your code, scala-proto-java will check the error at compile time.
sbt (for 2.12, 2.13 and scala 3. If you need more, just submit an issue or PR)
libraryDependencies += "com.github.changvvb" %% "scala-protobuf-java" % "0.3.0"maven
<dependency>
<groupId>com.github.changvvb</groupId>
<artifactId>scala-protobuf-java_2.13</artifactId>
<version>0.3.0</version>
</dependency>Define case class Person
case class Person(
id: Long,
name: String,
phone: Option[String],
hobbies: Seq[String])Define protobuf message PBPerson
message PBPerson {
int64 id = 1;
string name = 2;
google.protobuf.StringValue phone = 3;
repeated string hobbies = 4;
}Using protoc, it will generate PBPerson.java with corresponding members. You can convert Person to PBPerson like this
val builder = PBPerson.newBuilder()
builder.setId(person.id)
builder.setName(person.name)
person.phone.foreach(p => builder.setPhone(StringValue.of(p)))
person.hobbies.foreach(builder.addHobbies)
val pbPerson:PBPerson = builder.build()On the contrary, you can convert PBPerson to Person like this
val person1 = Person(
pbPerson.getId,
pbPerson.getName,
if(pbPerson.hasPhone) Some(pbPerson.getPhone.getValue) else None,
scala.collection.JavaConverters.iterableAsScalaIterable(pbPerson.getHobbiesList).toSeq
)Now, we can do it with a few codes with the help of scala-protobuf-java
import pbconverts.{ Protoable, Scalable }
val convertedPBPerson:PBPerson = Protoable[Person,PBPerson].toProto(person)
val convertedPerson:Person = Scalable[Person,PBPerson].toScala(pbPerson)More simplified
import pbconverts.ProtoScalable
val protoScalable = ProtoScalble[Person,PBPerson]
protoScalable.toProto(person)
protoScalable.toScala(pbPerson)Or you can use implicit style
import pbconverts.{ ProtoScalable, Converter}
implicit val protoScalable = ProtoScalble[Person,PBPerson]
Converter.toProto(person)In general, we often put the implicit value in companion object
object Person {
implicit val protoScalable = ProtoScalble[Person,PBPerson]
}
Converter.toScala(pbPerson)Define a nested case class ParentMessage
case class SubMessage(subValue:Int)
case class ParentMessage(parentValue:Int, subMessage:SubMessage)Define a nested protobuf message PBParentMessage
message PBSubMessage {
int32 sub_value = 1;
}
message PBParentMessage {
int32 parent_value = 1;
PBSubMessage sub_message = 2;
}import pbconverts.{ Scalable, Protoable }
implicit val subProtoable = Protoable[SubMessage,PBSubMessage]
Protoable[ParentMessage, PBParentMessage].toProto(ParentMessage(...))
implicit val subScalable = Scalable[SubMessage,PBSubMessage]
Scalable[ParentMessage, PBParentMessage].toScala(PBParentMessage.newBuilder().build())Hint: you can replace
Protoable[SubMessage,PBSubMessage]andScalable[SubMessage,PBSubMessage]withProtoScalable[SubMessage,PBSubMessage]
If you want use Protoable[Person, PBPerson] to convert a Person object to PBPerson object but you want custom field id.
You can use ProtoableBuilder:
val customProtoable = ProtoableBuilder[Person,PBPerson]
.setField(_.getId, p => if (p.id < 0) 0 else p.id)
.build
customProtoable.toProto(Person(...))Also, if you want convert protobuf to case class with the same logic, you can use ScalableBuilder
val customScalable = ScalableBuilder[Person,PBPerson]
.setField(_.id, p => if(p.getId < 0) 0 else p.id)
.build
customScalable.toScala(PBPerson.newBuilder().build)