JSON to Protobuf Generator
Backend and mobile engineers who adopt gRPC often begin with a JSON example from a REST predecessor or a vendor documentation page, then hand-write the equivalent .proto messages. This generator walks the sample payload, infers scalar types, spins nested objects into their own messages, marks arrays as repeated, and prints a proto3 schema ready for review. Everything runs locally, so internal service payloads never leave the browser.
How Type Inference Walks Your Payload
The generator parses the JSON with the built-in parser, then recursively visits every value. Strings become string, booleans become bool, integral numbers become int32 or int64 depending on magnitude, and decimals become double. Each plain object triggers a new message definition whose name is derived from its key, and fields are numbered from one in encounter order so the same input always yields the same schema.
Arrays inspect their first non-null element to decide the repeated type: an array of objects produces repeated MessageName, and an array of primitives produces repeated scalar. Nested messages are emitted before the messages that reference them, which some older toolchains prefer, and every identifier is screened against Protobuf reserved words so keywords like message or oneof never appear as field names.
Scalar mapping rules
Integers within the signed 32-bit range map to int32; anything larger but still a safe integer maps to int64. Floating-point values and numbers that lose integer fidelity map to double. Nulls and empty arrays default to string and repeated string respectively, with the FAQ documenting how to tighten those fields after export.
Naming and collision handling
Keys are converted to PascalCase for message names and lowerCamelCase for field names, matching the style guides most gRPC teams follow. A second message with the same derived name receives a numeric suffix, and a field whose name matches a reserved word gets an underscore appended. Leading digits in keys are prefixed so identifiers stay valid.
Sample payload mapped to a .proto schema
{
"shipment_id": "SHP-9921",
"insured": true,
"weight_kg": 12.5,
"recipient": {
"full_name": "Sam Rivera"
},
"tags": ["fragile", "perishable"]
} syntax = "proto3";
message Recipient {
string fullName = 1;
}
message Shipment {
string shipmentId = 1;
bool insured = 2;
double weightKg = 3;
Recipient recipient = 4;
repeated string tags = 5;
} Use Cases
| Developer | Production | Workflow |
|---|---|---|
| Bootstrap .proto messages from a vendor JSON sample | Seed a new gRPC microservice schema from legacy REST data | Review generated field numbers before a codegen review |
| Compare inferred types against a hand-written schema | Document an internal event payload as an IDL file | Onboard contractors with a compiled starter .proto |
| Check reserved-word collisions on legacy field names | Generate test fixtures that mirror production message shapes | Diff two vendors' payloads by comparing their schemas |
Common Mistakes and Troubleshooting
Pasting a top-level array is the most common error; Protobuf messages are objects, so wrap the array in an object key first and the generator will treat that key as a repeated message field. Trailing commas and single-quoted strings from hand-edited samples also fail the JSON parse and are reported before any schema is emitted.
A null-heavy payload will produce many string fields that should really be int64 or enum; treat those as placeholders and tighten them manually. Field numbers inferred from one sample are not safe to renumber once clients ship, because Protobuf wire format keys on the number rather than the name. If two different object shapes appear under the same array across samples, only the first element shapes the repeated message, so merge the variants by hand before publishing the schema.
Best Practices for Generated Schemas
Use a payload that exercises optional fields, not just the happy path, so optional keys appear in the message. Keep the generated file under version control and treat it as the source of truth from the moment it lands; further edits should happen in the .proto, not by regenerating from a moving JSON sample. Review int64 fields carefully, because JavaScript consumers often mishandle 64-bit integers unless they enable special handling.
Run the file through protoc or buf lint before merging; the generator reserves keywords but your team may enforce additional naming rules such as trailing underscores for private fields. When the schema will be public, replace the inferred scalar types with domain-specific enums where appropriate, and add package and service declarations in your normal IDL workflow. Pair the schema with the site\u2019s JSON and JSON Schema tools when you need to validate live traffic against the contract.
JSON to Protobuf Generator FAQ
Why generate a .proto file from a JSON sample?
gRPC services and Protobuf-based APIs need message definitions before any client code compiles, yet most teams start with a real JSON payload from a vendor or a captured response. Reverse-mapping that payload into a .proto saves the manual bookkeeping of naming messages, numbering fields, and classifying types. The generated schema is a starting point you refine, not a final authority, which still cuts the initial setup time significantly.
How are JSON numbers mapped to Protobuf types?
Integers that fit in 32 bits become int32, and larger safe integers become int64. Floats and any number with a decimal point become double. This covers the common cases without pulling in sint or fixed variants; if you specifically need sint32 for zig-zag encoded values or sfixed64 for raw binary payloads, adjust the generated field type by hand after export.
What happens to null values and empty arrays?
A null value carries no type information, so the field defaults to string and you should replace it with the real type once known. An empty array becomes repeated string for the same reason. Both cases appear frequently in partial API responses, and the generated comments plus this behavior keep the schema compiling while you fill in the true types.
How does the generator name nested messages?
Object keys are converted to PascalCase to form message names: recipient becomes Recipient, scan_events becomes ScanEvents and is singularized to ScanEvent when it appears as a repeated element. Collisions receive numeric suffixes, and names that collide with Protobuf reserved words such as message, enum, or oneof are suffixed with an underscore so the file still compiles.
Are field numbers stable across regenerations?
Field numbers are assigned sequentially in the order keys appear in your JSON object. JavaScript preserves insertion order for object keys, so the same payload always produces the same numbering. If you reorder keys in the sample, numbers shift, which would break wire compatibility with already-shipped clients. Freeze the sample, generate once, and treat the .proto as the source of truth from then on.
Does the generated schema use proto2 or proto3 syntax?
The output declares syntax = "proto3";, which is the default for new gRPC services and modern Google APIs. Proto3 drops required fields and raw default values in favor of presence semantics and optional keywords. If you maintain a legacy proto2 codebase, use the generated file as a reference and port the fields into your existing proto2 messages.
Is my JSON payload uploaded anywhere?
No. Type inference runs as plain recursive TypeScript in the browser. The payload, the generated schema, and any names you type stay in the page; nothing is sent to a server. That makes the tool appropriate for vendor contracts, internal service payloads, and anything covered by a non-disclosure agreement.
Related Tools
Mirror the same payload as TypeScript interfaces
Generate Go structs alongside your Protobuf messages
Produce dataclasses for the same sample payload
Emit a JSON Schema contract for the same object
Check payloads before using them as schema samples
Add C# POCO classes for .NET client projects