Open-source project
omissis/go-jsonschema avatar
omissis/go-jsonschema

go-jsonschema generates Go types and validating unmarshalling code

A tool to generate Go data types from JSON Schema definitions.

810 stars135 forksGoMIT

At a glance

What is it?
go-jsonschema is a Go code generator that reads JSON Schema definitions and produces matching structs together with unmarshalling code that applies the schema's validation rules. Its README ships an honest coverage matrix, including keywords such as const, allOf and oneOf that are not implemented.
Who is it for?
Go teams with a JSON Schema they must enforce at runtime should adopt go-jsonschema, because it produces the structs and the validating unmarshaller together and keeps the two in step as the schema changes.
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?
Yes. The repository last received commits 2 days 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 22, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Types and validation from one schema, generated together

go-jsonschema is a tool to generate Go data types from JSON Schema definitions. The README states the scope in one line, and it is broader than most generators of this kind.

The tool generates Go data types and structs that correspond to definitions in the schema, along with unmarshalling code that validates the input JSON according to the schema's validation rules.

That second half is the reason to choose one generator over another. A schema-to-struct tool gives you the shape of the data. This one also gives you the checks, in the same generated file, so that decoding a payload that violates the schema produces an error at the decode site rather than a bad value three layers deeper in your service.

The audience is Go developers who already have a JSON Schema as the source of truth, usually because the schema is shared with clients, published as an API contract, or consumed by a code generator on another platform. The tool is aimed at keeping Go in step with that contract rather than at being a schema validator in its own right.

The repository is a conventional Go project with a Makefile, a Dockerfile, a mise.toml for toolchain versions, a docs directory, an internal and a pkg directory, a tests directory and a scripts directory. The dependency list in go.mod is short and shows the shape of the tool: cobra for the command line, goccy/go-yaml for YAML input, testify and go-cmp for tests, litter for debugging output, and mergo for merging options.

It is MIT licensed and published under the omissis organisation, with releases cut frequently enough that pinning matters.

Three ways to install it, and one naming trap

The installation section offers a release download, a source install and a Homebrew tap.

The source install requires Go 1.25 or later:

shell
go get github.com/atombender/go-jsonschema/...
go install github.com/atombender/go-jsonschema@latest

Homebrew is the tidier route:

shell
brew tap omissis/go-jsonschema
brew install go-jsonschema

The download link in the README also points at the atombender releases page rather than the repository you are reading.

That inconsistency is the naming trap worth knowing about before you spend an afternoon on it. The repository is omissis/go-jsonschema, the module path declared in go.mod is github.com/atombender/go-jsonschema, the Homebrew tap is omissis/go-jsonschema, and the release links in the README point at the atombender project. The tool has been maintained under a new organisation while the Go module path kept the old one, which is a deliberate choice rather than an accident, since changing a module path breaks every dependent's go.mod.

The practical consequences are two. If you depend on the library rather than the command, your import path will carry the old name. And if you pin a version, look at the tags in the repository you are actually reading rather than assuming the release page and the repo are the same place.

The go.mod also pins the toolchain explicitly, requiring Go 1.25.0 with toolchain go1.25.12, so an older Go on your machine will refuse to build it.

Generating one schema, or several with different import paths

The basic invocation writes a Go source file to standard output under the package name you give it:

shell
go-jsonschema -p main schema.json

Writing to standard output rather than to a file is the right default for a generator, because it composes with shell redirection and with build tooling.

For several schemas in one run, with each output landing in a different file and package, the flags take a compound argument:

shell
$ go-jsonschema \
  --schema-package=https://example.com/schema1=github.com/myuser/myproject \
   --schema-output=https://example.com/schema1=schema1.go \
  --schema-package=https://example.com/schema2=github.com/myuser/myproject/stuff \
   --schema-output=https://example.com/schema2=stuff/schema2.go \
  schema1.json schema2.json

The left side of the equals sign is the schema $id and the right side is the full import URL. The README marks that with an annotated sketch of the flag format, and the mapping matters because the tool uses the $id to decide which schema a cross-reference refers to.

The result is schema1.go declared as package myproject and stuff/schema2.go declared as package stuff. And if one schema refers to the other, the two generated files import each other's package, which is how a schema set with cross references turns into Go packages that compile together.

That last point is the feature that makes multi-schema generation worth the flags. A schema graph becomes a package graph, with the import paths you choose, and the tool does the dependency arithmetic for you.

The coverage matrix is the real specification

The README contains a validation checklist built from the JSON Schema RFC drafts, and it is the most valuable section of the documentation for anyone deciding to use this tool.

The headline is honest: while not finished, go-jsonschema can be used today, and aside from some minor features only specific validations remain to be fully implemented.

What works includes the core data model for null, boolean, object, array, number and string, and references against top-level names both inside a document and across external files, plus comments. Numeric validation is complete with multipleOf, maximum, exclusiveMaximum, minimum and exclusiveMinimum. String validation is complete with maxLength, minLength and pattern. In the validation section, enum and single type are supported, and multiple types are supported with an explicit note that it is partial support with limited validation. Annotation keywords description and default are done, the latter only for struct fields.

What does not work is the interesting part. Location identifiers for nested names are not implemented, so only top-level $defs names resolve. const is not implemented. Array validation covers items, maxItems and minItems but not uniqueItems, additionalItems or contains. Object validation covers required and properties but not patternProperties, dependencies, propertyNames, maxProperties or minProperties. Conditional subschemas, that is if, then and else, are absent. Boolean subschemas, allOf, anyOf, oneOf and not, are absent. Format validation covers dates and times only, not email addresses, hostnames, IP addresses, resource identifiers, URI templates, JSON pointers or regex.

