Skip to content

torand/jsonschema2java

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

JSONSchema2Java

CI Maven Central Javadoc Coverage Apache 2.0 License

A Maven plugin to generate Java source code (POJOs) from JSON Schema files.

Table of Contents

Overview

Include this Maven plugin in any Java project processing JSON payloads to enable a Contract First build workflow. The current version supports the JSON Schema specification version "2020-12".

The JSON Schema files are read, parsed and validated using the networknt/json-schema-validator library. For each JSON Schema file a Java class or enum definition is written to a specified output directory.

The generated source code is compatible with Java 17+ and optionally includes annotations from the following libraries:

Usage

The package is available from the Maven Central Repository.

Include in a Maven POM File

<build>
  <plugins>
    <plugin>
      <groupId>io.github.torand</groupId>
      <artifactId>jsonschema2java</artifactId>
      <version>1.1.2</version>
      <executions>
        <execution>
          <id>generate</id>
          <phase>generate-sources</phase>
          <goals>
            <goal>generate</goal>
          </goals>
        </execution>
      </executions>
      <configuration>
        <searchRootDir>.</searchRootDir>
        <searchFilePattern>*.json</searchFilePattern>
        <schemaIdRootUri>https://my-domain.com/my-api/schemas</schemaIdRootUri>
        <outputDir>target/jsonschema2java</outputDir>
        <rootPackage>io.github.torand.mymodel</rootPackage>
      </configuration>
    </plugin>
  </plugins>
</build>

Run from the Command Line

$ mvn io.github.torand:jsonschema2java:1.1.2:generate \
  -DsearchRootDir=. \
  -DsearchFilePattern=*.json \
  -DschemaIdRootUri=https://my-domain.com/my-api/schemas \
  -DoutputDir=target/jsonschema2java \
  -DrootPackage=io.github.torand.mymodel

Configuration

Parameter Default Description
searchRootDir Project root dir Root directory to search for schema files
searchFilePattern Schema file path search pattern. Supports glob patterns
schemaIdRootUri Root URI of $id property in schema files. Path elements beyond this value must correspond to subdirectories inside 'searchRootDir'.
outputDir Project build dir Directory to write POJO source code files to
rootPackage Root package path of output POJO classes and enums
pojoNameSuffix "Dto" Suffix for POJO class and enum names
pojosAsRecords true Whether to output Java records instead of Java classes
addOpenApiSchemaAnnotations false Whether to generate model files with OpenAPI schema annotations
addJsonPropertyAnnotations true Whether to generate model files with JSON property annotations
addJakartaBeanValidationAnnotations true Whether to generate model files with Jakarta Bean Validation annotations
useKotlinSyntax false Whether to generate model files with Kotlin syntax
indentWithTab false Whether to output indents with the tab character
indentSize 4 Number of spaces in one indentation level. Relevant only when 'indentWithTab' is false.
verbose false Whether to log extra details

Type Mapping

JSON schema types and formats map to the following Java and Kotlin types in generated source code:

Type Format Java type Kotlin type
"array" N/A java.util.List java.util.List
"array" with "uniqueItems" = true N/A java.util.Set java.util.Set
"boolean" N/A Boolean Boolean
"integer" Integer Int
"integer" "int32" Integer Int
"integer" "int64" Long Long
"number" java.math.BigDecimal java.math.BigDecimal
"number" "double" Double Double
"number" "float" Float Float
"object" N/A 1 1
"object" with "additionalProperties" = {...} N/A java.util.Map java.util.Map
"string" String String
"string" "uri" java.net.URI java.net.URI
"string" "uuid" java.util.UUID java.util.UUID
"string" "duration"2 java.time.Duration java.time.Duration
"string" "date"3 java.time.LocalDate java.time.LocalDate
"string" "date-time"4 java.time.LocalDateTime java.time.LocalDateTime
"string" "binary" byte[] ByteArray
"string" All other formats String String

Constraint Mapping

JSON schema restriction properties map to the following Jakarta Bean Validation annotations (when enabled):

Type Restriction Annotation
"array" @Valid
"array" Not nullable @Valid @NotNull
"array" "minItems": n @Valid @Size(min = n)
"array" "maxItems": n @Valid @Size(max = n)
"boolean" Not nullable @NotNull
"integer" Not nullable @NotNull
"integer" "minimum": n @Min(n)
"integer" "maximum": n @Max(n)
"number" Not nullable @NotNull
"number" "minimum": n5 @Min(n)
"number" "maximum": n5 @Max(n)
"object" @Valid
"object" Not nullable @Valid @NotNull
"string" Not nullable @NotBlank
"string" Not nullable and "format": "binary" @NotEmpty
"string" "pattern": "expr" @Pattern(regexp = "expr")
"string" "minLength": n @Size(min = n)
"string" "maxLength": n @Size(max = n)
"string" "format": "email" @Email

Guidelines

General

Relaxed, abstract schemas are useful for validation, not so much for code generation. As a general rule, to produce meaningful POJOs, strict schemas are necessary. Hence, the "type" property is mandatory.

Ids and Refs

Always use absolute URIs for "$id" and "$ref" properties, e.g. "https://my-domain.com/my-api/schemas/my-entity". Relative URIs are discouraged by the official JSON Schema documentation, and therefore not supported by this project.

Customizing Code Generation

