Eingabe (.proto-Schema)

Ausgabe (JSON Schema)

Was dieses Tool macht

Du hast ein Protocol-Buffers-Schema und einen Service, der JSON entgegennimmt — vielleicht ein Webhook-Handler, vielleicht ein HTTP-transcoded gRPC-Gateway, vielleicht ein API-Gateway, das die Request validiert, bevor sie deinen Code erreicht. Du willst ein JSON-Schema-Dokument, das das Proto spiegelt, um eingehende Payloads zu validieren, OpenAPI-Fragmente zu erzeugen oder es in Structured-Output-Prompts zu schicken. Dieser Konverter macht das in deinem Browser — füge das .proto ein, kopiere das JSON Schema, schmeiß es in deine Validator-Config.

Die Ausgabe ist JSON Schema draft 2020-12 — der aktuelle Draft, von jedem modernen Validator inklusive Ajv unterstützt. Jede message wird zu einem Eintrag unter $defs mit type: "object" und einer properties-Map. Jeder enum wird zu einem $defs-Eintrag mit type: "string" und den Wertenamen unter enum — passend dazu, wie proto3 Enums in JSON nach Namen serialisiert. Field-Referenzen lösen sich über $ref: "#/$defs/MessageName" mit dem Leaf-Namen auf, also bleiben verschachtelte Typen lesbar.

Das Type-Mapping folgt der proto3-JSON-Mapping-Spec. string/bool mappen direkt. 32-Bit-Ints werden zu type: "integer" mit format: "int32"; unsigned 32-Bit ergänzen minimum: 0. 64-Bit-Ints werden zu type: "string" mit format: "int64" und einem numerischen Pattern, weil JSON Numbers oberhalb 2^53 Präzision verlieren und proto3 64-Bit-Integer auf dem Wire als gequotete Strings kodiert. bytes wird zu einem String mit contentEncoding: "base64". Well-known Types wie google.protobuf.Timestamp mappen gemäß RFC 3339 auf format: "date-time". Die Konvertierung läuft komplett in deinem Browser — dein Schema verlässt die Seite nicht.

So nutzt du es

Drei Schritte. Die Ausgabe ist ein einzelnes JSON-Schema-Dokument, das du direkt einem Validator übergeben kannst.

1

Füge dein .proto-Schema ein

Wirf das Schema in den linken Editor. syntax = "proto3"; oben ist okay, aber optional. Der Parser kommt mit verschachtelten message-Blöcken, enum-Deklarationen, oneof, map<K, V> und Field Options klar. import-Direktiven werden erkannt, aber übersprungen — füge importierte Typen inline ein, falls du sie brauchst.

Field-Namen bleiben in snake_case (passend zu dem, was der proto3-JSON-Encoder standardmäßig ausgibt — keine Transformation). Wenn dein Client preserve_proto_field_names = false setzt, schalte die Property-Keys von Hand auf camelCase um.

2

Lies das Schema

Rechts: ein JSON-Schema-2020-12-Dokument mit $id, title, einem Top-Level-$ref, der auf deine zuletzt deklarierte Root-Message zeigt, und einem $defs-Block mit jedem Message und Enum aus der Datei. Jede Message wird zu einem Object-Schema mit einer properties-Map; jeder Enum wird zu einem String-Schema mit den Wertenamen. Lies den Guide Understanding JSON Schema, falls du die $ref- oder $defs-Semantik nochmal auffrischen willst.

3

Hänge es an einen Validator

Speichere die Ausgabe als schema.json, lade sie in Ajv (Node) oder jsonschema (Python) oder welchen Validator dein Stack auch nutzt, und feuere sie dann gegen das JSON, das deine gRPC-Gateway oder dein Webhook empfängt. Mismatches kommen als lesbare Error-Pfade wie /items/0/sku must be string raus. Dasselbe Schema kann auch OpenAPI-3.1-Component-Definitionen oder Structured-Output-Prompts für ein LLM speisen.

Wann das wirklich Zeit spart

Webhooks validieren, die in Proto definiert sind

Dein Team nutzt Protobuf als Source of Truth für ein OrderShipped-Event, aber der eigentliche Webhook-Empfänger bekommt JSON — auf der Empfängerseite läuft keine Proto-Runtime. Füge das .proto ein, schmeiß das JSON Schema in Ajv und lehne kaputte Payloads an der Edge ab, bevor sie die Business-Logic erreichen. Eine SKU-101 mit fehlender quantity kommt nie in der Datenbank an.

OpenAPI 3.1 aus gRPC-Schemas bauen

Du schreibst OpenAPI-3.1-Specs für eine gRPC-Gateway. OpenAPI 3.1 ist JSON-Schema-2020-12-kompatibel, also fällt der $defs-Block hier nach einem kleinen Rename direkt unter components.schemas. Kein protoc-gen-openapi-Plugin zu installieren, kein Buf CLI aufzusetzen — einfach einfügen, bearbeiten, committen.

LLM-Structured-Output aus einem Proto

Du willst, dass OpenAI oder Anthropic ein typisiertes Order-Objekt zurückgeben, das zu deinem bestehenden .proto passt. Füge das Schema ein, nimm den $defs/Order-Eintrag und übergib ihn als JSON Schema in response_format. Das Modell produziert jetzt Output, der ohne manuelle Coercion durch deinen gRPC-Service durchgeht.

