Decorative site banner
Project icon

com.io7m.sumjack

  • About
  • Releases
  • Manual
  • Sources
  • License
  • Issues
Maven Central Version Maven Snapshot Code Coverage

sumjack


A JSON schema generator for Jackson annotated Java types.

Usage


Building


$ mvn clean package

Overview


The sumjack package is intended to produce JSON schema documents from hierarchies of Jackson annotated Java types. The package provides a generator that walks a tree of Java classes, inspects the @JsonProperty annotations on the class methods, and then calls registered definitions to generate fragments of JSON schemas that are eventually combined into a single schema document.

Five Second Example


final var configuration = SjGeneratorConfiguration.builder() .setId(URI.create("urn:com.io7m.example:1.0")) .setMapper(JsonMapper.shared()) .setRootType(Example.class) .setSchemaVersion(SjSchemaVersion.DRAFT_2020_12) .setTitle("Example 1.0") .build(); final var generator = SjGenerators.create(configuration); generator.executeAndWrite(outputFile);

Classes And Annotations


The sumjack package is opinionated about the kinds of classes for which it will generate schemas. Specifically, the package expects to be generating schemas for hierarchies of classes that are expressed in an algebraic sums and products style. That is, the classes for which schemas are to be generated must be one of:

  • Sealed interfaces (representing sum types)
  • Records (representing product types)
  • Primitives (int, long, etc.)
  • Collections (java.util.Map, java.util.Set, java.util.List, java.util.SortedMap, java.util.Optional, etc.)
  • Enums

The package is capable of producing schemas for any classes that fall into the above categories without any kind of extra configuration or work needed on the part of the user of the package. However, if a class does not fit into one of these categories, then it is necessary to register a custom definition in order to generate a schema for it.

Definition


A definition is a Java function that, when evaluated, produces a schema object. For example, a definition that produces a schema for the java.util.UUID type might look like this:

SjGeneratorConfiguration configuration; () -> { final var mapper = configuration.mapper(); final var object = mapper.createObjectNode(); object.put("description", "An RFC 9562 UUID string."); object.put("type", "string"); object.put("format", "uuid"); return object; };

The sumjack package includes definitions for many of the standard Java classes, although these must be enabled manually by adding them to the SjGeneratorConfiguration value used to configure the generator:

final var configuration = SjGeneratorConfiguration.builder() .addDefinitions(SjUUID.UUID) .addDefinitions(SjBigInteger.BIG_INTEGER) .addDefinitions(SjOffsetDateTime.OFFSET_DATE_TIME) .setId(URI.create("urn:com.io7m.example:1.0")) .setMapper(JsonMapper.shared()) .setRootType(Example.class) .setSchemaVersion(SjSchemaVersion.DRAFT_2020_12) .setTitle("Example 1.0") .build();

Generation


Sealed Interfaces


A sealed interface is expected to represent a sum type and therefore compiles down to a schema that matches oneOf a set of types. For example:

sealed interface SimpleBase0Type permits SimpleBaseA, SimpleBaseB, SimpleBaseC { }

Produces a schema:

"SimpleBase0Type": { "oneOf": [ { "$ref": "#/$defs/SimpleBaseA" }, { "$ref": "#/$defs/SimpleBaseB" }, { "$ref": "#/$defs/SimpleBaseC" } ] },

Records


A record is expected to represent a product type. The produced schema for a record type is simply the @JsonProperty annotated constructor parameters. For example:

public record Vector3( @JsonProperty(value = "X", required = true) double x, @JsonProperty(value = "Y", required = true) double y, @JsonProperty(value = "Z", required = true) double z) { }

Produces a schema:

"Vector3": { "type": "object", "properties": { "X": { "$ref": "#/$defs/double" }, "Y": { "$ref": "#/$defs/double" }, "Z": { "$ref": "#/$defs/double" } }, "required": [ "X", "Y", "Z" ] }, "double": { "type": "number", "minimum": 2.2250738585072014E-308, "maximum": 1.7976931348623157E308, "description": "An IEEE764 64-bit floating point value." }

Enums


Enums are translated to enumeration strings.

public enum TrafficLight { RED, GREEN, YELLOW }

Produces a schema:

