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)