Wejście (schemat .proto)

Wyjście (JSON Schema)

Co robi to narzędzie

Masz schemat Protocol Buffers i serwis, który łyka JSON — może handler webhooka, może gateway gRPC z HTTP transcoding, może API gateway, który waliduje request, zanim trafi do twojego kodu. Chcesz dokument JSON Schema, który odzwierciedla proto, żeby walidować przychodzące payloady, generować fragmenty OpenAPI albo wpychać go do promptów structured output. Ten konwerter robi to w przeglądarce — wklej .proto, skopiuj JSON Schema, wrzuć do konfiguracji walidatora.

Wyjście to JSON Schema draft 2020-12 — aktualny draft, wspierany przez każdy nowoczesny walidator, w tym Ajv. Każdy message staje się wpisem pod $defs z type: "object" i mapą properties. Każdy enum staje się wpisem $defs z type: "string" i nazwami wartości wymienionymi pod enum — zgodnie z tym, jak proto3 serializuje enumy po nazwie w JSON. Referencje pól rozwiązują się przez $ref: "#/$defs/MessageName" używając nazwy liścia, więc zagnieżdżone typy zostają czytelne.

Mapowanie typów idzie za specyfikacją mapowania proto3 na JSON. string/bool mapują się bezpośrednio. 32-bitowe inty dostają type: "integer" z format: "int32"; unsigned 32-bit dokładają minimum: 0. 64-bitowe inty stają się type: "string" z format: "int64" i numerycznym patternem, bo Numbery JSON tracą precyzję powyżej 2^53, a proto3 koduje 64-bitowe całkowite jako stringi w cudzysłowach na wire. bytes staje się stringiem z contentEncoding: "base64". Well-known typy jak google.protobuf.Timestamp mapują się na format: "date-time" według RFC 3339. Konwersja chodzi w całości w przeglądarce — twój schemat nie opuszcza strony.

Jak tego użyć

Trzy kroki. Wyjściem jest jeden dokument JSON Schema, który możesz dać prosto walidatorowi.

1

Wklej swój schemat .proto

Wrzuć schemat do edytora po lewej. syntax = "proto3"; na górze jest okej, ale opcjonalne. Parser ogarnia zagnieżdżone bloki message, deklaracje enum, oneof, map<K, V> i opcje pól. Dyrektywy import są rozpoznawane, ale pomijane — wklej importowane typy inline, jeśli ich potrzebujesz.

Nazwy pól zostają w snake_case (zgodnie z tym, co koder JSON proto3 emituje domyślnie — bez transformacji). Jeśli twój klient ustawia preserve_proto_field_names = false, przerób klucze property ręcznie na camelCase.

2

Przeczytaj schemat

Po prawej: dokument JSON Schema 2020-12 z $id, title, top-level $ref wskazującym na ostatnio zadeklarowany message-korzeń i blokiem $defs trzymającym każdy message i enum z pliku. Każdy message staje się schematem obiektu z mapą properties; każdy enum staje się schematem stringa z nazwami wartości. Przeczytaj przewodnik Understanding JSON Schema, jeśli potrzebujesz odświeżenia semantyki $ref albo $defs.

3

Podłącz to do walidatora

Zapisz wyjście jako schema.json, załaduj do Ajv (Node) albo jsonschema (Python) lub jakiego walidatora używa twój stack, a potem puść go na JSON, który dostaje twój gRPC-gateway albo webhook. Niezgodności wychodzą jako czytelne ścieżki błędów typu /items/0/sku must be string. Ten sam schemat może też zasilać definicje komponentów OpenAPI 3.1 albo prompty structured output dla LLM.

Kiedy faktycznie oszczędza czas

Walidacja webhooków zdefiniowanych w proto

Twój zespół używa Protobuf jako źródła prawdy dla zdarzenia OrderShipped, ale prawdziwy odbiorca webhooka dostaje JSON — po stronie odbiorcy nie ma runtime'u proto. Wklej .proto, wrzuć JSON Schema do Ajv i odrzucaj zniekształcone payloady na krawędzi, zanim trafią do logiki biznesowej. SKU-101 z brakującym quantity nigdy nie dociera do bazy.

Budowanie OpenAPI 3.1 ze schematów gRPC

Piszesz specki OpenAPI 3.1 dla gRPC-gateway. OpenAPI 3.1 jest kompatybilny z JSON Schema 2020-12, więc blok $defs stąd ląduje prosto pod components.schemas po małym przemianowaniu. Bez instalowania pluginu protoc-gen-openapi, bez stawiania Buf CLI — tylko wklej, edytuj, commituj.

Structured output LLM z proto

Chcesz, żeby OpenAI albo Anthropic zwrócił otypowany obiekt Order pasujący do twojego istniejącego .proto. Wklej schemat, weź wpis $defs/Order i podaj go jako JSON Schema w response_format. Model produkuje teraz wyjście, które robi round-trip przez twój serwis gRPC bez ręcznego rzutowania.