"TrafficLight": { "type": "string", "enum": [ "RED", "GREEN", "YELLOW" ] }

Primitives


The Java primitives are, if the definitions in the SjPrimitives class are registered, transformed to numeric and boolean types:

"boolean": { "type": "boolean", "description": "A boolean value." }, "byte": { "type": "number", "description": "A primitive byte.", "minimum": -128, "maximum": 127 }, "char": { "type": "number", "description": "A primitive char.", "minimum": 0, "maximum": 65535 }, "double": { "type": "number", "minimum": 2.2250738585072014E-308, "maximum": 1.7976931348623157E308, "description": "An IEEE764 64-bit floating point value." }, "float": { "type": "number", "minimum": 1.1754943508222875E-38, "maximum": 3.4028234663852886E38, "description": "An IEEE764 32-bit floating point value." }, "int": { "type": "number", "description": "A primitive int.", "minimum": -2147483648, "maximum": 2147483647 }, "long": { "type": "number", "description": "A primitive long.", "minimum": -9223372036854775808, "maximum": 9223372036854775807 }, "short": { "type": "number", "description": "A primitive short.", "minimum": -32768, "maximum": 32767 }

Collections


Collection types are transformed to schema objects that match their types as closely as possible. A List<SimpleBaseA> type, for example, becomes:

"List<SimpleBaseA>": { "type": "array", "items": { "$ref": "#/$defs/SimpleBaseA" } }, "SimpleBaseA": { "type": "object", "properties": {}, "required": [] },

java.util.Optional values are effectively erased and become non-required properties of the containing object:

public record SimpleContainsOptional( @JsonProperty("Optional") @JsonPropertyDescription("An element that might not be there.") Optional<SimpleBaseA> elements) { }

Becomes:

"Optional<SimpleBaseA>": { "$ref": "#/$defs/SimpleBaseA" }, "SimpleBaseA": { "type": "object", "properties": {}, "required": [] }, "SimpleContainsOptional": { "type": "object", "properties": { "Optional": { "description": "An element that might not be there.", "$ref": "#/$defs/Optional<SimpleBaseA>" } }, "required": [] },

Releases & Development Snapshots


Releases


You can subscribe to the atom feed to be notified of project releases.

The most recently released version of the package is 2.0.0.

2.0.0 Release (2025-12-13Z)

  • Respect @JsonTypeInfo to allow specifying a type attribute.
  • Ensure primitive integers are really integers. (Backwards incompatible)
  • Disallow additional properties on record types. (Backwards incompatible)
  • Correct bounds on float and double values.

The compiled artifacts for the release (and all previous releases) are available on Maven Central.

Maven Modules


<dependency> <group>com.io7m.sumjack</group> <artifactId>com.io7m.sumjack.bom</artifactId> <version>2.0.0</version> </dependency><dependency> <group>com.io7m.sumjack</group> <artifactId>com.io7m.sumjack.core</artifactId> <version>2.0.0</version> </dependency><dependency> <group>com.io7m.sumjack</group> <artifactId>com.io7m.sumjack.lanark</artifactId> <version>2.0.0</version> </dependency><dependency> <group>com.io7m.sumjack</group> <artifactId>com.io7m.sumjack.tests</artifactId> <version>2.0.0</version> </dependency>

Previous Releases


The changelogs for the most recent previous releases are as follows:

1.0.0 Release (2025-12-11Z)

  • Initial version.

Development Snapshots


At the time of writing, the current unstable development version of the package is 2.1.0-SNAPSHOT.

Development snapshots may be available in the Central Portal Snapshots repository. Snapshots are published to this repository every time the project is built by the project's continuous integration system, but snapshots do expire after around ninety days and so may or may not be available depending on when a build of the package was last triggered.

Manual


This project does not have any user manuals or other documentation beyond what might be present on the page above.

Sources


This project uses Git to manage source code.

Repository: https://www.github.com/io7m-com/sumjack

$ git clone --recursive https://www.github.com/io7m-com/sumjack

Issues


This project uses GitHub Issues to track issues.

License


Copyright © 2025 Mark Raynsford <code@io7m.com> https://www.io7m.com Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

Last Updated 2025-12-13T21:28:22Z