### Install protoc-gen-pubsub Plugin Source: https://github.com/bufbuild/protoschema-plugins/blob/main/README.md Install the protoc-gen-pubsub plugin using go install. Ensure you have Go installed and configured. ```sh go install github.com/bufbuild/protoschema-plugins/cmd/protoc-gen-pubsub@latest ``` -------------------------------- ### Install protoc-gen-jsonschema Source: https://github.com/bufbuild/protoschema-plugins/blob/main/README.md Install the protoc-gen-jsonschema plugin directly using go install. Ensure your Go environment is configured correctly. ```sh go install github.com/bufbuild/protoschema-plugins/cmd/protoc-gen-jsonschema@latest ``` -------------------------------- ### Example Generated JSON Schema (Lenient, Not Bundled) Source: https://context7.com/bufbuild/protoschema-plugins/llms.txt An example of a generated JSON Schema for a product message, demonstrating schema structure, properties, and references. ```json { "$id": "buf.protoschema.test.v1.Product.schema.json", "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "A product.", "description": "A product is a good or service that is offered for sale.", "type": "object", "additionalProperties": false, "required": ["product_id", "product_name", "location"], "properties": { "product_id": { "description": "The unique identifier for the product.", "anyOf": [ {"type": "integer", "minimum": -2147483648, "maximum": 2147483647}, {"type": "string", "pattern": "^\\-?[0-9]+$"} ] }, "product_name": {"description": "The name of the product.", "type": "string"}, "price": { "description": "The price of the product.", "anyOf": [ {"type": "number", "minimum": 0, "maximum": 3.4028234663852886e38}, {"type": "string", "pattern": "^-?[0-9]+(\\.[0-9]+)?([eE][+-]?[0-9]+)?$"} ], "default": 0 }, "tags": {"description": "The tags associated with the product.", "type": "array", "items": {"type": "string"}}, "location": {"$ref": "buf.protoschema.test.v1.Product.Location.schema.json", "description": "The location of the product."} }, "patternProperties": { "^(productId)$": {"anyOf": [{"type":"integer",...},{"type":"string",...}], "description": "..."}, "^(productName)$": {"type": "string", "description": "..."} } } ``` -------------------------------- ### Configure Remote Plugin in buf.gen.yaml Source: https://github.com/bufbuild/protoschema-plugins/blob/main/README.md Reference the protoc-gen-pubsub plugin as a remote plugin in your buf.gen.yaml configuration file. This is an alternative to direct installation. ```yaml version: v1 plugins: - plugin: buf.build/bufbuild/protoschema-pubsub out: ./gen ``` -------------------------------- ### Protobuf Definition for Product Source: https://github.com/bufbuild/protoschema-plugins/blob/main/README.md Example protobuf message definition for a 'Product' including nested messages and validation rules. This definition is used to generate JSON Schemas. ```proto message Product { // A point on the earth's surface. message Location { double lat = 1 [ (buf.validate.field).double.finite = true, (buf.validate.field).double.gte = -90, (buf.validate.field).double.lte = 90 ]; double long = 2 [ (buf.validate.field).double.finite = true, (buf.validate.field).double.gte = -180, (buf.validate.field).double.lte = 180 ]; } // The unique identifier for the product. int32 product_id = 1 [(buf.validate.field).required = true]; // The name of the product. string product_name = 2 [(buf.validate.field).required = true]; // The price of the product. float price = 3 [ (buf.validate.field).float.finite = true, (buf.validate.field).float.gte = 0 ]; // The tags associated with the product. repeated string tags = 4; // The location of the product. Location location = 5 [(buf.validate.field).required = true]; } ``` -------------------------------- ### Generated JSON Schema for Product.Location.schema.json Source: https://github.com/bufbuild/protoschema-plugins/blob/main/README.md Example of a generated JSON Schema for the nested Product.Location message. This schema defines latitude and longitude with specific constraints. ```json { "$id": "Location.schema.json", "$schema": "https://json-schema.org/draft/2020-12/schema", "additionalProperties": false, "title": "Location", "description": "A point on the earth's surface.", "type": "object", "properties": { "lat": { "anyOf": [ { "maximum": 90, "minimum": -90, "type": "number" }, { "pattern": "^-?[0-9]+(\\.[0-9]+)?([eE][+-]?[0-9]+)?$", "type": "string" } ], "description": "A point on the earth's surface." }, "long": { "anyOf": [ { "maximum": 180, "minimum": -180, "type": "number" }, { "pattern": "^-?[0-9]+(\\.[0-9]+)?([eE][+-]?[0-9]+)?$", "type": "string" } ], "description": "A point on the earth's surface." } }, "required": ["lat", "long"] } ``` -------------------------------- ### Generated JSON Schema for Product.schema.json Source: https://github.com/bufbuild/protoschema-plugins/blob/main/README.md Example of a generated JSON Schema for the Product protobuf message, using protobuf field names. This schema includes validation rules and references nested schemas. ```json { "$id": "Product.schema.json", "$schema": "https://json-schema.org/draft/2020-12/schema", "additionalProperties": false, "title": "A product.", "description": "A product is a good or service that is offered for sale.", "type": "object", "properties": { "product_id": { "description": "The unique identifier for the product.", "maximum": 2147483647, "minimum": -2147483648, "type": "integer" }, "product_name": { "description": "The name of the product.", "type": "string" }, "price": { "anyOf": [ { "maximum": 3.4028234663852886e38, "minimum": 0, "type": "number" }, { "pattern": "^-?[0-9]+(\\.[0-9]+)?([eE][+-]?[0-9]+)?$", "type": "string" } ], "default": 0, "description": "The price of the product." }, "tags": { "description": "The tags associated with the product.", "items": { "type": "string" }, "type": "array" }, "location": { "$ref": "Product.Location.schema.json", "description": "The location of the product." } }, "required": ["product_id", "product_name", "location"], "patternProperties": { "^(productId)$": { "description": "The unique identifier for the product.", "maximum": 2147483647, "minimum": -2147483648, "type": "integer" }, "^(productName)$": { "description": "The name of the product.", "type": "string" } } } ``` -------------------------------- ### protoc-gen-pubsub Plugin Entry Point Source: https://context7.com/bufbuild/protoschema-plugins/llms.txt This is the main entry point for the protoc-gen-pubsub plugin. It uses protoplugin.Main to register the plugin handler and set its version. ```go package main import ( "github.com/bufbuild/protoplugin" "github.com/bufbuild/protoschema-plugins/internal/protoschema" "github.com/bufbuild/protoschema-plugins/internal/protoschema/plugin/pluginpubsub" ) // This is the complete source of cmd/protoc-gen-pubsub/main.go. func main() { protoplugin.Main( protoplugin.HandlerFunc(pluginpubsub.Handle), protoplugin.WithVersion(protoschema.Version()), ) } ``` -------------------------------- ### Generate All Schema Variants with `(*Generator).Generate` Source: https://context7.com/bufbuild/protoschema-plugins/llms.txt Demonstrates how to generate schemas in various configurations (e.g., proto, proto-bundle, strict, JSON names) using the `(*Generator).Generate` method. Each configuration produces different output file suffixes. ```go package main import ( "encoding/json" "fmt" "github.com/bufbuild/protoschema-plugins/internal/protoschema/jsonschema" // assume msgDesc is obtained from a FileDescriptor as shown above ) func generateAllVariants(msgDesc interface{ /* protoreflect.MessageDescriptor */ }) { targets := []struct { name string opts []jsonschema.GeneratorOption }{ {"proto (lenient)", nil}, {"proto-bundle", []jsonschema.GeneratorOption{jsonschema.WithBundle()}}, {"proto-strict", []jsonschema.GeneratorOption{jsonschema.WithStrict()}}, {"proto-strict-bundle", []jsonschema.GeneratorOption{jsonschema.WithStrict(), jsonschema.WithBundle()}}, {"json (camelCase, lenient)", []jsonschema.GeneratorOption{jsonschema.WithJSONNames()}}, {"json-bundle", []jsonschema.GeneratorOption{jsonschema.WithJSONNames(), jsonschema.WithBundle()}}, {"json-strict", []jsonschema.GeneratorOption{jsonschema.WithJSONNames(), jsonschema.WithStrict()}}, {"json-strict-bundle", []jsonschema.GeneratorOption{jsonschema.WithJSONNames(), jsonschema.WithStrict(), jsonschema.WithBundle()}}, } // Each target corresponds to one of the 8 output file suffixes: // .schema.json / .schema.bundle.json / .schema.strict.json / .schema.strict.bundle.json // .jsonschema.json / .jsonschema.bundle.json / .jsonschema.strict.json / .jsonschema.strict.bundle.json for _, t := range targets { gen := jsonschema.NewGenerator(t.opts...) // gen.Add(msgDesc) <-- add your descriptor here schemas := gen.Generate() for name, schema := range schemas { data, _ := json.MarshalIndent(schema, "", " ") fmt.Printf("[%s] %s: %d bytes\n", t.name, name, len(data)) } } } ``` -------------------------------- ### Configure Generator with Options Source: https://context7.com/bufbuild/protoschema-plugins/llms.txt Illustrates how to use functional options like `WithJSONNames`, `WithStrict`, `WithBundle`, and `WithAdditionalProperties` when creating a new `Generator`. These options control schema output format, bundling, and property handling. ```go package main import "github.com/bufbuild/protoschema-plugins/internal/protoschema/jsonschema" func optionExamples() { // WithJSONNames: use camelCase JSON field names as primary (proto_name becomes alias) // Without: proto_field_name is primary (jsonName becomes patternProperties alias) genJSON := jsonschema.NewGenerator(jsonschema.WithJSONNames()) // WithStrict: disallow aliases, string-encoded numbers, implicit default omission. // Required when validating ProtoJSON output with "always emit fields without presence". // Strict schemas use "required" for all non-optional proto3 scalar fields. genStrict := jsonschema.NewGenerator(jsonschema.WithStrict()) // WithBundle: inline all $ref dependencies into $defs of the root schema. // Without: each message gets its own file and uses relative $ref paths. genBundle := jsonschema.NewGenerator(jsonschema.WithBundle()) // WithAdditionalProperties: set additionalProperties=true (default: false). // Use when the receiver may have an older schema version than the sender. genOpen := jsonschema.NewGenerator(jsonschema.WithAdditionalProperties()) // Combine for a single strict, bundled, camelCase schema with open additional props: genAll := jsonschema.NewGenerator( jsonschema.WithJSONNames(), jsonschema.WithStrict(), jsonschema.WithBundle(), jsonschema.WithAdditionalProperties(), ) _ = genJSON; _ = genStrict; _ = genBundle; _ = genOpen; _ = genAll } ``` -------------------------------- ### Protoc Plugin Entry Point for JSON Schema Generation Source: https://context7.com/bufbuild/protoschema-plugins/llms.txt The `pluginjsonschema.Handle` function is the `protoplugin.HandlerFunc` for `protoc-gen-jsonschema`. It processes plugin parameters like `target` and `additional_properties`, generates schema variants, and writes them as output files. It supports proto3 optional and Editions 2023. ```go package main import ( "github.com/bufbuild/protoplugin" "github.com/bufbuild/protoschema-plugins/internal/protoschema" "github.com/bufbuild/protoschema-plugins/internal/protoschema/plugin/pluginjsonschema" ) // This is the complete source of cmd/protoc-gen-jsonschema/main.go. // Run as a protoc/buf plugin: reads a CodeGeneratorRequest on stdin, // writes a CodeGeneratorResponse on stdout. func main() { protoplugin.Main( protoplugin.HandlerFunc(pluginjsonschema.Handle), protoplugin.WithVersion(protoschema.Version()), ) } // Plugin parameter examples (passed via buf.gen.yaml opt: or --jsonschema_opt=): // // Generate all 8 variants (default when no target is specified): // (no opt) // // Generate only proto-named and JSON strict-bundle: // target=proto+json-strict-bundle // // Allow additional unknown fields and generate proto + json: // target=proto+json,additional_properties=true // // All valid target names: // proto | proto-bundle | proto-strict | proto-strict-bundle // json | json-bundle | json-strict | json-strict-bundle // all (shorthand for all 8) ``` -------------------------------- ### Create JSON Schema Generator with Options Source: https://context7.com/bufbuild/protoschema-plugins/llms.txt Constructs a Generator with various options to control naming, strictness, bundling, and additional properties. Add message descriptors to the generator before calling Generate. ```go package main import ( "encoding/json" "fmt" "github.com/bufbuild/protoschema-plugins/internal/protoschema/jsonschema" examplev1 "github.com/bufbuild/protoschema-plugins/internal/gen/proto/buf/protoschema/test/v1" "google.golang.org/protobuf/reflect/protoreflect" ) func main() { // Proto field names, lenient (default): allows string-encoded numbers, aliases gen := jsonschema.NewGenerator() // JSON camelCase field names, strict, bundled into a single file per message strictBundledGen := jsonschema.NewGenerator( jsonschema.WithJSONNames(), jsonschema.WithStrict(), jsonschema.WithBundle(), ) // Allow extra fields (useful when receiver has an older schema version) lenientGen := jsonschema.NewGenerator( jsonschema.WithAdditionalProperties(), ) // Add a message descriptor to each generator msgDesc := examplev1.File_buf_protoschema_test_v1_examples_proto. Messages().ByName("Product") for _, g := range []*jsonschema.Generator{gen, strictBundledGen, lenientGen} { if err := g.Add(msgDesc); err != nil { panic(err) } schemas := g.Generate() // map[protoreflect.FullName]map[string]any for name, schema := range schemas { data, _ := json.MarshalIndent(schema, "", " ") fmt.Printf("=== %s ===\n%s\n", name, data) } } } ``` -------------------------------- ### Generate PubSub Proto2 Schema Source: https://context7.com/bufbuild/protoschema-plugins/llms.txt Use `pubsub.Generate` to create a compact proto2 source string from a `protoreflect.MessageDescriptor`. This output can be directly registered as a Google Cloud PubSub schema. Ensure `msgDesc` is a top-level message. ```go package main import ( "fmt" "github.com/bufbuild/protoschema-plugins/internal/protoschema/pubsub" // assume msgDesc is a protoreflect.MessageDescriptor for a top-level message ) func main() { // msgDesc must be a top-level message (not nested inside another message) // Obtain via: someFile.Messages().Get(0) or .ByName("MyMessage") data, err := pubsub.Generate(msgDesc) if err != nil { fmt.Printf("error: %v\n", err) return } fmt.Println(data) // Output: compact proto2 source, e.g.: // syntax = "proto2"; message Product { required int32 product_id = 1; optional string product_name = 2; ... message Inline_buf_protoschema_test_v1_Product_Location { ... } } // // The output file is named ".pubsub.proto" by the plugin handler. // pubsub.FileExtension == "pubsub.proto" } ``` -------------------------------- ### pubsub.Generate Source: https://context7.com/bufbuild/protoschema-plugins/llms.txt `Generate` takes a `protoreflect.MessageDescriptor` and returns a compact, self-contained proto2 source string normalizing all transitive dependencies into nested message types. The output can be registered directly as a Google Cloud PubSub schema. ```APIDOC ## `pubsub.Generate` — Generate a PubSub Proto2 Schema ### Description Takes a `protoreflect.MessageDescriptor` and returns a compact, self-contained proto2 source string normalizing all transitive dependencies into nested message types. The output can be registered directly as a Google Cloud PubSub schema (type: PROTOCOL_BUFFER). ### Parameters - **msgDesc** (`protoreflect.MessageDescriptor`) - Required - The message descriptor to generate the schema from. Must be a top-level message. ### Returns - **string** - The generated proto2 source string. - **error** - An error if the generation fails. ### Example ```go import ( "fmt" "github.com/bufbuild/protoschema-plugins/internal/protoschema/pubsub" // assume msgDesc is a protoreflect.MessageDescriptor for a top-level message ) func main() { data, err := pubsub.Generate(msgDesc) if err != nil { fmt.Printf("error: %v\n", err) return } fmt.Println(data) } ``` ``` -------------------------------- ### Go Code for JSON Schema Generation with Annotations Source: https://context7.com/bufbuild/protoschema-plugins/llms.txt Illustrates the Go code used to generate a JSON Schema, highlighting how annotations like 'jsonschema:ignore' and 'jsonschema:hide' affect the output. ```go // Resulting generated schema properties will contain only "api_key". // "deprecated_alias" appears only in patternProperties (regex-keyed, no IDE hint). // "internal_debug_token" does not appear anywhere in the output. gen := jsonschema.NewGenerator() // gen.Add(configDesc) // gen.Generate() -> schema with 1 property ("api_key") + 1 patternProperty ("deprecated_alias") ``` -------------------------------- ### pluginjsonschema.Handle Source: https://context7.com/bufbuild/protoschema-plugins/llms.txt `Handle` is the `protoplugin.HandlerFunc` that powers the `protoc-gen-jsonschema` binary. It parses plugin parameters, iterates top-level messages, generates schema variants, and writes each schema as an output file. ```APIDOC ## `pluginjsonschema.Handle` — protoc Plugin Entry Point (JSON Schema) ### Description This function serves as the `protoplugin.HandlerFunc` for the `protoc-gen-jsonschema` binary. It processes plugin parameters like `target` and `additional_properties`, iterates through top-level messages in specified files, generates various JSON schema variants, and outputs each schema with a filename derived from its `$id`. It also declares support for proto3 optional fields and Editions 2023. ### Handler Function `pluginjsonschema.Handle` ### Usage This function is intended to be used as the handler for `protoplugin.Main`. ### Parameters (Plugin Options) These parameters are passed via `buf.gen.yaml` (opt:) or `protoc` command line (`--jsonschema_opt=`): - **target** (string) - Specifies which schema variants to generate. Valid values include: `proto`, `proto-bundle`, `proto-strict`, `proto-strict-bundle`, `json`, `json-bundle`, `json-strict`, `json-strict-bundle`. The value `all` is a shorthand for all 8 variants. If not specified, all variants are generated by default. - **additional_properties** (boolean) - If `true`, allows additional unknown fields in the generated schemas. Defaults to `false`. ### Example Configuration (buf.gen.yaml) ```yaml version: v1 plugins: - plugin: jsonschema out: gen/jsonschema opt: - target=proto+json - additional_properties=true ``` ### Example Go Code (main.go) ```go package main import ( "github.com/bufbuild/protoplugin" "github.com/bufbuild/protoschema-plugins/internal/protoschema" "github.com/bufbuild/protoschema-plugins/internal/protoschema/plugin/pluginjsonschema" ) func main() { protoplugin.Main( protoplugin.HandlerFunc(pluginjsonschema.Handle), protoplugin.WithVersion(protoschema.Version()), ) } ``` ``` -------------------------------- ### Configure buf.gen.yaml for JSON Schema Generation Source: https://github.com/bufbuild/protoschema-plugins/blob/main/README.md Reference the protoc-gen-jsonschema plugin as a Remote Plugin in your buf.gen.yaml file. Specify the output directory for the generated schemas. ```yaml version: v1 plugins: - plugin: buf.build/bufbuild/protoschema-jsonschema out: ./gen ``` -------------------------------- ### Generated JSON Schema Output File Naming Conventions Source: https://context7.com/bufbuild/protoschema-plugins/llms.txt Lists the naming conventions for various JSON Schema output files, including proto-named and JSON-named variants, with and without bundling, and strict or lenient modes. ```text # Proto-named (snake_case), lenient .schema.json # Proto-named, lenient, bundled ($defs inlined) .schema.bundle.json # Proto-named, strict .schema.strict.json # Proto-named, strict, bundled .schema.strict.bundle.json # JSON-named (camelCase), lenient .jsonschema.json # JSON-named, lenient, bundled .jsonschema.bundle.json # JSON-named, strict .jsonschema.strict.json # JSON-named, strict, bundled .jsonschema.strict.bundle.json ``` -------------------------------- ### Normalize Protobuf Descriptors with Dependencies Source: https://context7.com/bufbuild/protoschema-plugins/llms.txt Use `normalize.NewNormalizer` and `(*Normalizer).Normalize` to resolve a `protoreflect.MessageDescriptor` into a single `descriptorpb.DescriptorProto` with all cross-file references inlined. This is useful for custom schema generation. Proto3 enums are converted to `int32` fields. You can use `normalize.WithSkipTypes` to prevent specific types from being inlined. ```go package main import ( "fmt" "github.com/bufbuild/protoschema-plugins/internal/protoschema/normalize" "google.golang.org/protobuf/proto" ) func main() { // Basic usage — normalize a message with all defaults n := normalize.NewNormalizer() rootPb, err := n.Normalize(msgDesc) // msgDesc: protoreflect.MessageDescriptor if err != nil { panic(err) } fmt.Printf("normalized message: %s, nested types: %d\n", rootPb.GetName(), len(rootPb.GetNestedType())) // e.g.: normalized message: Product, nested types: 3 // Skip specific types (leave them as external references, not inlined) nSkip := normalize.NewNormalizer( normalize.WithSkipTypes( "google.protobuf.Timestamp", "google.protobuf.Duration", ), ) rootPb2, err := nSkip.Normalize(msgDesc) if err != nil { panic(err) } // After normalizing, look up a specific descriptor proto by its original descriptor refDesc := msgDesc.Fields().ByName("location").Message() located, err := n.FindDescriptorProto(refDesc) if err != nil { panic(err) } fmt.Printf("found nested proto: %s\n", located.GetName()) // e.g.: found nested proto: Inline_buf_protoschema_test_v1_Product_Location _ = rootPb2 _ = proto.Size(rootPb) // rootPb is a standard *descriptorpb.DescriptorProto } ``` -------------------------------- ### Register Protobuf Message Descriptor for Schema Generation Source: https://context7.com/bufbuild/protoschema-plugins/llms.txt Registers a protoreflect.MessageDescriptor for schema generation. It recursively processes referenced types and resolves buf.validate constraints. Call Add once per top-level message before Generate. ```go package main import ( "fmt" "github.com/bufbuild/protoschema-plugins/internal/protoschema/jsonschema" "google.golang.org/protobuf/reflect/protoreflect" "google.golang.org/protobuf/types/dynamicpb" "google.golang.org/protobuf/types/descriptorpb" "google.golang.org/protobuf/reflect/protodesc" "google.golang.org/protobuf/proto" ) func main() { // Build a minimal FileDescriptorProto programmatically fdp := &descriptorpb.FileDescriptorProto{ Name: proto.String("example.proto"), Syntax: proto.String("proto3"), MessageType: []*descriptorpb.DescriptorProto{ { Name: proto.String("Order"), Field: []*descriptorpb.FieldDescriptorProto{ {Name: proto.String("order_id"), Number: proto.Int32(1), Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(), Label: descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum()}, {Name: proto.String("amount"), Number: proto.Int32(2), Type: descriptorpb.FieldDescriptorProto_TYPE_DOUBLE.Enum(), Label: descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum()}, }, }, }, } fd, err := protodesc.NewFile(fdp, nil) if err != nil { panic(err) } msgDesc := fd.Messages().Get(0) // "Order" descriptor gen := jsonschema.NewGenerator(jsonschema.WithJSONNames()) if err := gen.Add(msgDesc); err != nil { fmt.Printf("error: %v\n", err) return } schemas := gen.Generate() fmt.Printf("generated %d schema(s)\n", len(schemas)) // generated 1 schema(s) } ``` -------------------------------- ### Proto Field Annotations for JSON Schema Exclusion Source: https://context7.com/bufbuild/protoschema-plugins/llms.txt Demonstrates how to use '// jsonschema:ignore' and '// jsonschema:hide' comments in proto files to control field inclusion in generated JSON Schemas. ```proto syntax = "proto3"; package example.v1; message Config { // The public API key. string api_key = 1; // jsonschema:ignore — excluded entirely from the generated schema. string internal_debug_token = 2; // jsonschema:hide — moved to patternProperties; valid but not suggested by IDEs. string deprecated_alias = 3; } ``` -------------------------------- ### normalize.NewNormalizer / (*Normalizer).Normalize Source: https://context7.com/bufbuild/protoschema-plugins/llms.txt `Normalize` resolves a top-level `protoreflect.MessageDescriptor` into a single `*descriptorpb.DescriptorProto` with all cross-file references inlined as nested types, names mangled to avoid collisions, and custom options/extensions stripped. This is used internally by the PubSub generator but is also available for custom schema generation pipelines. ```APIDOC ## `normalize.NewNormalizer` / `(*Normalizer).Normalize` — Inline All Transitive Dependencies ### Description Resolves a top-level `protoreflect.MessageDescriptor` into a single `*descriptorpb.DescriptorProto` with all cross-file references inlined as nested types, names mangled to avoid collisions, and custom options/extensions stripped. Open proto3 enums are converted to `int32` fields. ### Constructor `NewNormalizer(options ...normalize.Option) *Normalizer` ### Method `(*Normalizer).Normalize(msgDesc protoreflect.MessageDescriptor) (*descriptorpb.DescriptorProto, error)` ### Parameters - **msgDesc** (`protoreflect.MessageDescriptor`) - Required - The top-level message descriptor to normalize. - **options** (`normalize.Option`) - Optional - Options to configure the normalizer, such as `WithSkipTypes`. ### Returns - `*descriptorpb.DescriptorProto` - The normalized descriptor proto. - `error` - An error if normalization fails. ### Example ```go import ( "fmt" "github.com/bufbuild/protoschema-plugins/internal/protoschema/normalize" "google.golang.org/protobuf/proto" ) func main() { n := normalize.NewNormalizer() rootPb, err := n.Normalize(msgDesc) // msgDesc: protoreflect.MessageDescriptor if err != nil { panic(err) } fmt.Printf("normalized message: %s, nested types: %d\n", rootPb.GetName(), len(rootPb.GetNestedType())) nSkip := normalize.NewNormalizer( normalize.WithSkipTypes( "google.protobuf.Timestamp", "google.protobuf.Duration", ), ) rootPb2, err := nSkip.Normalize(msgDesc) if err != nil { panic(err) } refDesc := msgDesc.Fields().ByName("location").Message() located, err := n.FindDescriptorProto(refDesc) if err != nil { panic(err) } fmt.Printf("found nested proto: %s\n", located.GetName()) _ = rootPb2 _ = proto.Size(rootPb) } ``` ``` -------------------------------- ### Product Schema Definition Source: https://github.com/bufbuild/protoschema-plugins/blob/main/README.md Defines a strict JSON schema for a product, including its ID, name, price, tags, and location. It specifies required fields and disallows additional properties. ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "$id": "buf.protoschema.test.v1.Product.jsonschema.strict.bundle.json", "$ref": "#/$defs/buf.protoschema.test.v1.Product.jsonschema.strict.json", "$defs": { "buf.protoschema.test.v1.Product.jsonschema.strict.json": { "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "A product.", "description": "A product is a good or service that is offered for sale.", "type": "object", "properties": { "productId": { "description": "The unique identifier for the product.", "maximum": 2147483647, "minimum": -2147483648, "type": "integer" }, "productName": { "description": "The name of the product.", "type": "string" }, "price": { "description": "The price of the product.", "maximum": 3.4028234663852886e38, "minimum": 0, "type": "number" }, "tags": { "description": "The tags associated with the product.", "items": { "type": "string" }, "type": "array" }, "location": { "$ref": "#/$defs/buf.protoschema.test.v1.Product.Location.jsonschema.strict.json", "description": "The location of the product." } }, "required": ["productId", "productName", "price", "location"], "additionalProperties": false }, "buf.protoschema.test.v1.Product.Location.jsonschema.strict.json": { "$schema": "https://json-schema.org/draft/2020-12/schema", "additionalProperties": true, "description": "A point on the earth's surface.", "properties": { "lat": { "maximum": 90, "minimum": -90, "type": "number" }, "long": { "maximum": 180, "minimum": -180, "type": "number" } }, "required": ["lat", "long"], "title": "Location", "type": "object" } } } ``` -------------------------------- ### `jsonschema.NewGenerator` — Create a JSON Schema Generator Source: https://context7.com/bufbuild/protoschema-plugins/llms.txt Constructs a `Generator` that accumulates message descriptors and produces JSON Schema maps. Options control naming, strictness, bundling, and additional-properties behavior. ```APIDOC ## `jsonschema.NewGenerator` — Create a JSON Schema Generator `NewGenerator` constructs a `Generator` that accumulates message descriptors and produces JSON Schema maps. Options control naming, strictness, bundling, and additional-properties behavior. ### Options - `jsonschema.WithJSONNames()`: Use JSON camelCase field names. - `jsonschema.WithStrict()`: Enable strict mode. - `jsonschema.WithBundle()`: Bundle schemas into a single file per message. - `jsonschema.WithAdditionalProperties()`: Allow extra fields in the schema. ### Example Usage ```go package main import ( "encoding/json" "fmt" "github.com/bufbuild/protoschema-plugins/internal/protoschema/jsonschema" examplev1 "github.com/bufbuild/protoschema-plugins/internal/gen/proto/buf/protoschema/test/v1" "google.golang.org/protobuf/reflect/protoreflect" ) func main() { // Proto field names, lenient (default): allows string-encoded numbers, aliases gen := jsonschema.NewGenerator() // JSON camelCase field names, strict, bundled into a single file per message strictBundledGen := jsonschema.NewGenerator( jsonschema.WithJSONNames(), jsonschema.WithStrict(), jsonschema.WithBundle(), ) // Allow extra fields (useful when receiver has an older schema version) lenientGen := jsonschema.NewGenerator( jsonschema.WithAdditionalProperties(), ) // Add a message descriptor to each generator msgDesc := examplev1.File_buf_protoschema_test_v1_examples_proto. Messages().ByName("Product") for _, g := range []*jsonschema.Generator{gen, strictBundledGen, lenientGen} { if err := g.Add(msgDesc); err != nil { panic(err) } schemas := g.Generate() // map[protoreflect.FullName]map[string]any for name, schema := range schemas { data, _ := json.MarshalIndent(schema, "", " ") fmt.Printf("=== %s ===\n%s\n", name, data) } } } ``` ``` -------------------------------- ### `(*Generator).Add` — Register a Message Descriptor Source: https://context7.com/bufbuild/protoschema-plugins/llms.txt Registers a `protoreflect.MessageDescriptor` for schema generation. It recursively processes all referenced message types, resolving `buf.validate` constraints into JSON Schema keywords. Call `Add` once per top-level message before calling `Generate`. ```APIDOC ## `(*Generator).Add` — Register a Message Descriptor `Add` registers a `protoreflect.MessageDescriptor` for schema generation. It recursively processes all referenced message types, resolving `buf.validate` constraints into JSON Schema keywords. Call `Add` once per top-level message before calling `Generate`. ### Parameters - `msgDesc` (`protoreflect.MessageDescriptor`): The message descriptor to register. ### Returns - `error`: An error if the descriptor cannot be added or processed. ### Example Usage ```go package main import ( "fmt" "github.com/bufbuild/protoschema-plugins/internal/protoschema/jsonschema" "google.golang.org/protobuf/reflect/protoreflect" "google.golang.org/protobuf/types/dynamicpb" "google.golang.org/protobuf/types/descriptorpb" "google.golang.org/protobuf/reflect/protodesc" "google.golang.org/protobuf/proto" ) func main() { // Build a minimal FileDescriptorProto programmatically fdp := &descriptorpb.FileDescriptorProto{ Name: proto.String("example.proto"), Syntax: proto.String("proto3"), MessageType: []*descriptorpb.DescriptorProto{ { Name: proto.String("Order"), Field: []*descriptorpb.FieldDescriptorProto{ {Name: proto.String("order_id"), Number: proto.Int32(1), Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(), Label: descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum()}, {Name: proto.String("amount"), Number: proto.Int32(2), Type: descriptorpb.FieldDescriptorProto_TYPE_DOUBLE.Enum(), Label: descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum()}, }, }, }, } fd, err := protodesc.NewFile(fdp, nil) if err != nil { panic(err) } msgDesc := fd.Messages().Get(0) // "Order" descriptor gen := jsonschema.NewGenerator(jsonschema.WithJSONNames()) if err := gen.Add(msgDesc); err != nil { fmt.Printf("error: %v\n", err) return } schemas := gen.Generate() fmt.Printf("generated %d schema(s)\n", len(schemas)) // generated 1 schema(s) } ``` ``` -------------------------------- ### JSON Schema Number and String Types Source: https://github.com/bufbuild/protoschema-plugins/blob/main/README.md Defines a field that can be either a number or a string, with specific constraints for each type. Useful for fields that may accept numeric input or string representations of numbers. ```json { "type": "number" }, { "pattern": "^-?[0-9]+(\.[0-9]+)?([eE][+-]?[0-9]+)?$", "type": "string" } ], "default": 0 }, "long": { "anyOf": [ { "maximum": 180, "minimum": -180, "type": "number" }, { "pattern": "^-?[0-9]+(\.[0-9]+)?([eE][+-]?[0-9]+)?$", "type": "string" } ], "default": 0 } } } ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.