jsonschema: JSON Schema generated from Go types
Generate JSON Schemas from Go types
At a glance
- What is it?
- invopop/jsonschema turns a Go struct into a JSON Schema by reflection, so a type that already describes your API does not have to be described a second time in a schema file. The package is a fork of an earlier library, it has moved to the current JSON Schema draft, and it is deliberately still on a zero major version, which means you should pin it.
- Who is it for?
- jsonschema suits a Go service that wants an accurate schema for its own types without maintaining a parallel description by hand, and the reflection approach means a field added to the struct appears in the schema automatically. It is a poor fit if you need the schema to be stable across releases, because the version is still zero and the readme says breaking changes are likely, and it is a poor fit if you serve YAML, since the YAML tag support was dropped on purpose.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Activity is slowing. The repository last received commits 6 months ago.
- What is it written in?
- Mainly Go, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
Reflection, and the four things it can express
The package generates JSON Schemas from Go types through reflection, and the feature list is a good statement of scope.
It supports arbitrarily complex types, including empty interfaces, maps and slices. It supports JSON Schema features such as minimum and maximum length, pattern, and format. It supports simple string and numeric enums. And it supports custom property fields through a struct tag, which is the escape hatch for anything the reflection rules do not cover.
The core idea is the one every schema generator in a typed language has, but it is worth being concrete about why it is useful here. Your handler functions are already described by their Go types. A request body struct says which fields exist, which are required by virtue of not having an omit marker, and what their types are. A schema generator that reads those types produces a description that cannot drift from the code, and when a field is renamed, the schema changes with it.
The custom property tag matters more than it first appears, because it is how you attach the information a Go type cannot express. A title for documentation tooling, a description, a default, an example, a format. The example in the readme shows a struct where almost every field carries an extra tag, and the resulting schema is a fully documented object with examples and a default value.
The generated schema also carries an identifier, a reference to the definition, and the set of required properties, and it marks the object as not allowing additional properties. Those are the parts of a schema that consumers such as form generators rely on, and they are the parts a hand-written schema usually gets wrong.
A fork, and the four differences that matter
This repository is a fork of the original library by a different author, and the readme is explicit about the differences, which is unusual and useful.
The fork exists because the Invopop team uses the library as a cornerstone in another library of theirs, and they wanted to keep building it without taking up the original author's time. The readme warns that the significant changes probably mean this version is not compatible with the original.
There are four changes. The original was stuck on the draft-04 version of JSON Schema, and this one has moved to the latest draft, the 2020-12 one. Schema identifiers are added automatically from the current Go package's URL so that they are unique, and that can be disabled with the Anonymous option. Support for the fully qualify type name option has been removed, and the readme gives three alternatives if you hit conflicts: use multiple schema files with different identifiers, set the DoNotReference option to true to hide definitions completely, or add your own naming strategy using the Namer property. And support for YAML tags and related options has been dropped for the sake of simplification, after some inconsistencies around it were fixed.
The removal of YAML support is the one with a follow-up section in the readme. The recommended approach for YAML data is to convert it to JSON first, using a companion library the author also maintains. The readme links a discussion where people can make the case for the other answer.
The version policy is the other thing to read. The project is still under the zero major version, as per Go convention, breaking changes are likely, and you are asked to pin your module to version tags or branches. The Go version requirement is 1.24 or newer, and the stated aim is to support one version below the last supported Go version.
The struct tag vocabulary, in practice
The example in the readme is the best documentation of the tag vocabulary, and it is worth reading the tags and the generated output side by side.
A plain field with a JSON tag becomes a typed property, and the required list is derived from whether the field can be omitted. A field with the omit marker and a description tag becomes an optional property with a description. A field with an extras tag adds arbitrary custom properties to the schema, and the example shows the same key repeated, which becomes an array in the output, so the tag syntax is a simple comma separated list where repeating a key collects values.
The more interesting tags are the ones that change the shape of the property rather than decorating it. The oneof_required tag makes the field one of several alternatives, and the example uses it to express a date of birth that may be given either as a full date or as a year, with the generated schema containing a oneOf with two branches, each requiring a different field. The oneof_type tag does the same for the type itself, so a field that may be a string or an array produces a oneOf on types rather than on required properties.
An enum tag turns a string field into an enumeration, and the generated schema carries the list of allowed values. The example also shows the modern omitzero marker, and the comment notes that it requires Go 1.24 or later, which is a nice illustration of the package tracking the language.
The reflect call itself is one line.
jsonschema.Reflect(&TestUser{})which is the entire API for the common case, with the reflector type existing for the cases that need options.
The reflector options, and the semantics of required
The configurable behaviour section explains that the generator's behaviour can be altered with parameters when a reflector instance is created, and the options are where the interesting decisions live.
The expanded struct option makes the top level struct not reference itself in the definitions, with the caveat that the type passed should be a struct type. That matters for a schema you intend to publish, because a top level reference to a definition is fine internally and awkward for consumers who expect the object inline.
The other options are the ones named in the differences list. Anonymous suppresses the automatically generated identifier. DoNotReference hides the definitions entirely. And Namer is the extension point for the naming strategy, which is how you would solve the type name conflicts that the removed option used to handle.
The second worked example in the readme is about required, and it is a good illustration of a subtle rule. The struct in question has private fields, fields ignored by the JSON tag, fields ignored by the schema tag, and untagged fields, and several of them are tagged as required. The comment explains that the required tag is nonsensical for private and ignored properties, and that the example exists to test that those fields will not be required in the output schema even if they are tagged that way.
type GrandfatherType struct {The generated output shows the required list containing only the fields that are actually part of the JSON representation, including the untagged exported one, and excluding the private, the ignored and the schema-ignored ones. That is a careful set of rules, and the readme tests them rather than asserting them.
The repository, and what the tooling says about it
The top level of the repository shows a small, focused package with a testing setup worth noting.
There is a reflect file and a reflect comments file, with a matching test file for each, an identifier file with its own test, a schema file and a utilities file, and an examples file. The interesting one is the fuzz test, which is a sign that a reflection based generator is tested against arbitrary input rather than only against hand written cases, which is the right way to test something that walks arbitrary type structures.
There is a fixtures directory, an examples directory with a user example and a nested example, and the build files. The dependency list is short, with an ordered map library for building the JSON output deterministically and a test assertion library, and a small set of indirect dependencies. There is no code generation step, which keeps the footprint of the package small.
The quality tooling is visible in the badges. There is a lint workflow, a test workflow, a report card badge, a documentation badge and a coverage badge, and there is a lint configuration file at the root, which means the style rules are enforced in continuous integration rather than left to review.
So the shape of the project is: one small dependency, a core of reflection code, a fuzz test, and a set of examples that double as documentation. For a package whose whole job is to read Go types correctly, the fuzz test is the part that would make me trust it with a large union type.
Editorial conclusion
jsonschema suits a Go service that wants an accurate schema for its own types without maintaining a parallel description by hand, and the reflection approach means a field added to the struct appears in the schema automatically. It is a poor fit if you need the schema to be stable across releases, because the version is still zero and the readme says breaking changes are likely, and it is a poor fit if you serve YAML, since the YAML tag support was dropped on purpose. Before you adopt it, pin the version you test against, and if you are migrating from the original library, read the list of breaking differences rather than assuming compatibility.
Frequently asked questions
What does invopop/jsonschema do?
It generates JSON Schemas from Go types through reflection, so a struct that already describes your API does not have to be described a second time in a schema file. It supports complex types including empty interfaces, maps and slices, JSON Schema constraints such as length, pattern and format, simple enums, and custom properties through a struct tag.
Is invopop/jsonschema compatible with the original jsonschema library?
Probably not. It is a fork of the original by another author and the readme lists the significant changes, including moving from draft-04 to the 2020-12 draft, adding automatic schema identifiers, removing the fully qualify type name option in favour of multiple files, the DoNotReference option or a Namer property, and dropping YAML tag support.
How do I express that a field can be one of two alternatives in invopop/jsonschema?
With the oneof_required tag, which makes the field one of several alternatives and produces a oneOf in the generated schema, and with the oneof_type tag, which does the same for the property's type. The readme shows a date of birth that may be given either as a full date or as a year, and a field that may be a string or an array.
Can I use invopop/jsonschema with YAML tags?
No. Support for YAML tags and the related options was dropped for the sake of simplification, and a discussion is open about it. The recommended approach is to convert the YAML data to JSON first, using a companion library the author maintains.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/invopop-jsonschema)Was this useful?