That last list decides adoption. A schema that leans on allOf or oneOf, which most real world schemas do for composition, will generate code that does not enforce those keywords at all.

Date and time get their own generated types

One detail in the README is a good example of a generator paying attention to the target language rather than the source format.

In a few cases special types are used to help with serializing and deserializing data from JSON. A custom type is provided for the semantic types SerializableDate and SerializableTime, and the reason given is that there is no native type provided by Go which properly handles them.

That is the right call. JSON Schema has a date format and a time format that are not merely strings with a pattern attached; they are values with their own semantics, and mapping both onto time.Time makes the generated code lossy in the way that matters, because a date with no time zone and a timestamp with one are not the same value.

The generated types carry the marshal and unmarshal behaviour so the round trip preserves that distinction, which means a payload validated against a schema that says date will decode into a value that knows it is a date.

The README does not describe how the format is chosen beyond the list of semantic types, so whether a schema property is emitted as SerializableDate depends on the schema marking it with the corresponding format. If your schema uses the date-time format rather than date, that is a separate question the documentation does not answer, and one worth checking against a generated file before you rely on it.

The generated code is tested, with golden files you can regenerate

The contributing section explains an unusual and deliberate choice: this project makes use of go workspaces in order to ease testing of the generated code during development while keeping the codebase as tidy and maintainable possible. The README calls it an unusual choice and explains the benefit, which is that it allows not only testing the code-generation logic but also the generated code itself.

That is the part worth pausing on. Most generators test that the output matches a golden file. This one also compiles and runs what it generates, which is the only way to catch the class of bug where the output looks right in a diff and does not build.

When a new feature or bug fix requires regenerating the tests' golden files, the README gives the procedure:

shell
export OVERWRITE_EXPECTED_GO_FILE="true"
make test

The environment variable flips the golden files from asserted to written, and the Makefile runs the suite. That is the standard pattern, and having it documented in the README rather than in a contributor wiki tells you something about how the project expects to be worked on.

The Makefile itself shows the level of tooling around the project. Linting runs in containers, one image per tool, with golangci-lint, hadolint, file-lint, jsonlint, checkmake, markdownlint, shellcheck, shfmt and yamllint, and the Dockerfile builds the tool with a scratch final stage so the shipped image contains the binary and nothing else.

Where go-jsonschema is the wrong tool

The limitations follow from two facts: this is a code generator with a partial implementation of the specification, and it is a command line tool rather than a library with a validation API.

The first limitation is the keyword coverage. If your schema uses allOf, anyOf, oneOf, not, if, then, else, const or the pattern-matching object keywords, the generated code will not enforce them, and it will not warn you that it is not enforcing them either. A schema that is your contract with other teams should therefore be checked against the coverage matrix before you trust the generated validator, and ideally the schema should be restricted to the keywords the tool supports.

The second is that validation happens at unmarshal time, on the values your code chooses to decode. A payload that is never decoded into the generated type is never validated by it, so the tool is not a general request validator and it does not enforce a schema against arbitrary JSON documents.

The third is the toolchain floor. Go 1.25 with a pinned toolchain, plus a binary that writes to standard output, means the generator belongs in a build step. Regenerating types inside a hot reload or a watch loop is fine, but the tool is not designed to be embedded in an editor plugin that has to feel instant.

The fourth is the naming situation described earlier. The module path, the repository, the tap and the release links do not all say the same thing, which is a small tax that anyone integrating the library rather than running the binary will pay.

On maintenance, the record is good. The repository is not archived, the last push was on 2026-10-16, and the recent releases v0.23.1, v0.24.0 and v0.24.1 between May and August 2026 show a project that is still cutting versions. The licence is MIT, so the generated code and the tool are both free to use in commercial work, and generated structs carry no licensing obligation of their own.

Editorial conclusion

Go teams with a JSON Schema they must enforce at runtime should adopt go-jsonschema, because it produces the structs and the validating unmarshaller together and keeps the two in step as the schema changes. It is the wrong tool if your schema leans on keywords the generator does not implement, since const, if, then, else, allOf, anyOf, oneOf and not are all listed as unimplemented, and it is the wrong tool if you need the generated code to be the contract, because a schema feature the tool ignores produces no error at all. Before adopting, check the coverage matrix against your schema, confirm whether you want the legacy module path, and pin a release since the project publishes frequent ones.

Frequently asked questions

How do I install go-jsonschema?

Install a release binary, install from source with Go 1.25 or later using go install github.com/atombender/go-jsonschema@latest, or use Homebrew with brew tap omissis/go-jsonschema followed by brew install go-jsonschema.

Does go-jsonschema generate validation code as well as structs?

Yes. The README says it generates Go data types and structs corresponding to the schema definitions, along with unmarshalling code that validates the input JSON according to the schema's validation rules. Coverage is partial, and the README publishes a per-keyword checklist.

Which JSON Schema keywords does go-jsonschema not support?

The README lists them as unimplemented: const, nested location identifiers, if, then and else, allOf, anyOf, oneOf and not, uniqueItems, additionalItems, contains, patternProperties, dependencies, propertyNames, maxProperties and minProperties, and format validation beyond dates and times.

How do I generate types for several schemas in one run?

Pass --schema-package and --schema-output flags per schema, where the left side of the equals sign is the schema $id and the right side is the full import URL, then list the schema files. Files that reference each other generate imports of each other's packages.

Official sources

  1. Issues
  2. License: MIT
  3. omissis/go-jsonschema on GitHub
  4. README
  5. Releases
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.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/omissis-go-jsonschema.svg)](https://hysenlabs.com/projects/omissis-go-jsonschema)

Was this useful?