Skipping the blank-file problem
Deciding *whether* to move from JSON to Protobuf is a separate question from actually writing the schema — see Protocol Buffers vs JSON: When Should You Switch? for that tradeoff. Once you've decided, the practical next step is usually staring at a blank .proto file and manually transcribing every field from an existing API response. JSONKit's JSON to Protobuf / Avro generator skips that step — paste a real JSON sample, get a starting schema back.
What gets inferred, and what you still need to check
{
"orderId": "ord_9182",
"total": 129.99,
"paid": true,
"items": [{ "sku": "SKU-1", "qty": 2 }]
}syntax = "proto3";
message Order {
string order_id = 1;
double total = 2;
bool paid = 3;
repeated Items items = 4;
}
message Items {
string sku = 1;
int32 qty = 2;
}Scalar types, nested messages, and repeated fields all infer cleanly from a single example — a string stays a string, a decimal becomes double, a nested object becomes its own message. What a single JSON sample fundamentally *can't* tell you: whether an integer field should be int32 or int64 if your sample value happens to be small right now but could grow past 2^31 later, and what a field typed null in your sample should really be (the generator flags this with a comment rather than guessing). Field numbers are also assigned sequentially in the order they appear — review and lock these in deliberately, since a Protobuf field number can never be reassigned to a different field once real messages using it exist anywhere.
The Avro side of the same tool
Switching the output format to Avro on the same input produces a record schema instead of a .proto message — useful if your target is Kafka, a data lake ingestion pipeline, or another Avro-native system rather than gRPC:
{
"type": "record",
"name": "Order",
"fields": [
{ "name": "order_id", "type": "string" },
{ "name": "total", "type": "double" },
{ "name": "paid", "type": "boolean" }
]
}Avro's schema is itself JSON, which makes it directly diffable and reviewable in a normal pull request the way a compiled .proto binary descriptor isn't — a real practical advantage during schema review, independent of Avro vs. Protobuf's other tradeoffs.
After generating: compile it, don't just paste it into production
The generated schema is a draft, not a finished artifact — run it through protoc/buf (Protobuf) or your Avro library's schema validation before treating it as final, and specifically review: field numbering (Protobuf), any field the tool flagged as inferred-from-null, and whether an integer field's size assumption (int32 vs int64) matches what the field will actually hold in production, not just in your one sample.