Proto to JSON Converter

Proto Input

JSON Output

What this tool does

Paste a Protocol Buffers (.proto) schema definition above and the tool generates a sample JSON document that conforms to it. The output follows the proto3 JSON mapping conventions: scalars become their JSON equivalents, repeated fields become arrays, nested message types become nested objects, and well-known types like google.protobuf.Timestamp are rendered as their canonical JSON form.

Note: this tool converts the schema text, not binary protobuf wire bytes. If you have a raw .pb file or hex-encoded payload from gRPC, you need a runtime decoder with access to the matching .proto file — protoc --decode, buf convert, or a language binding like protobuf-javascript.

What is Protocol Buffers?

Protocol Buffers (often shortened to protobuf or proto) is a schema-first binary serialization format originally developed at Google. You define your data structures once in a .proto file and then use a code generator (protoc) to produce typed accessors in Go, Java, Python, C++, Rust, Kotlin, Swift, TypeScript, and many other languages.

Compared to JSON, protobuf payloads are typically 3-10× smaller on the wire and 20-100× faster to parse, at the cost of being unreadable without the schema. That trade-off is why every backend system that cares about latency — gRPC, Google APIs, Kafka with Confluent Schema Registry, BigQuery streaming inserts, Envoy’s xDS protocol, Bazel’s remote-execution API — uses protobuf under the hood.

When you encounter .proto schemas

You typically end up wanting a JSON view of a protobuf schema when:

  • You’re integrating with a gRPC service and want to see what its request/response bodies look like in REST-friendly form.
  • A teammate sent you a .proto file and you want to mock a payload for testing.
  • You’re writing documentation or an API reference and need example payloads.
  • You’re debugging a system that logs proto messages and want to know which field is which.
  • You’re configuring something like gRPC-Gateway, grpc-web, Envoy, or Connect-RPC that uses proto3-JSON as its HTTP transport.

How proto3 maps to JSON

The proto3 spec defines a canonical JSON encoding you should know about:

Proto type JSON representation
string, bytes string (bytes is base64-encoded)
int32, uint32, sint32 JSON number
int64, uint64, fixed64 string (because JSON can’t safely represent 64-bit ints)
bool true / false
float, double JSON number, with "NaN", "Infinity", "-Infinity" allowed
enum the enum value’s name as a string
repeated T JSON array
map<K, V> JSON object with string keys
oneof exactly one of the variant fields set
google.protobuf.Timestamp RFC 3339 string, e.g. "2026-05-20T10:30:00Z"
google.protobuf.Duration string with s suffix, e.g. "3600s"
google.protobuf.Empty {}

Field names in canonical proto3-JSON use lowerCamelCase, even if your .proto declared them as snake_case — so user_id in the schema becomes userId in the JSON.

Common pitfalls

1. Missing fields don’t mean “null”. In proto3, scalar fields that are unset on the wire show up with their default value on the receiving side: 0 for numbers, "" for strings, false for bools, [] for repeated. There’s no way to tell “this field was explicitly zero” apart from “this field was never set” unless you wrap it in google.protobuf.Int32Value or use the optional keyword (proto3 re-added field presence in 3.15+).

2. 64-bit integers are strings. int64, uint64, sint64, fixed64, sfixed64 all serialize as JSON strings, because JavaScript and many JSON parsers lose precision above 2⁵³. If you naively JSON.parse() a proto-JSON message, your IDE may complain about strings where you expected numbers — that’s intentional.

3. Enum values can appear as either name or number. The canonical form uses the name ("STATUS_ACTIVE"), but parsers must also accept the numeric value (1). When generating mocks or fixtures, prefer the name.

4. bytes is base64, not hex. A bytes field over proto-JSON is always a base64-encoded string, never hex. Double-check this if you’re hand-crafting test payloads.

5. oneof lets exactly one variant be set. If two variants appear in the same JSON object, the parser will reject it. Make sure your sample uses only one.

6. Maps need string keys in JSON. map<int32, string> is legal in proto, but the JSON form stringifies the key: {"1": "foo", "2": "bar"}.

FAQs

What does this tool do? It takes a Protocol Buffers schema (a .proto file with syntax = "proto3"; and one or more message definitions) and generates a sample JSON document that conforms to that schema. It’s useful for previewing what your proto looks like in REST or gRPC-Gateway form, generating mock data for tests, or writing API documentation.
Does this tool decode binary protobuf payloads? No — this is a schema visualiser, not a wire-format decoder. To decode binary protobuf bytes you need the matching .proto schema and a runtime that knows how to parse the wire format. Try protoc --decode=YourMessage your_schema.proto < payload.bin, buf convert, or the protobuf library in your favourite language.
What proto syntax versions are supported? The tool is tuned for proto3, which is what most modern services use. Older proto2 schemas with required and optional keywords will mostly work, but proto2-specific features like extensions, groups, and field-presence tracking aren’t fully modelled in the output.
Why do some fields have generic placeholder values? Without a real payload, the tool fills each field with a representative sample ("Sample value" for strings, 42 for integers, true for booleans, etc.). Replace these with your actual test data; the goal is to show you the shape and field names of the JSON, not realistic content.
How do snake_case proto fields become camelCase in JSON? The proto3 JSON mapping rule says snake_case field names in the schema become lowerCamelCase in the JSON. So user_id becomes userId, last_login_at becomes lastLoginAt. Most JSON parsers will accept either form on input, but the canonical encoder always emits camelCase.
What about google.protobuf well-known types? Well-known types have special JSON forms: Timestamp"2026-05-20T10:30:00Z", Duration"3600s", Empty{}, Struct → an arbitrary JSON object, Value → any JSON value, FieldMask → a comma-joined string of field paths, and the wrapper types (Int32Value, StringValue, etc.) unwrap to their underlying scalar.
How do I convert binary proto bytes to JSON? You need the schema plus a decoder. The quickest options: (1) protoc --decode_raw < payload.bin if you don’t have the schema, which gives you a structural view; (2) protoc --decode=Package.MessageType schema.proto < payload.bin if you do; (3) buf convert --type=Package.MessageType --from payload.bin --to payload.json; (4) at runtime, call MessageType.decode(bytes).toJSON() in your language binding.