Every field in a schema needs a type. This reference lists the types available. Scalar types map directly to primitives your language already has. Well-known types cover common structures like timestamps. Every type, scalar or well-known, gets a defined JSON representation too, covered in the ProtoJSON mapping below.
Language mappings follow the generated code of each official runtime, with TypeScript from Protobuf-ES and Python from protobuf-py.
Non-Numeric Types
Numeric Types
Every compiler ships a set of schemas already written for you, called well-known types, or WKTs for short. Google standardizes them, so you import google.protobuf.Timestamp or any other one without declaring a dependency yourself. Each WKT also gets its own JSON mapping, so a Timestamp, for example, appears in JSON as an RFC 3339 string. Full definitions are in the google.protobuf reference.
General
Dynamic JSON
Like every well-known type, these are ordinary messages, defined in plain .proto files. struct.proto defines Struct and Value. wrappers.proto defines the wrapper types, though you rarely need those now that scalar fields track presence by default.
One more family of WKTs describes APIs and types themselves. They power runtime reflection and API tooling, and application code seldom uses them directly. The main ones are Method in api.proto, Type in type.proto, and SourceContext in source_context.proto.
JSON shows RFC 3339 and a seconds string; the bytes are just seconds and nanos fields encoded as varints.
edition = "2024"; package demo.v1; import "google/protobuf/timestamp.proto"; import "google/protobuf/duration.proto"; message Job { google.protobuf.Timestamp scheduled_at = 1; google.protobuf.Duration timeout = 2; }
Compiling the schema...
Protobuf's wire format is binary, but it also defines a standard ProtoJSON mapping: the rules for how each type appears in JSON, so JSON written by one library can be read by any other. It does not pin down the exact output. Whitespace, field order, and serializer options can differ between libraries and versions, so don't hash, sign, or byte-compare ProtoJSON.
| Protobuf Type | JSON Type(s) | JSON Value Example | Notes |
|---|---|---|---|
message | JSONObject | Example{"userName": "hiro"} | Serialized as a JSON object. Field names are mapped to lowerCamelCase by default, or the json_name option if set. |
repeated | JSONArray | Example["a", "b"] | Serialized as a JSON array. |
map<K, V> | JSONObject | Example{"k": "v"} | Serialized as a JSON object. |
int32, uint32 | JSONNumber | Example42 | Standard JSON numbers. |
float, double | JSONNumber | Example123.45 | Standard JSON numbers. |
bool | JSONBoolean | Exampletrue | Standard JSON booleans. |
int64, uint64 | JSONString | Example"9007199254740993" | Strings prevent precision loss in JS. |
enum | JSONString | Example"ROLE_ADMIN" | Uses the string name of the enum value. |
bytes | JSONString | Example"NDI=" | Base64 encoded string. |
google.protobuf.Timestamp | JSONString | Example"2023-10-01T12:00:00Z" | RFC 3339 formatted timestamp string. |
google.protobuf.Duration | JSONString | Example"1.000340012s" | Seconds with up to 9 fractional digits. |
google.protobuf.FieldMask | JSONString | Example"f.a,f.b" | Comma-separated paths as a single string. |
google.protobuf.Struct | JSONObject | Example{"foo": "bar"} | Standard representation for a generic JSON object. |
google.protobuf.Value | JSONAny | Example"foo" or 123 | Can be any valid JSON value (null, number, string, boolean, struct, or list). |
google.protobuf.NullValue | JSONnull | Examplenull | The JSON null value. |
google.protobuf.Empty | JSONObject | Example{} | An empty JSON object. |
64-bit Precision
Every number in JavaScript is a 64-bit float, which can only represent integers exactly up to 253 - 1. Past that, values get rounded. Parsing 9007199254740993 as a JSON number rounds it to 9007199254740992.
That is why 64-bit integer types (int64, fixed64, uint64, sint64, and sfixed64) are encoded as strings in JSON, not numbers.
64-bit values are strings in JSON so JavaScript's floats can't corrupt them. The canonical output keeps the quotes.
edition = "2024"; package demo.v1; message Record { int64 big_number = 1; bytes raw = 2; double ratio = 3; }
Compiling the schema...
Further Reading
- Protobuf Tip #10: Choosing the right integer type
int32, sint64, fixed32: which integer type to reach for, and what each costs as a varint.