Customizations
#
Getting started with scalapb.protoScalaPB's code generator provides supports many different customizations. To
get access to these customizations, you need to import scalapb/scala.proto
in the proto files you want to customize. You can also have the options apply
to an entire proto3 package by using package-scoped options (see below).
To have scalapb/scalapb.proto
available to be imported in your project, add
the following SBT setting in your build.sbt
:
If you are invoking protoc
manually, you will need to ensure that the files in
protobuf
directory are available to your project.
#
ScalaPB File-level OptionsScalaPB file-level options lets you
- specify the name of the Scala package to use (the default is using the java package name).
- request that ScalaPB will not append the protofile name to the package name.
- specify Scala imports so custom base traits and custom types (see below) do not require the full class name.
The file-level options are not required, unless you are interested in those customizations. If you do not want to customize the defaults, you can safely skip this section.
#
File-level optionsscope
controls whether the specified options apply only for this proto files or for the entire package. Default isFILE
. See package-scoped options for more details.package_name
sets the Scala base package name, if this is not defined, then it falls back to thejava_package
option. If thejava_package
option is also not the found, then the package name from file'spackage
statement is used.Setting
flat_package
to true (default isfalse
) makes ScalaPB not append the protofile base name to the package name. You can also apply this option globally to all files by adding it to your ScalaPB SBT Settings.The
single_file
option makes the generator output all messages and enums to a single Scala file.The
java_conversions
options tells ScalaPB to generate converters to the corresponding Java messages in this file. It does not automatically trigger Java source code generation for the messages. If you need to generate source code in Java, includePB.gens.java
in the list of targets in sbt-protoc.The
preamble
is a list of strings that is output at the top of the generated Scala file. This option requiressingle_file
to be set. It is commonly used to define sealed traits that are extended using(scalapb.message).extends
- see custom base traits below and this example.The
object_name
option lets you customize the name of the generated class that contains various file-level members such as descriptors and a list of companion objects for the generated messages and enums. This is useful in case you are running into issues where the generated class name conflicts with other things in your project.Setting
lenses
tofalse
inhibits generation of lenses (default istrue
).Setting
getters
tofalse
inhibits generation of getters (default istrue
).Setting
retain_source_code_info
totrue
retains information in the descriptor that can be used to retrieve source code information from the descriptor at runtime (such as comments and source code locations). This option is turned off by default to conserve source size and memory at runtime. When this option is enabled, use thelocation
method on various descriptors to access source code information.By default, all non-required fields have default values in the constructor of the generated case classes. When setting
no_default_values_in_constructor
totrue
no default values will be generated for all fields. There is also a message-levelno_default_values_in_constructor
and field-levelno_default_value_in_constructor
. If the field-level setting is set, it overrides the message-level. If the message-level setting is set, it overrides the file-level setting.Typically, enum values appear in UPPER_CASE in proto files, and ScalaPB generates case objects with exactly the same name in Scala. If you would like ScalaPB to transform the names into CamelCase, set
enum_value_naming
toCAMEL_CASE
.It is a common practice in protobufs to prefix each enum value name with the name of the enum. For example, an enum name
Size
may have values namedSIZE_SMALL
andSIZE_LARGE
. When you setenum_strip_prefix
totrue
, ScalaPB will strip the enum's name from each value name, and they would becomeSMALL
andLARGE
. Then the name can be transformed to camel-case according toenum_value_naming
. Note that the prefix that is removed is the all-caps version of the enum name followed by an underscore.By default, during deserialization only known fields are retained. When setting
preserve_unknown_fields
totrue
, all generated messages in this file will preserve unknown fields. This is default behaviour in java for Proto3 messages since 3.5.0. In ScalaPB 0.10.0: the default of this field becametrue
for consistency with Java.Use
bytes_type
to customize the Scala type used for thebytes
field type. You will need to have an implicitTypeMapper[com.google.protobuf.ByteString, YourType]
instance so ScalaPb can convert back and forth to the type of your choice. That implicit will be found if it is defined underYourType
companion object, or on a package object that matches the generated code (or any of its parent packages).By default, ScalaPB generates Scala sources that are compatible with both Scala 2 and Scala 3. To generate sources that can be compiled error-free with
-source feature
on Scala 3 or with-Xsource:3
on Scala 2.13, setscala3_sources
totrue
or pass thescala3_sources
generator parameter.Use
public_constructor_parameters
to make constructor parameters public, including defaults and TypeMappers. This is helpful for automated schema derivation with e.g.magnolia
when trying to also derive default fields by using the compiler flag-Yretain-trees
. Without this flag, the companion object's_typemapper_*
fields are private.
#
Package-scoped optionsNote: this option is available in ScalaPB 0.8.2 and later.
Sometimes you want to have the same file-level options applied to all
the proto files in your project. To accomplish that, add a package.proto
file (the name does not matter) next to your proto files that looks like this:
All the options in this file will be applied to all proto files in the
package com.mypackage
and its sub-packages.
There is no need to explicitly import this file from other protos. If you are
using sbt-protoc
and the file is in the proto source directory (default is
src/main/protobuf
) then the file will be found and the options applied. If
you are invoking protoc in another way, you need to ensure that this
file is passed to protoc together with the rest of the files.
If you are generating Scala code for proto files that you don't own, you can
use this feature to customize code generation by creating a package.proto
file for that third-party package and include it within your proto source
directory.
The following rules are applied when validating package-scoped options:
- At most one file in each package may provide package-scoped options.
- Sub-packages may override package-scoped options provided by their parent
packages. The options are merged using the Protocol Buffers
mergeFrom
semantics. Specifically, this implies that repeated fields such asimport
andpreamble
are concatenated. - Proto files get the most specific package-scoped options for the package
they are in. File-level options defined in a proto file get merged with the
package-level options using
mergeFrom
. - Proto files with package-scoped options must have a
package
statement. This is to prevent the possibility of options applied globally. Standard classes that are shipped with ScalaPB already assume certain options, so overriding options globally may lead to compilation errors.
#
Publishing package-scoped optionsIf you are publishing a library that includes protos with package-scoped options, you need to make sure your library users source the package-scoped option proto file so the customizations are applied when they generate code.
Your users can simply import your package-scoped options from any proto file in their project to have the settings applied (a single import of the package-scoped options file would apply it globally for the code generator). However, since ScalaPB 0.10.11 and sbt-protoc 1.0.1, sbt-protoc provides a way to automate this with no need to manually import the package-scoped options file. This is accomplished by including a special attribute in the manifest of the library you publish. Add the following to your library's settings:
The path above is relative to the root directory of the published JAR (so src/main/protobuf
is not needed). Users add your library to their projects like this:
The first dependency provides the precompiled class files. The second dependency makes it possible
for users to import the protos in the jar file. sbt-protoc
will look for the
ScalaPB-Options-Proto
attribute in the jar's manifest and automatically add the package scoped options file
to the protoc command line.
note
Since the package-scoped options file is used as a source file in multiple projects, it should not define any types (messages, enums, services). This ensures that the package-scoped proto file does not generate any code on its own so we don't end up with duplicate class files.
#
Disabling package-scoped options processingAs a consumer of third-party dependencies that come with options proto, you can disable the behavior of automatically adding the options proto to protoc by setting
in sbt. In that case, it is your responsibility to either manually import
the option protos in one
of your own project source files so it gets applied, or ensure that the
generator settigs used in your project are consistent with the ones used to
generate the dependency. Differences in settings can lead to generated code
that does not compile.
#
Auxiliary optionsIn some situations, you may want to set some options in a proto file, but without modifying the original proto file or adding anything ScalaPB-specific to it. To accomplish that, you can define auxiliary options under package-scoped options.
For example, if you are given this proto file:
You can add a file package.proto
with the following content:
The list aux_message_options
contains options targeted at different messages define under the same proto package of the package-scoped options. The target
name needs to be fully-qualified message name in the protobuf namespace. Similar to aux_message_options
, we also have aux_enum_options
, aux_enum_value_options
and aux_field_options
. See example usage here. If the target is set *
then the options will be
applied to all the entities in the file or package (depending on the scope
option).
#
Primitive wrappersIn proto 3, unlike proto 2, primitives are not wrapped in an option by default.
The standard technique to obtain an optional primitive is to wrap it inside a
message (since messages are provided inside an Option
). Google provides
standard wrappers to the primitive types in
wrappers.proto.
primitive_wrappers
is enabled by default for ScalaPB>=0.6.0. Whenever one
of the standard wrappers is used, it will be mapped to Option[X]
where X
is a primitive type. For example:
would generate
To disable primitive wrappers in a file:
In versions of ScalaPB prior to 0.6.0, primitive wrappers had to be turned on manually in each file:
#
Custom base traits for messagesNote: this option is available in ScalaPB 0.6.1 and later.
ScalaPBs allows you to specify custom base traits to a generated case class. This is useful when you have a few messages that share common fields and you would like to be able to access those fields through a single trait.
Example:
In your code, define the base trait BaseCustomer
and include any subset of the fields:
You can specify any number of base traits for a message.
It is also possible to make the generated companion classes extend a class
or trait, by using the companion_extends
option. For example:
Will generate a case class that extends MySuperClass
, and the companion
object will extend MySuperCompanionClass
.
#
Custom base traits for sealed oneofsSince 0.9.0, you can use sealed_one_extends
to define one or more base traits for a generated SealedOneof.
Since 0.11.16, you can also add base traits to the empty case object using sealed_oneof_empty_extends
.
Use the following options to
As of ScalaPB 0.11.11 you may also use following option to make the generated sealed oneof
trait universal.
It may be useful when your sealed oneof variants are value-classes (e.g. extends AnyVal
)
#
Custom base traits for sealed oneofs companion objectsNote: this option is available in ScalaPB 0.11.11 and later.
Use the following option to define one or more base traits for a generated SealedOneof companion object:
#
Custom base traits for enumsIn a similar fashion to custom base traits for messages, it is possible to define custom base traits for enum types, for the companion objects of enum types and even for specific values.
For example:
The generated code will look something like this:
#
Custom typesYou can customize the Scala type of any field. One use-case for this is when
you would like to use type-safe wrappers around primitive values to enforce unit
correctness. For example, instead of using a raw integer for time fields, you can
wrap them in a Seconds
class.
We would like to write code like this:
How will ScalaPB know how to convert from the original type (Integer
) to the
custom type Seconds
? For each custom type you need to define an implicit
TypeMapper
that will tell ScalaPB how to convert between the custom type and
the base Scala type. A good place to define this implicit is in the companion
class for your custom type, since the Scala compiler will look for a
typemapper there by default. If your typemapper is defined elsewhere, you
will need to import it manually by using the import
file-level option.
TypeMapper
takes two function parameters. The first converts from the original type to
the custom type. The second function converts from the custom type to the
original type.
In addition to primitive values, you can customize enums and messages as well.
For more examples, see:
If you have a TypeMapper that maps a generated type into a type you don't own
(such as String
, or a third-party class) then you don't have access to the
companion object to define the typemapper in. Instead, you can place the
typemapper in one of the parent package objects of the generated code. For
example, if you want to map an enum to a string, and the message containing it
goes into the a.b.c
package, you can define the type mapper like this:
#
Message-level custom type and boxingIn the previous section you saw how to customize the type generated for a
specific field. ScalaPB also lets you specify a custom type at the message
level. When type
is set at the message level, that type is used for all the
fields that use that message. This eliminates the need to specify type
on each field
of this type.
In a Scala file define an implicit mapper:
Now, each time you reference Duration
in a proto file, the generated field in Scala code
will be of type MyDuration
:
If you do not want any instance of your message to be boxed (regardless if it
has a custom type), you can set no_box
at the message-level:
Then when this message is used, it will not be wrapped in an Option
. If
no_box
is specified at the field level, it overrides the value specified at
the message level.
#
Custom types on mapsSince version 0.6.0 it is possible to customize the key and value types of
maps. Like the custom types described above you will need to have a TypeMapper
for the custom type.
Example:
Example: see CustomMaps
in maps.proto
You can also customize the collection type used for a map. See the next section for details.
#
Custom collection typesBy default, ScalaPB compiles repeated fields into a Seq[T]
. When a message
is parsed from bytes, the default implementation instantiates a Vector[T]
,
which is a subtype of Seq[T]
. You can instruct ScalaPB to use a different
collection type for one field by specifying the collection_type
option. You
can also specify a collection_type
for the entire proto file by specifying a
collection_type
at the file-level.
If both are defined then the field-level setting wins.
Similar to collection_type
, we have map_type
for map types. By default,
ScalaPB generates scala.collection.immutable.Map
for maps, and you can
customize it at the field level, or file-level by specifying a map_type
option.
map_type
was introduced in ScalaPB 0.8.5.
Note on mutable collection: ScalaPB assumes that all data is immutable. For example, the result
of serializedSize
is cached in a private field. When choosing mutable collections, you must be
careful not to mutate any collection after it has been passed to any message, or you might get some
surprising results!
Note: using Array
is not supported along with Java conversions.
Note: Most Scala collections can be used with this feature. If you are trying
to implement your own collection type, it may be useful to check MyVector
and MyMap
, the simplest custom collection that is compatible with ScalaPB:
#
Custom namesSometimes it may be useful to manually specify the name of a field in the
generated code. For example, if you have a field named hash_code
, then the
camel-case version of it would be hashCode
. Since that name would conflict with
the hashCode()
method
we inherit from Java, ScalaPB issues an error. You can tell ScalaPB to use an
alternative name by using the scala_name
option:
It is also possible to customize the Scala name of an enum value:
The same customization can be applied to oneof
fields:
#
Adding annotationsSince ScalaPB 0.6.3, you can add annotations to the generated case classes like this:
In ScalaPB 0.7.0, you can add annotations to the companion object of a message and to individual fields:
In ScalaPB 0.10.9, you can also add annotations to the auto generated unknownFields field:
In ScalaPB 0.11.4, you can also add annotations to the enum values and the Unrecognized
case class:
#
Adding derives clauseIn ScalaPB 0.11.14, it is possible to add a derives
clause to generated messages and
sealed oneofs: