PB.JSON (PB v0.1.0)

Copy Markdown View Source

ProtoJSON encoder/decoder for PB.

The headline API works directly with JSON binary strings: encode/4 returns a JSON-encoded binary, decode/4 parses a JSON-encoded binary. Parsing and serialisation are performed with OTP's built-in :json module (OTP 27+), with two ProtoJSON-required customisations baked in:

  • JSON null decodes as Elixir nil (and Elixir nil is encoded as the JSON null literal) — OTP's default mapping uses the atom :null, which PB does not.
  • Duplicate JSON object keys are rejected as kind: :malformed_json errors — OTP's default decoder silently keeps the last occurrence, masking ProtoJSON FieldNameDuplicate violations.

A secondary surface exposes the underlying term-in/term-out pipeline. to_term/4 returns a JSON-compatible Elixir term (maps, lists, strings, numbers, booleans, nil) and from_term/4 accepts one. Use these when composing with another JSON library or when asserting on the JSON shape directly.

Like the wire codec, these honor the schema's configured projections: the decoded/accepted message shape follows the schema's native representation (adapters, struct/unwrap), so under a projection the value may be a struct, a tuple, or a bare scalar rather than a plain map.

Supported

All scalar types, enums (by name or integer), repeated fields, maps, oneofs, proto2 and proto3 (including proto3 optional).

Wrapper messages (google.protobuf.Int32Value, StringValue, BoolValue, BytesValue, etc.) are serialised as their bare scalar value per ProtoJSON. google.protobuf.FieldMask is serialised as a single comma-separated string with each path converted between snake_case (proto) and lowerCamelCase (JSON). google.protobuf.Duration is serialised as a decimal-seconds string ending in "s". google.protobuf.Timestamp is serialised as an RFC 3339 / ISO 8601 string in UTC ending in "Z". google.protobuf.Value, Struct, and ListValue round-trip as bare JSON values (null/number/bool/string), objects, and arrays respectively. google.protobuf.Any serialises as an object with a @type URL plus the embedded message — flat-merged when the embedded message uses the default JSON object form, or under a value field when the embedded message itself has a custom JSON mapping (other WKTs, including nested Any). Only the type.googleapis.com/<fqn> URL prefix is accepted.

Options

  • :emit_defaults — when true, fields without presence that have their default value are emitted instead of omitted. Defaults to false.
  • :use_proto_names — when true, the encoder emits field names as written in the .proto file (snake_case) rather than lowerCamelCase. Parsers always accept both spellings. Defaults to false.
  • :enums_as_ints — when true, enums are encoded as their numeric value instead of the enum-value name. Defaults to false.
  • :ignore_unknown — when true, unknown JSON keys are skipped during decoding. Defaults to false (decode/4 returns {:error, %PB.ValueError{kind: :unknown_field}}).

Encode (encode/4, to_term/4) accepts :emit_defaults, :use_proto_names, and :enums_as_ints; decode (decode/4, from_term/4) accepts :ignore_unknown. Like the core codec, the option set is validated per direction: an unknown key (including a typo such as emit_default:), an option meant for the other direction, or a non-boolean value returns {:error, %PB.OptionError{}} rather than being silently ignored.

Summary

Functions

Decodes a JSON binary string into the map shape used by PB.encode/3. Returns {:ok, map} or {:error, error} (see PB.Error).

Decodes a JSON binary string into the map shape used by PB.encode/3, raising PB.Error on failure.

Encodes data (the same shape produced by PB.decode/4) as a JSON binary string. Returns {:ok, binary} or {:error, error} (see PB.Error).

Encodes data as a JSON binary string, raising PB.Error on failure.

Encodes data as JSON iodata. Returns {:ok, iodata} or {:error, error} (see PB.Error).

Encodes data as JSON iodata, raising PB.Error on failure.

Decodes a parsed JSON term into the map shape used by PB.encode/3. Returns {:ok, map} or {:error, error} (see PB.Error).