The code generation can be customized per JSON Schema by using the following extension properties in the schema definition:

Extension property Type Allowed where Description
x-json-serializer String In a property schema Fully qualified classname of a JSON serializer class for the property
x-validation-constraint String In a property schema Fully qualified classname of an annotation class to validate the property
x-nullable Boolean In a property schema If true the type of the property can be null
x-model-subdir String In an enum or object schema Subdirectory to place the generated DTO model classes
x-deprecation-message String Everywhere deprecated can be used Describing why something is deprecated, and what to use instead

Nullability

Mandatory properties are (optionally) decorated with @NonNull and similar Jakarta Bean Validation annotations during code generation. For a JSON Schema property to be considered mandatory, i.e. present and with a non-null value, it must be mentioned in the "required" list AND NOT have a "nullable" indicator.

The standard way to represent mandatory properties is as follows:

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://my-domain.com/my-api/schemas/person",
  "type": "object",
  "properties": {
    "name": {
      "type": "string"
    },
    "address": {
      "$ref": "https://my-domain.com/my-api/schemas/address"
    }
  },
  "required": [ "name", "address" ]
}

Correspondingly, the standard way to represent non-mandatory (nullable) properties is like this:

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://my-domain.com/my-api/schemas/person",
  "type": "object",
  "properties": {
    "name": {
      "type": ["string", "null"]
    },
    "address": {
      "oneOf": [
        {
          "$ref": "https://my-domain.com/my-api/schemas/address"
        },
        {
          "type": "null"
        }
      ]
    }
  },
  "required": []
}

Note the use of "OneOf" to express a nullable object reference.

For convenience, a non-standard schema extension is available to express nullability uniformly regardless of property type:

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://my-domain.com/my-api/schemas/person",
  "type": "object",
  "properties": {
    "name": {
      "type": "string",
      "x-nullable": true
    },
    "address": {
      "$ref": "https://my-domain.com/my-api/schemas/address",
      "x-nullable": true
    }
  },
  "required": []
}

Inheritance

Inheritance is not supported, nor has JSON Schema such a construct. Inheritance can be "simulated" with composition using "allOf" on the root schema:

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://my-domain.com/my-api/schemas/vehicle",
  "type": "object",
  "properties": {
    "brand": {
      "type": "string"
    }
  },
  "required": [ "brand" ]
}
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://my-domain.com/my-api/schemas/car",
  "allOf" : [
    {
      "$ref": "https://my-domain.com/my-api/schemas/vehicle"
    },
    {
      "type": "object",
      "properties": {
        "doors": {
          "type": "integer"
        }
      },
      "required": [ "doors" ]
    }
  ]
}
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://my-domain.com/my-api/schemas/motor-cycle",
  "allOf" : [
    {
      "$ref": "https://my-domain.com/my-api/schemas/vehicle"
    },
    {
      "type": "object",
      "properties": {
        "sidekick": {
          "type": "boolean"
        }
      },
      "required": [ "sidekick" ]
    }
  ]
}

This produces the following Java records:

public record VehicleDto (
    @NotBlank String brand
) {}
public record CarDto (
    @NotBlank String brand,
    @NotNull Integer doors
) {}
public record MotorCycleDto (
    @NotBlank String brand,
    @NotNull Boolean sidekick
) {}

Note that this is not the correct interpretation of the "allOf" clause in a JSON Schema, and as such, the output from the code generation is non-standard. A future release will support inheritance using the "$ref" property.

Limitations

The following JSON Schema constructs are currently not supported, and for the most part silently ignored during code generation:

  • Restrictions on the "number" type: "multipleOf".
  • Properties with "const".
  • "string" properties with: "contentMediaType", "contentEncoding", "contentSchema".
  • Dynamic objects: "if", "then", "unevaluatedProperties".
  • Nested inline objects. Creating a separate JSON Schema and referencing it with "$ref" is recommended.
  • Extended schema validation features: "patternProperties", "propertyNames", "minProperties", "maxProperties".
  • Restrictions on arrays: tuple validation with "prefixItems".
  • Dynamic arrays: "unevaluatedItems", "contains", "minContains", "maxContains".
  • Documentation: "readOnly", "writeOnly".
  • Property schema composition: "allOf", "anyOf", "not". Only supports two subschemas for "oneOf", one of which must be {"type": "null"}.
  • Conditional subschemas: "dependentRequired", "dependentSchemas", "if"-"then"-"else".
  • Structuring: "$anchor", "$defs", recursion using "$ref".

Contributing

  1. Fork it (https://github.com/torand/jsonschema2java/fork)
  2. Create your feature branch (git checkout -b feature/fooBar)
  3. Commit your changes (git commit -am 'Add some fooBar')
  4. Push to the branch (git push origin feature/fooBar)
  5. Create a new Pull Request

License

This project is licensed under the Apache-2.0 License.

Footnotes

  1. Inline objects not supported. 2

  2. Expects string in the ISO 8601 duration format.

  3. Expects string in the ISO 8601 local date format.

  4. Expects string in the ISO 8601 local date time format (without milliseconds).

  5. When "format" is unspecified (i.e. BigDecimal). 2

About

Maven plugin to generate Java models from JSON Schema files

Topics

Resources

License

Stars

Watchers

Forks

Contributors 2

  •  
  •