Conversor de Protobuf a JSON Schema
Pega un esquema .proto. Recibe un documento JSON Schema 2020-12 con una entrada $defs por cada message y enum, listo para Ajv o cualquier validador moderno.
Entrada (esquema .proto)
Salida (JSON Schema)
Qué hace esta herramienta
Tienes un esquema de Protocol Buffers y un servicio que recibe JSON — quizás un handler de webhook, quizás una pasarela gRPC con HTTP transcoding, quizás un API gateway que valida la petición antes de que llegue a tu código. Quieres un documento JSON Schema que refleje el proto para validar payloads entrantes, generar fragmentos de OpenAPI o pasarlo a prompts de salida estructurada. Este conversor lo hace en tu navegador — pega el .proto, copia el JSON Schema, mételo en la config de tu validador.
La salida es JSON Schema draft 2020-12 — el draft actual, soportado por todo validador moderno incluyendo Ajv. Cada message se convierte en una entrada bajo $defs con type: "object" y un mapa properties. Cada enum se convierte en una entrada de $defs con type: "string" y los nombres de valor listados bajo enum — coincidiendo con cómo proto3 serializa los enums por nombre en JSON. Las referencias de campo se resuelven con $ref: "#/$defs/MessageName" usando el nombre hoja, así los tipos anidados quedan legibles.
El mapeo de tipos sigue la especificación de mapeo proto3 a JSON. string/bool mapean directamente. Los enteros de 32 bits salen como type: "integer" con format: "int32"; los unsigned de 32 bits añaden minimum: 0. Los enteros de 64 bits se convierten en type: "string" con format: "int64" y un pattern numérico, porque los Numbers de JSON pierden precisión por encima de 2^53 y proto3 codifica los enteros de 64 bits como strings entre comillas en el wire. bytes se convierte en string con contentEncoding: "base64". Tipos bien conocidos como google.protobuf.Timestamp mapean a format: "date-time" según RFC 3339. La conversión corre íntegramente en tu navegador — tu esquema no sale de la página.
Cómo usarlo
Tres pasos. La salida es un único documento JSON Schema que puedes pasar directamente a un validador.
Pega tu esquema .proto
Suelta el esquema en el editor de la izquierda. syntax = "proto3"; arriba está bien pero es opcional. El parser maneja bloques message anidados, declaraciones enum, oneof, map<K, V> y opciones de campo. Las directivas import se reconocen pero se omiten — pega los tipos importados inline si los necesitas.
Los nombres de campo se mantienen en snake_case (coincidiendo con lo que el codificador JSON de proto3 emite por defecto — sin transformación). Si tu cliente fija preserve_proto_field_names = false, cambia las claves de propiedad a camelCase a mano.
Lee el esquema
A la derecha: un documento JSON Schema 2020-12 con $id, title, un $ref de nivel superior apuntando al último message raíz declarado, y un bloque $defs con cada message y enum del archivo. Cada message se vuelve un schema de objeto con un mapa properties; cada enum se vuelve un schema de string con los nombres de valor. Lee la guía Understanding JSON Schema si necesitas repasar la semántica de $ref o $defs.
Conéctalo a un validador
Guarda la salida como schema.json, cárgalo en Ajv (Node) o jsonschema (Python) o el validador que use tu stack, y luego pásalo contra el JSON que reciba tu gRPC-gateway o webhook. Los desajustes salen como rutas de error legibles tipo /items/0/sku must be string. El mismo schema también puede alimentar definiciones de componentes OpenAPI 3.1 o prompts de salida estructurada para un LLM.
Cuándo ahorra tiempo de verdad
Validar webhooks definidos en proto
Tu equipo usa Protobuf como fuente de verdad para un evento OrderShipped, pero el receptor real del webhook recibe JSON — no hay runtime de proto en el receptor. Pega el .proto, mete el JSON Schema en Ajv y rechaza payloads malformados en la frontera antes de que lleguen a la lógica de negocio. Un SKU-101 sin quantity no llega a la base de datos.
Construir OpenAPI 3.1 desde esquemas gRPC
Estás escribiendo specs OpenAPI 3.1 para un gRPC-gateway. OpenAPI 3.1 es compatible con JSON Schema 2020-12, así que el bloque $defs de aquí cae directamente bajo components.schemas tras un pequeño rename. Sin instalar el plugin protoc-gen-openapi, sin montar Buf CLI — solo pegar, editar, commitear.
Salidas estructuradas de LLM desde un proto
Quieres que OpenAI o Anthropic devuelvan un objeto Order tipado que coincida con tu .proto existente. Pega el esquema, coge la entrada $defs/Order y pásala como el JSON Schema de response_format. El modelo ahora produce salida que viaja round-trip por tu servicio gRPC sin coerciones manuales.
Revisar un cambio en una API Protobuf
Un compañero de backend añadió dos campos a Address y renombró un valor de enum. Quieres ver cómo eso afecta al JSON Schema que usa tu gateway sin correr toda la pipeline de codegen. Pega el nuevo .proto, haz diff del schema contra tu copia commiteada, deja un comentario de revisión enfocado en el PR.
Preguntas frecuentes
¿Mi esquema se envía a algún sitio?
No. El parser y el emisor de JSON Schema corren íntegramente en tu navegador como JavaScript. Abre las DevTools y mira la pestaña Network mientras pegas — cero peticiones. Útil cuando tu esquema incluye paths de package internos, nombres de tipo o cualquier cosa que no quieras pasar a un servicio de terceros.
¿Qué draft de JSON Schema apunta la salida?
Draft 2020-12, el draft publicado actual. El URI $schema en cada documento de salida es https://json-schema.org/draft/2020-12/schema. Mira las notas de release de 2020-12 para ver qué cambió desde 2019-09. Todo validador mantenido activamente (Ajv 8+, jsonschema 4+ para Python, NJsonSchema, Justify para Java) soporta 2020-12 por defecto.
¿Por qué los campos int64 son strings, no integers?
Porque eso es lo que dice la especificación de mapeo proto3 a JSON, y tiene razón. Los Numbers de JSON son doubles IEEE-754, que pierden precisión por encima de 2^53. Un int64 real puede llevar valores muy por encima de ese tope — IDs de pedido, timestamps en nanos, balances de ledger — así que proto3 codifica los enteros de 64 bits como strings JSON entre comillas. El schema lo refleja con type: "string", format: "int64" y un pattern numérico para que un validador siga rechazando "abc". Si tu servidor entrega ints de 64 bits como Numbers JSON crudos (algunos gateways legacy lo hacen), cambia esas entradas a { "type": "integer" } a mano.
¿Por qué los enums son strings, no integers?
Mismo motivo — es la codificación JSON por defecto de proto3. Los enums se serializan como su nombre de valor ("ORDER_STATUS_PAID") en lugar del número de wire entero (2). Eso hace que los payloads JSON sean legibles y el schema más simple. Los números enteros no están en el JSON Schema porque son una preocupación de wire-format, no de validación. Si tienes un encoder no estándar configurado para emitir ints, cambia type: "string" por type: "integer" en la entrada del enum.
¿Cómo maneja map<K, V>?
Lo renderiza como { "type": "object", "additionalProperties": <V-schema> }. Las claves de objeto JSON son siempre strings, así que un map de proto con clave no-string (p. ej. map<int32, string>) recibe una nota descriptiva explicando que las claves en runtime serán coercionadas a string. El schema del valor sigue las mismas reglas de mapeo de tipo que un campo regular.
¿Los campos se marcan como required?
No — los campos de proto3 siempre tienen un default en el wire format y siempre están presentes en la salida JSON (con defaults vacíos como "", 0, false, [], {}), así que el schema no lista nada bajo required. Si quieres que un campo sea realmente required en tiempo de validación, añádelo al array required del message padre a mano. Los campos optional de proto3 y oneof tampoco se aplican como oneOf en la salida — esas son semánticas de runtime que JSON Schema no puede expresar del todo sin anotaciones extra.
¿Cómo se referencian los messages anidados?
Cada message y enum se eleva a un bloque $defs plano indexado por su nombre hoja. Las referencias de campo van por $ref: "#/$defs/MessageName". Aplanar mantiene el documento compacto y significa que los tipos anidados dos veces no se duplican. Si dos messages en packages distintos comparten un nombre hoja, el conversor mantiene la primera definición — separa nombres conflictivos antes de pegar si te importa.
¿Puedo enchufar esto directamente a Ajv?
Sí. ajv.compile(schema) sobre la salida funciona out-of-the-box una vez que Ajv esté configurado para draft 2020-12 (new Ajv2020() de ajv/dist/2020). Las entradas $ref resuelven internamente porque todo está en el mismo documento. Si quieres validación de format (date-time, duration), añade ajv-formats al lado.
Herramientas relacionadas
Si trabajas con Protobuf, JSON Schema y validación, estas combinan bien: