Every field in a schema needs a type, and this page is the reference for what's 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.
Numeric Types
Other Scalar 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, built for clean integration with web APIs. 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 you can read yourself. struct.proto defines Struct and Value. wrappers.proto defines the wrapper types, though you rarely need those now that optional gives you explicit presence.
One more family of WKTs describes APIs and types themselves. They power runtime reflection and API tooling, and you will rarely write them by hand. The most interesting 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...
Encode the same message twice and you get identical JSON both times. That is not an accident. Protobuf's wire format is binary, but it also defines a canonical ProtoJSON mapping, and every payload maps to exactly one JSON representation under it.
| 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
Try to hold 9007199254740993 in a JavaScript number and it becomes 9007199254740992. JavaScript numbers are 64-bit floats, and they lose precision for integers above 253 - 1.
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.