The library is designed around 4 Type Classes:

  • Message Decoder (aka Decoder)
  • Message Encoder (aka Encoder)
  • Field Decoder
  • Field Encoder

Message encoder & decoder:

protoless uses Encoder and Decoder type classes for encoding and decoding. An Encoder[A] instance provides a function that will convert any A to a binary, and a Decoding[A] takes a binary type to either an exception (DecodingFailure) or an A.

The binary type can be an Array[Byte], a ByteBuffer, or a ByteArray[Input|Output]Stream.

The distinctive feature of Protobuf compared to Json is that fields are referenced by a field number (index) rather than by their name. Consequently, encoders and decoders require to know type and number of each field you want to read/write.

Several implementations are available to fit multiple scenarios:

  • Decode all fields of the protobuf message, or just some of them.
  • Fields are numbered consecutively, or their numbering must be specified.
  • Fields must be transformed or validated after decoding
  • Protobuf numbering is known at compile-time or not

Field encoder & decoder:

Field decoders/encoders allow to read a specific type at a specific position in a protobuf message.

protoless provides implicit instances of these type classes for many types from the Scala standard library, including Int, String, BigDecimal, UUID, and others. It also provides instances for Seq[A], Option[A] and other generic types, but only if A has an encoder/decoder instance.

The implicit instance for types A where A has a Decoder[A] instance is also provided, in order to support protobuf nested fields.

Important note

For performance reasons protobuf messages are processed in a streaming fashion.

As a consequence, reading the same fields several times, trying to read a field with one type and retrying to read it with another type, or reading in reverse order is not allowed.