Eine Protobuf-API-Änderung reviewen

Ein Backend-Kollege hat zwei Felder zu Address hinzugefügt und einen Enum-Wert umbenannt. Du willst sehen, wie sich das auf das JSON Schema auswirkt, das deine Gateway nutzt, ohne die ganze Codegen-Pipeline anzuwerfen. Füge das neue .proto ein, diff das Schema gegen deine committete Kopie, hinterlasse einen fokussierten Review-Kommentar am PR.

Häufige Fragen

Wird mein Schema irgendwohin gesendet?

Nein. Parser und JSON-Schema-Emitter laufen komplett in deinem Browser als JavaScript. Öffne die DevTools und schau im Network-Tab beim Einfügen zu — null Requests. Praktisch, wenn dein Schema interne Package-Pfade, Type-Namen oder irgendwas enthält, das du nicht an einen Drittanbieter übergeben willst.

Welchen JSON-Schema-Draft hat die Ausgabe als Ziel?

Draft 2020-12, den aktuell veröffentlichten Draft. Die $schema-URI auf jedem Ausgabedokument ist https://json-schema.org/draft/2020-12/schema. Schau in die 2020-12-Release-Notes für das, was sich seit 2019-09 geändert hat. Jeder aktiv gepflegte Validator (Ajv 8+, jsonschema 4+ für Python, NJsonSchema, Justify für Java) unterstützt 2020-12 standardmäßig.

Warum sind int64-Felder Strings, nicht Integers?

Weil das so in der proto3-JSON-Mapping-Spec steht, und das ist auch korrekt. JSON Numbers sind IEEE-754-Doubles, die oberhalb von 2^53 Präzision verlieren. Ein echter int64 kann Werte tragen, die deutlich über dieser Grenze liegen — Order-IDs, Timestamps in Nanosekunden, Ledger-Bilanzen — also kodiert proto3 64-Bit-Integer als gequotete JSON-Strings. Das Schema spiegelt das mit type: "string", format: "int64" und einem numerischen Pattern, damit ein Validator "abc" trotzdem ablehnt. Wenn dein Server 64-Bit-Ints als rohe JSON-Numbers ausliefert (manche Legacy-Gateways tun das), ändere diese Einträge per Hand auf { "type": "integer" }.

Warum sind Enums Strings, nicht Integers?

Gleicher Grund — das ist die JSON-Encoding-Default von proto3. Enums werden über ihren Wertenamen ("ORDER_STATUS_PAID") serialisiert, nicht über die Integer-Wire-Nummer (2). Das macht JSON-Payloads lesbar und das Schema einfacher. Die Integer-Nummern sind nicht im JSON Schema, weil sie eine Wire-Format-Sache sind, keine Validierungs-Sache. Wenn du einen nicht-default Encoder konfiguriert hast, der Ints emittiert, tausch type: "string" gegen type: "integer" beim Enum-Eintrag.

Wie wird map<K, V> behandelt?

Wird als { "type": "object", "additionalProperties": <V-schema> } gerendert. JSON-Object-Keys sind immer Strings, also bekommt eine Proto-Map mit einem Nicht-String-Key (z. B. map<int32, string>) eine Description-Note, die erklärt, dass die Runtime-Keys zu Strings gecoerct werden. Das Value-Schema folgt denselben Type-Mapping-Regeln wie ein normales Field.

Werden Felder als required markiert?

Nein — proto3-Felder haben im Wire-Format immer einen Default und sind in der JSON-Ausgabe immer da (mit leeren Defaults wie "", 0, false, [], {}), also listet das Schema nichts unter required. Wenn du wirklich willst, dass ein Field zur Validierungszeit required ist, füg es per Hand in ein required-Array auf der Parent-Message ein. proto3-optional-Felder und oneof werden in der Ausgabe auch nicht als oneOf erzwungen — das sind Runtime-Semantiken, die JSON Schema ohne zusätzliche Annotationen nicht voll ausdrücken kann.

Wie werden verschachtelte Messages referenziert?

Jede Message und jedes Enum wird in einen flachen $defs-Block hochgezogen, indiziert nach dem Leaf-Namen. Field-Referenzen gehen über $ref: "#/$defs/MessageName". Das Flachziehen hält das Dokument kompakt und sorgt dafür, dass zweifach verschachtelte Typen nicht dupliziert werden. Wenn zwei Messages in unterschiedlichen Packages denselben Leaf-Namen teilen, behält der Konverter die erste Definition — splitte konfligierende Namen vor dem Einfügen, falls das wichtig ist.

Kann ich das direkt in Ajv stecken?

Ja. ajv.compile(schema) auf der Ausgabe funktioniert sofort, sobald Ajv für draft 2020-12 konfiguriert ist (new Ajv2020() aus ajv/dist/2020). Die $ref-Einträge lösen sich intern auf, weil alles im gleichen Dokument liegt. Wenn du Format-Validierung willst (date-time, duration), pack ajv-formats dazu.

Verwandte Tools

Wenn du mit Protobuf, JSON Schema und Validierung hantierst, passen diese gut dazu: