An annotation processor converting JSON schemas to java records.
- Import the API:
<dependencies> <!-- ... --> <dependency> <groupId>com.cosium.json_schema_to_java_record</groupId> <artifactId>json-schema-to-java-record-api</artifactId> <version>${json-schema-to-java-record.version}</version> <scope>provided</scope> </dependency> <!-- ... --> </dependencies>
- Import the annotation processor:
<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <annotationProcessorPaths> <path> <groupId>com.cosium.json_schema_to_java_record</groupId> <artifactId>json-schema-to-java-record</artifactId> <version>${json-schema-to-java-record.version}</version> </path> </annotationProcessorPaths> </configuration> </plugin>
- Add your JSON schema files to the class path:
src/main/resources/com/aqme └── customer.json
{ "$schema": "https://json-schema.org/draft/2020-12/schema", "$id": "customer", "type": "object", "properties": { "firstName": { "type": "string" }, "lastName": { "type": "string" } }, "required": [ "firstName", "lastName" ] }
- Annotate a
package-info.java
file like this:@GenerateRecordsFromJsonSchemas( schemaRootFileLocations = @JsonSchemaFileLocation( moduleAndPackage = "com.aqme", relativeName = "customer.json" ) ) package com.aqme; import com.cosium.json_schema_to_java_record_api.GenerateRecordsFromJsonSchemas; import com.cosium.json_schema_to_java_record_api.JsonSchemaConfiguration; import com.cosium.json_schema_to_java_record_api.JsonSchemaFileLocation;
- Compile to generate this kind of output:
package com.aqme; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; import java.util.Objects; import javax.annotation.processing.Generated; import org.jspecify.annotations.NonNull; import org.jspecify.annotations.Nullable; @JsonInclude(JsonInclude.Include.NON_NULL) @Generated("com.cosium.json_schema_to_java_record_api.GenerateRecordsFromJsonSchemas") public record Customer( @JsonProperty("firstName") @NonNull String firstName, @JsonProperty("lastName") @NonNull String lastName) { public Customer { Objects.requireNonNull(firstName); Objects.requireNonNull(lastName); } }
JSON type | JSON format | JSON required | Java type |
---|---|---|---|
object | A java record | ||
string | date-time | java.time.ZonedDateTime | |
string | uri | java.net.URI | |
string | java.lang.String | ||
array | Immutable java.util.List | ||
number | required | double | |
number | non required | java.lang.Double | |
integer | required | int | |
integer | non required | java.lang.Integer | |
boolean | required | boolean | |
boolean | non required | java.lang.Boolean | |
null | java.lang.Void |
A schema having a non-null enum
array will be converted to a java enum.
Record components will be annotated with Jackson annotations .
JSON schema required is supported. By default, a property is considered as nullable. If a property is part of JSON schema required array, it will be considered as non-nullable.
If JSpecify nullness annotations (@Nullable
and @NonNull
) are part of the classpath, they will be used to annotate generated java record components.
A non-null array
will default to an empty immutable java.util.List
.
If RecordBuilder annotation io.soabase.recordbuilder.core.RecordBuilder
is part of the classpath, the former will be added to each generated java record.
JSON schema $ref is supported via the classpath "protocol."
This allows you to have multiple JSON schema files referring to each other.
All $ref
values should start with classpath:
, followed by the absolute path to the referred JSON file like this:
{ "$schema": "https://json-schema.org/draft/2020-12/schema", "$id": "address", "type": "object", "properties": { "country": { "$ref": "classpath:/com/cosium/json_schema_to_java_record_tests/case1/country.json" } } }
Most customizations rely on the JSON schema $id
attribute (aka schemaId
on the java API side). Make sure this attribute is valued and unique to benefit from customizations using it.
You can ask a particular schemaId
to be bound to an explicit Java type qualified name via JsonSchemaConfiguration#javaTypeQualifiedName
.
Example of JSON schema country.json
:
{ "$schema": "https://json-schema.org/draft/2020-12/schema", "$id": "country", "enum": ["FRANCE", "MOROCCO"] }
Example of configuration:
@GenerateRecordsFromJsonSchemas( schemaRootFileLocations = @JsonSchemaFileLocation( moduleAndPackage = "com.cosium.json_schema_to_java_record_tests.case1", relativeName = "country.json"), schemaConfigurations = @JsonSchemaConfiguration( schemaId = "country", javaTypeQualifiedName = "com.cosium.json_schema_to_java_record_tests.case1.Country") ) package com.cosium.json_schema_to_java_record_tests.case1;
Example of generated java class:
package com.cosium.json_schema_to_java_record_tests.case1; import javax.annotation.processing.Generated; @Generated("com.cosium.json_schema_to_java_record_api.GenerateRecordsFromJsonSchemas") public enum Country { FRANCE, MOROCCO }
You can ask a generated Java type to implement a list of interfaces via JsonSchemaConfiguration#javaInterfaceQualifiedNames
.
Example of JSON schema country.json
:
{ "$schema": "https://json-schema.org/draft/2020-12/schema", "$id": "country", "enum": ["FRANCE", "MOROCCO"] }
Example of configuration:
@GenerateRecordsFromJsonSchemas( schemaRootFileLocations = @JsonSchemaFileLocation( moduleAndPackage = "com.cosium.json_schema_to_java_record_tests.case1", relativeName = "country.json"), schemaConfigurations = @JsonSchemaConfiguration( schemaId = "country", javaInterfaceQualifiedNames = "com.cosium.json_schema_to_java_record_tests.case1.Location") ) package com.cosium.json_schema_to_java_record_tests.case1;
Example of generated java class:
import com.cosium.json_schema_to_java_record_tests.case1.Location; import javax.annotation.processing.Generated; @Generated("com.cosium.json_schema_to_java_record_api.GenerateRecordsFromJsonSchemas") public enum Country implements Location { FRANCE, MOROCCO }
You can ask for the creation of a Java class generation report by providing a non-empty value to GenerateRecordsFromJsonSchemas#reportClassQualifiedName
.
The generated report class will contain:
- A public constant
Map<String, Class> CLASS_BY_SCHEMA_ID
mapping each generated class to its JSON schema$id
. If the latter was missing, there will be no entry in the Map.
Example of configuration:
@GenerateRecordsFromJsonSchemas( schemaRootFileLocations = @JsonSchemaFileLocation( moduleAndPackage = "com.cosium.json_schema_to_java_record_tests.case1", relativeName = "customers.json" ), reportClassQualifiedName = "com.cosium.json_schema_to_java_record_tests.case1.Report" ) package com.aqme;
Example of generated report class:
package com.cosium.json_schema_to_java_record_tests.case1; import java.lang.Class; import java.lang.String; import java.util.Map; import javax.annotation.processing.Generated; @Generated("com.cosium.json_schema_to_java_record_api.GenerateRecordsFromJsonSchemas") public final class Report { public static final Map<String, Class> CLASS_BY_SCHEMA_ID = Map.ofEntries(Map.entry("country",Country.class),Map.entry("address",Address.class),Map.entry("customers",Customers.class),Map.entry("customer",Customer.class)); private Report() { } }