Decodes a parsed JSON term into the map shape used by PB.encode/3, raising PB.Error on failure.

Encodes data into a JSON-compatible Elixir term (maps, lists, strings, numbers, booleans, nil). Returns {:ok, term} or {:error, error} (see PB.Error).

Encodes data into a JSON-compatible Elixir term, raising PB.Error on failure.

Types

opts()

@type opts() :: [
  emit_defaults: boolean(),
  use_proto_names: boolean(),
  enums_as_ints: boolean(),
  ignore_unknown: boolean()
]

Functions

decode(json, schema, message_name, opts \\ [])

@spec decode(binary(), PB.schema_source(), PB.message_name(), opts()) ::
  {:ok, term()} | {:error, PB.Error.t()}

Decodes a JSON binary string into the map shape used by PB.encode/3. Returns {:ok, map} or {:error, error} (see PB.Error).

Two ProtoJSON-required behaviours are baked in: JSON null is mapped to Elixir nil, and duplicate JSON object keys are rejected as kind: :malformed_json errors.

decode!(json, schema, message_name, opts \\ [])

@spec decode!(binary(), PB.schema_source(), PB.message_name(), opts()) :: term()

Decodes a JSON binary string into the map shape used by PB.encode/3, raising PB.Error on failure.

This is the bang variant of decode/4.

encode(data, schema, message_name, opts \\ [])

@spec encode(term(), PB.schema_source(), PB.message_name(), opts()) ::
  {:ok, binary()} | {:error, PB.Error.t()}

Encodes data (the same shape produced by PB.decode/4) as a JSON binary string. Returns {:ok, binary} or {:error, error} (see PB.Error).

encode!(data, schema, message_name, opts \\ [])

@spec encode!(term(), PB.schema_source(), PB.message_name(), opts()) :: binary()

Encodes data as a JSON binary string, raising PB.Error on failure.

This is the bang variant of encode/4.

encode_iodata(data, schema, message_name, opts \\ [])

@spec encode_iodata(term(), PB.schema_source(), PB.message_name(), opts()) ::
  {:ok, iodata()} | {:error, PB.Error.t()}

Encodes data as JSON iodata. Returns {:ok, iodata} or {:error, error} (see PB.Error).

encode_iodata!(data, schema, message_name, opts \\ [])

@spec encode_iodata!(term(), PB.schema_source(), PB.message_name(), opts()) ::
  iodata()

Encodes data as JSON iodata, raising PB.Error on failure.

This is the bang variant of encode_iodata/4.

from_term(json_term, schema, message_name, opts \\ [])

@spec from_term(term(), PB.schema_source(), PB.message_name(), opts()) ::
  {:ok, term()} | {:error, PB.Error.t()}

Decodes a parsed JSON term into the map shape used by PB.encode/3. Returns {:ok, map} or {:error, error} (see PB.Error).

from_term!(json_term, schema, message_name, opts \\ [])

@spec from_term!(term(), PB.schema_source(), PB.message_name(), opts()) :: term()

Decodes a parsed JSON term into the map shape used by PB.encode/3, raising PB.Error on failure.

This is the bang variant of from_term/4.

to_term(data, schema, message_name, opts \\ [])

@spec to_term(term(), PB.schema_source(), PB.message_name(), opts()) ::
  {:ok, term()} | {:error, PB.Error.t()}

Encodes data into a JSON-compatible Elixir term (maps, lists, strings, numbers, booleans, nil). Returns {:ok, term} or {:error, error} (see PB.Error).

Use this when composing with a different JSON library or when asserting on the JSON shape directly.

to_term!(data, schema, message_name, opts \\ [])

@spec to_term!(term(), PB.schema_source(), PB.message_name(), opts()) :: term()

Encodes data into a JSON-compatible Elixir term, raising PB.Error on failure.

This is the bang variant of to_term/4.