Review zmiany API Protobuf

Kolega z backendu dorzucił dwa pola do Address i przemianował wartość enuma. Chcesz zobaczyć, jak to wpływa na JSON Schema, którego używa twój gateway, bez odpalania całego pipeline'u codegen. Wklej nowy .proto, zrób diff schematu z twoją zacommitowaną kopią, zostaw skupiony komentarz review na PR.

Częste pytania

Czy mój schemat jest gdzieś wysyłany?

Nie. Parser i emiter JSON Schema chodzą w całości w twojej przeglądarce jako JavaScript. Otwórz DevTools i patrz na zakładkę Network podczas wklejania — zero żądań. Przydatne, gdy twój schemat zawiera wewnętrzne ścieżki package'y, nazwy typów albo cokolwiek, czego nie chcesz oddawać zewnętrznemu serwisowi.

Na jaki draft JSON Schema celuje wyjście?

Draft 2020-12, aktualnie opublikowany draft. URI $schema w każdym dokumencie wyjściowym to https://json-schema.org/draft/2020-12/schema. Zobacz release notes 2020-12, żeby zobaczyć, co się zmieniło od 2019-09. Każdy aktywnie utrzymywany walidator (Ajv 8+, jsonschema 4+ dla Pythona, NJsonSchema, Justify dla Javy) wspiera 2020-12 domyślnie.

Dlaczego pola int64 są stringami, a nie integerami?

Bo tak mówi specyfikacja mapowania proto3 na JSON i ma rację. Numbery JSON to double IEEE-754, które tracą precyzję powyżej 2^53. Prawdziwy int64 może nieść wartości daleko poza tym pułapem — ID zamówień, znaczniki czasu w nano, salda księgowe — więc proto3 koduje 64-bitowe całkowite jako stringi JSON w cudzysłowach. Schemat odzwierciedla to przez type: "string", format: "int64" i numeryczny pattern, żeby walidator dalej odrzucał "abc". Jeśli twój serwer wystawia 64-bitowe inty jako surowe Numbery JSON (niektóre legacy gateway'e tak robią), zmień te wpisy na { "type": "integer" } ręcznie.

Dlaczego enumy są stringami, a nie integerami?

Z tego samego powodu — to domyślne kodowanie JSON proto3. Enumy serializują się jako nazwa ich wartości ("ORDER_STATUS_PAID") zamiast całkowitego numeru wire (2). To czyni payloady JSON czytelnymi, a schemat prostszym. Numery całkowite nie są w JSON Schema, bo są kwestią wire format, a nie walidacji. Jeśli masz nie-domyślny enkoder skonfigurowany do emitowania intów, podmień type: "string" na type: "integer" we wpisie enuma.

Jak obsługuje map<K, V>?

Renderuje jako { "type": "object", "additionalProperties": <V-schema> }. Klucze obiektów JSON są zawsze stringami, więc proto map z kluczem nie-stringowym (np. map<int32, string>) dostaje notkę description tłumaczącą, że klucze runtime będą rzutowane na stringi. Schemat wartości idzie za tymi samymi regułami mapowania typu co zwykłe pole.

Czy pola są oznaczone jako required?

Nie — pola proto3 zawsze mają default w wire format i zawsze są obecne w wyjściu JSON (z pustymi defaultami jak "", 0, false, [], {}), więc schemat nie wymienia niczego pod required. Jeśli faktycznie chcesz, żeby pole było required w czasie walidacji, dodaj je do tablicy required na rodzicielskim message ręcznie. Pola optional proto3 i oneof też nie są wymuszane jako oneOf w wyjściu — to są semantyki runtime, których JSON Schema nie potrafi w pełni wyrazić bez dodatkowych adnotacji.

Jak referencowane są zagnieżdżone messagy?

Każdy message i enum jest podnoszony do płaskiego bloku $defs indeksowanego po nazwie liścia. Referencje pól idą przez $ref: "#/$defs/MessageName". Spłaszczanie trzyma dokument zwarty i powoduje, że typy zagnieżdżone dwukrotnie nie są duplikowane. Jeśli dwa messagy w różnych package'ach dzielą nazwę liścia, konwerter zachowuje pierwszą definicję — rozdziel kolidujące nazwy przed wklejeniem, jeśli to istotne.

Czy mogę to wpiąć prosto w Ajv?

Tak. ajv.compile(schema) na wyjściu działa od kopa, gdy Ajv jest skonfigurowany dla draftu 2020-12 (new Ajv2020() z ajv/dist/2020). Wpisy $ref rozwiązują się wewnętrznie, bo wszystko jest w tym samym dokumencie. Jeśli chcesz walidację formatu (date-time, duration), dorzuć ajv-formats obok.

Powiązane narzędzia

Jeśli walczysz z Protobuf, JSON Schema i walidacją, te dobrze się uzupełniają: