changvvb / scala-protobuf-java   0.3.0

Apache License 2.0 GitHub

A conversion tool between scala types and protobuf-java types.

Scala versions: 3.x 2.13 2.12


Scala CI codecov Maven Central


scala-protobuf-java has supported scala 3.

What is this

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.

Import to your project

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"



How to use

Basic usage

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 with corresponding members. You can convert Person to PBPerson like this

val builder = PBPerson.newBuilder()
builder.setName( => builder.setPhone(StringValue.of(p)))
val pbPerson:PBPerson =

On the contrary, you can convert PBPerson to Person like this

val person1 = Person(
  if(pbPerson.hasPhone) Some(pbPerson.getPhone.getValue) else None,

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]

Or you can use implicit style

import pbconverts.{ ProtoScalable, Converter}
implicit val protoScalable = ProtoScalble[Person,PBPerson]

In general, we often put the implicit value in companion object

object Person {
  implicit val protoScalable = ProtoScalble[Person,PBPerson]

Nested structure

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] and Scalable[SubMessage,PBSubMessage] with ProtoScalable[SubMessage,PBSubMessage]

Custom your own conversion

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 ( < 0) 0 else

Also, if you want convert protobuf to case class with the same logic, you can use ScalableBuilder

val customScalable = ScalableBuilder[Person,PBPerson]
                       .setField(, p => if(p.getId < 0) 0 else