AutoStruct
Generated structs for Elixir from JSON Schema.
AutoStruct is a thin code generation layer over Exonerate. Exonerate validates JSON-shaped Elixir terms generated from JSON Schema; AutoStruct generates structs, conversion helpers, and encoder implementations around that validator.
Installation
The package can be installed by adding auto_struct to your list of
dependencies in mix.exs:
def deps do
[
{:auto_struct, "~> 0.3.0"}
]
endDocumentation is available on HexDocs.
Usage
defmodule Person do
use AutoStruct.JsonSchema,
schema: """
{
"$id": "https://example.com/person.schema.json",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "Person",
"type": "object",
"properties": {
"first_name": {
"type": "string",
"description": "The person's first name."
},
"age": {
"description": "Age in years which must be equal to or greater than zero.",
"type": "integer",
"minimum": 0
},
"happy": {
"type": "boolean",
"default": true,
"description": "Whether the person is happy."
},
"attrs": {
"type": "array",
"items": {
"type": "string"
},
"description": "Optional array of attributes.",
"nullable": true
}
},
"required": ["first_name"]
}
"""
end
# `new/1` returns {:ok, value} | {:error, reason}
iex> Person.new(first_name: "George", age: 31)
{:ok, %Person{first_name: "George", age: 31, happy: true, attrs: nil}}
# `new!/1` raises on error
iex> Person.new!(first_name: "George", age: 31, attrs: %{phone: 123})
** Some Error Message from the underlying validation library
# `from_json/1` returns {:ok, value} | {:error, reason}
iex> Person.from_json(%{"first_name" => "George", "age" => 31})
{:ok, %Person{first_name: "George", age: 31, happy: true, attrs: nil}}
iex> Person.from_json(%{"first_name" => "George", "age" => 31, "attrs" => %{"phone" => 123}})
** Some Error Message from the underlying validation library
# Generated structs implement Elixir's built-in JSON.Encoder.
iex> JSON.encode!(Person.new!(first_name: "George"))
"{\"age\":null,\"attrs\":null,\"first_name\":\"George\",\"happy\":true}"
# If you would like to build the struct directly, and avoid the internal cast to json map
# and back again, you can skip validation but still enforce the keys:
iex> %Person{first_name: "Hello"}
Examples
Runnable examples are available in the examples/ directory:
mix run examples/inline_schema.exs
mix run examples/file_schema.exsMix Tasks
mix auto_struct.gen.modules
Generate the Module stubs from a folder of schema files.
mix auto_struct.gen.modules <path_to_schemas> <output_path_modules>How it works
Under the hood the use AutoStruct.JsonSchema macro uses Exonerate to generate the validation function needed
by new/1, from_json/1, and validate/1.
AutoStruct creates a defstruct with an atom key for every top-level property in the JSON Schema.
Property names are preserved as-is. For properties marked as required, AutoStruct also includes
them in @enforce_keys.
new/1 and validate/1 recursively normalize structs, maps, and lists into JSON-shaped terms before
calling Exonerate. Nested maps and arrays are validated by Exonerate, but from_json/1 only casts the
top-level object into a struct; nested objects remain JSON-shaped maps unless you transform them yourself.
Generated modules expose schema metadata:
Person.__schema__(:json)
Person.__schema__(:fields)
Person.__schema__(:required)
Elixir's built-in JSON.Encoder is the primary encoder. When Jason is available, AutoStruct also emits
a compatible Jason.Encoder implementation.
Limitations
Nested JSON Schema objects and arrays are validated by Exonerate, but AutoStruct only casts the top-level
schema object into a struct. Nested objects returned from from_json/1 remain string-keyed maps unless you
transform them yourself.
AutoStruct currently generates fields from top-level schema properties. Advanced JSON Schema composition features are delegated to Exonerate for validation and are not expanded into additional struct fields.