Convertisseur Protobuf vers JSON Schema
Colle un schéma .proto. Récupère un document JSON Schema 2020-12 avec une entrée $defs par message et par enum, prêt pour Ajv ou n'importe quel validateur moderne.
Entrée (schéma .proto)
Sortie (JSON Schema)
Ce que fait cet outil
Tu as un schéma Protocol Buffers et un service qui ingère du JSON — peut-être un handler de webhook, peut-être une gateway gRPC en HTTP transcoding, peut-être un API gateway qui valide la requête avant qu'elle n'atteigne ton code. Tu veux un document JSON Schema qui reflète le proto pour valider les payloads entrants, générer des fragments OpenAPI ou le passer dans un prompt de sortie structurée. Ce convertisseur fait ça dans ton navigateur — colle le .proto, copie le JSON Schema, lâche-le dans la config de ton validateur.
La sortie est en JSON Schema draft 2020-12 — le draft actuel, supporté par tout validateur moderne, y compris Ajv. Chaque message devient une entrée sous $defs avec type: "object" et une map properties. Chaque enum devient une entrée $defs avec type: "string" et les noms de valeur listés sous enum — calé sur la façon dont proto3 sérialise les enums par nom en JSON. Les références de champ passent par $ref: "#/$defs/MessageName" avec le nom feuille, donc les types imbriqués restent lisibles.
Le mapping de types suit la spec du mapping proto3 vers JSON. string/bool mappent directement. Les ints 32 bits passent en type: "integer" avec format: "int32" ; les unsigned 32 bits ajoutent minimum: 0. Les ints 64 bits deviennent type: "string" avec format: "int64" et un pattern numérique, parce que les Numbers JSON perdent en précision au-delà de 2^53 et que proto3 encode les entiers 64 bits comme des strings entre guillemets sur le wire. bytes devient une string avec contentEncoding: "base64". Les types bien connus comme google.protobuf.Timestamp mappent vers format: "date-time" selon la RFC 3339. La conversion tourne entièrement dans ton navigateur — ton schéma ne quitte pas la page.
Comment l'utiliser
Trois étapes. La sortie est un seul document JSON Schema que tu peux passer directement à un validateur.
Colle ton schéma .proto
Pose le schéma dans l'éditeur de gauche. syntax = "proto3"; en haut, c'est bon mais optionnel. Le parser gère les blocs message imbriqués, les déclarations enum, oneof, map<K, V> et les options de champ. Les directives import sont reconnues mais ignorées — colle les types importés inline si tu en as besoin.
Les noms de champ restent en snake_case (calés sur ce que l'encodeur JSON proto3 émet par défaut — pas de transformation). Si ton client met preserve_proto_field_names = false, passe les clés de propriété en camelCase à la main.
Lis le schéma
À droite : un document JSON Schema 2020-12 avec $id, title, un $ref de plus haut niveau pointant sur ton dernier message racine déclaré, et un bloc $defs contenant chaque message et enum du fichier. Chaque message devient un schema d'objet avec une map properties ; chaque enum devient un schema de string avec les noms de valeur. Lis le guide Understanding JSON Schema si tu as besoin de te rafraîchir sur la sémantique de $ref ou $defs.
Branche-le sur un validateur
Sauvegarde la sortie en schema.json, charge-la dans Ajv (Node) ou jsonschema (Python) ou le validateur de ta stack, puis fais-la tourner contre le JSON que reçoit ta gRPC-gateway ou ton webhook. Les écarts ressortent en chemins d'erreur lisibles, du genre /items/0/sku must be string. Le même schema peut aussi alimenter des définitions de composants OpenAPI 3.1 ou des prompts de sortie structurée pour un LLM.
Quand ça fait vraiment gagner du temps
Valider des webhooks définis en proto
Ton équipe utilise Protobuf comme source de vérité pour un événement OrderShipped, mais le récepteur réel du webhook reçoit du JSON — pas de runtime proto côté récepteur. Colle le .proto, dépose le JSON Schema dans Ajv et rejette les payloads mal formés à la frontière avant qu'ils n'atteignent la logique métier. Un SKU-101 sans quantity n'arrive jamais en base.
Construire un OpenAPI 3.1 depuis des schémas gRPC
Tu écris des specs OpenAPI 3.1 pour une gRPC-gateway. OpenAPI 3.1 est compatible JSON Schema 2020-12, donc le bloc $defs ici tombe direct sous components.schemas après un petit renommage. Pas de plugin protoc-gen-openapi à installer, pas de Buf CLI à mettre en place — juste coller, éditer, committer.
Sorties structurées LLM depuis un proto
Tu veux qu'OpenAI ou Anthropic renvoient un objet Order typé qui colle à ton .proto existant. Colle le schéma, prends l'entrée $defs/Order et passe-la comme JSON Schema dans response_format. Le modèle produit maintenant une sortie qui fait l'aller-retour avec ton service gRPC sans coercion manuelle.
Reviewer un changement d'API Protobuf
Un coéquipier backend a ajouté deux champs à Address et renommé une valeur d'enum. Tu veux voir comment ça affecte le JSON Schema que ta gateway utilise sans lancer toute la pipeline de codegen. Colle le nouveau .proto, diffe le schema avec ta copie committée, laisse un commentaire de review ciblé sur la PR.
Questions fréquentes
Mon schéma est-il envoyé quelque part ?
Non. Le parser et l'émetteur JSON Schema tournent entièrement dans ton navigateur en JavaScript. Ouvre les DevTools et regarde l'onglet Network pendant que tu colles — zéro requête. Pratique quand ton schéma contient des chemins de package internes, des noms de types ou tout ce que tu ne veux pas filer à un service tiers.
Quel draft JSON Schema vise la sortie ?
Le draft 2020-12, le draft publié actuel. L'URI $schema sur chaque document de sortie est https://json-schema.org/draft/2020-12/schema. Vois les release notes 2020-12 pour ce qui a changé depuis 2019-09. Tout validateur activement maintenu (Ajv 8+, jsonschema 4+ pour Python, NJsonSchema, Justify pour Java) supporte 2020-12 par défaut.
Pourquoi les champs int64 sont-ils des strings, pas des integers ?
Parce que c'est ce que dit la spec du mapping proto3 vers JSON, et elle a raison. Les Numbers JSON sont des doubles IEEE-754, qui perdent en précision au-delà de 2^53. Un vrai int64 peut porter des valeurs largement au-dessus de ce plafond — IDs de commande, timestamps en nanos, soldes de ledger — donc proto3 encode les entiers 64 bits comme des strings JSON entre guillemets. Le schema reflète ça avec type: "string", format: "int64" et un pattern numérique pour qu'un validateur rejette toujours "abc". Si ton serveur sort des ints 64 bits en Numbers JSON bruts (certaines gateways legacy le font), passe ces entrées à { "type": "integer" } à la main.
Pourquoi les enums sont-ils des strings, pas des integers ?
Même raison — c'est l'encodage JSON par défaut de proto3. Les enums se sérialisent par leur nom de valeur ("ORDER_STATUS_PAID") plutôt que par le numéro wire entier (2). Ça rend les payloads JSON lisibles et le schema plus simple. Les numéros entiers ne sont pas dans le JSON Schema parce qu'ils relèvent du wire-format, pas de la validation. Si tu as un encodeur non-default configuré pour émettre des ints, échange type: "string" contre type: "integer" sur l'entrée enum.
Comment gère-t-il map<K, V> ?
Rendu en { "type": "object", "additionalProperties": <V-schema> }. Les clés d'objet JSON sont toujours des strings, donc un map proto avec une clé non-string (par ex. map<int32, string>) reçoit une note descriptive expliquant que les clés runtime seront coercées en string. Le schema de la valeur suit les mêmes règles de mapping de type qu'un champ normal.
Les champs sont-ils marqués required ?
Non — les champs proto3 ont toujours un défaut dans le wire format et sont toujours présents dans la sortie JSON (avec des défauts vides comme "", 0, false, [], {}), donc le schema ne liste rien sous required. Si tu veux vraiment qu'un champ soit required à la validation, ajoute-le dans un tableau required sur le message parent à la main. Les champs optional de proto3 et oneof ne sont pas non plus appliqués comme oneOf dans la sortie — ce sont des sémantiques runtime que JSON Schema ne peut pas exprimer pleinement sans annotations supplémentaires.
Comment les messages imbriqués sont-ils référencés ?
Chaque message et enum est remonté dans un bloc $defs à plat, indexé par son nom feuille. Les références de champ passent par $ref: "#/$defs/MessageName". Aplatir garde le document compact et fait que les types imbriqués deux fois ne sont pas dupliqués. Si deux messages dans des packages différents partagent un nom feuille, le convertisseur garde la première définition — sépare les noms en conflit avant de coller si ça compte.
Je peux brancher ça direct dans Ajv ?
Oui. ajv.compile(schema) sur la sortie marche direct, à condition qu'Ajv soit configuré pour le draft 2020-12 (new Ajv2020() depuis ajv/dist/2020). Les entrées $ref résolvent en interne parce que tout est dans le même document. Si tu veux la validation de format (date-time, duration), ajoute ajv-formats à côté.
Outils liés
Si tu jongles avec Protobuf, JSON Schema et la validation, ceux-ci se complètent bien :