Repository: victools/jsonschema-generator
Pull request #44 (2020-03-13 21:42:12)

Support Draft 2019-09
Implements the minimal changes required to claim support of Draft 2019-09 as per #14.

- Introducing the ability to indicate the desired JSON Schema Version (for now only between "Draft 7" and "Draft 2019-09") for the schema being generated.
- Consider the possibility of keywords being renamed (like `definitions` to `$defs`).
- Deprecating various aspects that were not compatible with the support of multiple versions.


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 295c05f..6ade9b6 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,14 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
 and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
 
 ## [Unreleased]
+### Added
+- Explicit indication of targeted JSON Schema version (for now: Draft 7 or 2019-09)
+- Support for renamed keywords between Draft versions through new `SchemaKeyword` enum (replacing static `SchemaConstants` interface)
+
+### Deprecated
+- `SchemaConstants` interface containing static `String` constants for tag names/values
+- Internal `AttributeCollector` API without generation context as parameter
+- `SchemaGeneratorConfigBuilder` constructors without explicit JSON Schema version indication
 
 ## [4.5.0] - 2020-03-05
 ### Added
diff --git a/README.md b/README.md
index 82881ce..d53e245 100644
--- a/README.md
+++ b/README.md
@@ -2,7 +2,7 @@
 [![Build Status](https://github.com/victools/jsonschema-generator/workflows/Java%20CI%20(Maven)/badge.svg)](https://github.com/victools/jsonschema-generator/actions?query=workflow%3A%22Java+CI+%28Maven%29%22)
 [![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.github.victools/jsonschema-generator/badge.svg)](https://maven-badges.herokuapp.com/maven-central/com.github.victools/jsonschema-generator)
 
-Creating JSON Schema (Draft 7) from your Java classes utilising Jackson (inspired by JJSchema).
+Creating JSON Schema (Draft 7 or Draft 2019-09) from your Java classes utilising Jackson (inspired by JJSchema).
 
 Topics covered in this document are:
 - [Usage](#usage)
@@ -38,10 +38,11 @@ import com.github.victools.jsonschema.generator.OptionPreset;
 import com.github.victools.jsonschema.generator.SchemaGenerator;
 import com.github.victools.jsonschema.generator.SchemaGeneratorConfig;
 import com.github.victools.jsonschema.generator.SchemaGeneratorConfigBuilder;
+import com.github.victools.jsonschema.generator.SchemaVersion;
 ```
 ```java
 ObjectMapper objectMapper = new ObjectMapper();
-SchemaGeneratorConfigBuilder configBuilder = new SchemaGeneratorConfigBuilder(objectMapper, OptionPreset.PLAIN_JSON);
+SchemaGeneratorConfigBuilder configBuilder = new SchemaGeneratorConfigBuilder(objectMapper, SchemaVersion.DRAFT_2019_09, OptionPreset.PLAIN_JSON);
 SchemaGeneratorConfig config = configBuilder.build();
 SchemaGenerator generator = new SchemaGenerator(config);
 JsonNode jsonSchema = generator.generateSchema(YourClass.class);
@@ -59,9 +60,10 @@ The `OptionPreset` needs to be provided in the `SchemaGeneratorConfigBuilder`'s
 ```java
 import com.github.victools.jsonschema.generator.OptionPreset;
 import com.github.victools.jsonschema.generator.SchemaGeneratorConfigBuilder;
+import com.github.victools.jsonschema.generator.SchemaVersion;
 ```
 ```java
-SchemaGeneratorConfigBuilder configBuilder = new SchemaGeneratorConfigBuilder(objectMapper, OptionPreset.PLAIN_JSON);
+SchemaGeneratorConfigBuilder configBuilder = new SchemaGeneratorConfigBuilder(objectMapper, SchemaVersion.DRAFT_2019_09, OptionPreset.PLAIN_JSON);
 ```
 
 #### Toggling Standard Options (individually)
@@ -69,9 +71,10 @@ As alternative to (or on top of) the predefined `OptionPreset`s, you can set the
 ```java
 import com.github.victools.jsonschema.generator.Option;
 import com.github.victools.jsonschema.generator.SchemaGeneratorConfigBuilder;
+import com.github.victools.jsonschema.generator.SchemaVersion;
 ```
 ```java
-SchemaGeneratorConfigBuilder configBuilder = new SchemaGeneratorConfigBuilder(objectMapper)
+SchemaGeneratorConfigBuilder configBuilder = new SchemaGeneratorConfigBuilder(objectMapper, SchemaVersion.DRAFT_2019_09)
     .with(Option.FLATTENED_ENUMS)
     .without(Option.NULLABLE_FIELDS_BY_DEFAULT, Option.NULLABLE_METHOD_RETURN_VALUES_BY_DEFAULT);
 ```
@@ -81,9 +84,10 @@ You can also define your own `OptionPreset` or provide an empty one and then bui
 import com.github.victools.jsonschema.generator.Option;
 import com.github.victools.jsonschema.generator.OptionPreset;
 import com.github.victools.jsonschema.generator.SchemaGeneratorConfigBuilder;
+import com.github.victools.jsonschema.generator.SchemaVersion;
 ```
 ```java
-SchemaGeneratorConfigBuilder configBuilder = new SchemaGeneratorConfigBuilder(objectMapper, new OptionPreset())
+SchemaGeneratorConfigBuilder configBuilder = new SchemaGeneratorConfigBuilder(objectMapper, SchemaVersion.DRAFT_2019_09, new OptionPreset())
     .with(Option.ADDITIONAL_FIXED_TYPES, Option.PUBLIC_NONSTATIC_FIELDS);
 ```
 
@@ -91,10 +95,11 @@ SchemaGeneratorConfigBuilder configBuilder = new SchemaGeneratorConfigBuilder(ob
 ```java
 import com.github.victools.jsonschema.generator.Module;
 import com.github.victools.jsonschema.generator.SchemaGeneratorConfigBuilder;
+import com.github.victools.jsonschema.generator.SchemaVersion;
 ```
 ```java
 Module separateModule = new YourSeparateModule();
-SchemaGeneratorConfigBuilder configBuilder = new SchemaGeneratorConfigBuilder(objectMapper)
+SchemaGeneratorConfigBuilder configBuilder = new SchemaGeneratorConfigBuilder(objectMapper, SchemaVersion.DRAFT_2019_09)
     .with(separateModule);
 ```
 
@@ -108,10 +113,11 @@ Some available modules are:
 ```java
 import com.github.victools.jsonschema.generator.FieldScope;
 import com.github.victools.jsonschema.generator.SchemaGeneratorConfigBuilder;
+import com.github.victools.jsonschema.generator.SchemaVersion;
 import com.github.victools.jsonschema.generator.TypeScope;
 ```
 ```java
-SchemaGeneratorConfigBuilder configBuilder = new SchemaGeneratorConfigBuilder(objectMapper);
+SchemaGeneratorConfigBuilder configBuilder = new SchemaGeneratorConfigBuilder(objectMapper, SchemaVersion.DRAFT_2019_09);
 configBuilder.forTypesInGeneral()
     // populate the "title" of all schemas with a description of the java type
     .withTitleResolver(TypeScope::getSimpleTypeDescription);
@@ -129,10 +135,11 @@ According to the [JSON Schema Specification](https://json-schema.org/understandi
 While there are various ways to consider `additionalProperties`, the standard way could look like this (but may be simpler):
 ```java
 import com.github.victools.jsonschema.generator.SchemaGeneratorConfigBuilder;
+import com.github.victools.jsonschema.generator.SchemaVersion;
 import java.util.Map;
 ```
 ```java
-SchemaGeneratorConfigBuilder configBuilder = new SchemaGeneratorConfigBuilder(objectMapper)
+SchemaGeneratorConfigBuilder configBuilder = new SchemaGeneratorConfigBuilder(objectMapper, SchemaVersion.DRAFT_2019_09)
     .with(Option.FORBIDDEN_ADDITIONAL_PROPERTIES_BY_DEFAULT);
 configBuilder.forTypesInGeneral()
     .withAdditionalPropertiesResolver((scope) -> {
@@ -148,33 +155,34 @@ configBuilder.forTypesInGeneral()
 ## Supported JSON Schema attributes
 |    # | Attribute | Description |
 | ---: | :--- | :--- |
-|    1 | `$schema` | Fixed to "http://json-schema.org/draft-07/schema#" – can be toggled on/off via `Option.SCHEMA_VERSION_INDICATOR`. |
-|    2 | `definitions` | Filled with sub-schemas to support circular references – via `Option.DEFINITIONS_FOR_ALL_OBJECTS` it can be configured whether only sub-schemas appearing more than once are included or all. |
-|    3 | `$ref` | Used with relative references to sub-schemas in `definitions`. |
-|    4 | `type` | Differentiating between `boolean`/`string`/`integer`/`number` for primitive/known types. `null` is added if a property is deemed nullable according to configuration (`SchemaGeneratorConfigPart.withNullableCheck()`). Arrays and subtypes of `Collection<?>` are treated as `array`, everything else as `object`. A declared type may be interpreted as another type according to configuration (`SchemaGeneratorConfigPart.withTargetTypeOverrideResolver()`). |
-|    5 | `properties` | Listing all detected fields and/or methods in an `object`. Which ones are being included can be steered by various `Option`s or via one of the provided `OptionPreset`s as well as by ignoring individual ones via configuration (`SchemaGeneratorConfigPart.withIgnoreCheck()`). Names can be altered via configuration (`SchemaGeneratorConfigPart.withPropertyNameOverrideResolver()`). |
-|    6 | `items` | Indicating the type of `array`/`Collection` elements. |
-|    7 | `required` | Listing the names of fields/methods that are deemed mandatory according to configuration (`SchemaGeneratorConfigPart.withRequiredCheck()`). |
-|    8 | `allOf` | Used to combine general attributes derived from the type itself with attributes collected in the respective context of the associated field/method. |
-|    9 | `anyOf` | Used to list alternatives according to configuration (`SchemaGeneratorGeneralConfigPart.withSubtypeResolver()`). |
-|   10 | `oneOf` | Used to indicate when a particular field/method can be of `type` `null`. |
-|   11 | `title` | Collected value according to configuration (`SchemaGeneratorConfigPart.withTitleResolver()`). |
-|   12 | `description` | Collected value according to configuration (`SchemaGeneratorConfigPart.withDescriptionResolver()`). |
-|   13 | `const` | Collected value according to configuration (`SchemaGeneratorConfigPart.withEnumResolver()`) if only a single value was found. |
-|   14 | `enum` | Collected value according to configuration (`SchemaGeneratorConfigPart.withEnumResolver()`) if multiple values were found. |
-|   15 | `default` | Collected value according to configuration (`SchemaGeneratorConfigPart.withDefaultResolver()`). |
-|   16 | `additionalProperties` | Collected value according to configuration (`SchemaGeneratorConfigPart.withAdditionalPropertiesResolver()`). |
-|   17 | `patternProperties` | Collected value(s) according to configuration (`SchemaGeneratorConfigPart.withPatternPropertiesResolver()`). |
-|   18 | `minLength` | Collected value according to configuration (`SchemaGeneratorConfigPart.withStringMinLengthResolver()`). |
-|   19 | `maxLength` | Collected value according to configuration (`SchemaGeneratorConfigPart.withStringMaxLengthResolver()`). |
-|   20 | `format` | Collected value according to configuration (`SchemaGeneratorConfigPart.withStringFormatResolver()`). |
-|   21 | `pattern` | Collected value according to configuration (`SchemaGeneratorConfigPart.withStringPatternResolver()`). |
-|   22 | `minimum` | Collected value according to configuration (`SchemaGeneratorConfigPart.withNumberInclusiveMinimumResolver()`). |
-|   23 | `exclusiveMinimum` | Collected value according to configuration (`SchemaGeneratorConfigPart.withNumberExclusiveMinimumResolver()`). |
-|   24 | `maximum` | Collected value according to configuration (`SchemaGeneratorConfigPart.withNumberInclusiveMaximumResolver()`). |
-|   25 | `exclusiveMaximum` | Collected value according to configuration (`SchemaGeneratorConfigPart.withNumberExclusiveMaximumResolver()`). |
-|   26 | `multipleOf` | Collected value according to configuration (`SchemaGeneratorConfigPart.withNumberMultipleOfResolver()`). |
-|   27 | `minItems` | Collected value according to configuration (`SchemaGeneratorConfigPart.withArrayMinItemsResolver()`). |
-|   28 | `maxItems` | Collected value according to configuration (`SchemaGeneratorConfigPart.withArrayMaxItemsResolver()`). |
-|   29 | `uniqueItems` | Collected value according to configuration (`SchemaGeneratorConfigPart.withArrayUniqueItemsResolver()`). |
-|   30 | any other | You can directly manipulate the generated `ObjectNode` of a sub-schema – e.g. setting additional attributes – via configuration based on a given type in general (`SchemaGeneratorConfigBuilder.with(TypeAttributeOverride)`) and/or in the context of a particular field/method (`SchemaGeneratorConfigPart.withInstanceAttributeOverride()`). |
+|    1 | `$schema` | Set based on the `SchemaVersion` specified in the `SchemaGeneratorConfigBuilder`'s constructor – can be toggled on/off via `Option.SCHEMA_VERSION_INDICATOR`. |
+|    2 | `definitions` | Only for `SchemaVersion.DRAFT_7`: Filled with sub-schemas to support circular references – via `Option.DEFINITIONS_FOR_ALL_OBJECTS` it can be configured whether only sub-schemas appearing more than once are included or all. |
+|    3 | `$defs` | From `SchemaVersion.DRAFT_2019_09`: Filled with sub-schemas to support circular references – via `Option.DEFINITIONS_FOR_ALL_OBJECTS` it can be configured whether only sub-schemas appearing more than once are included or all. |
+|    4 | `$ref` | Used with relative references to sub-schemas in `definitions`. |
+|    5 | `type` | Differentiating between `boolean`/`string`/`integer`/`number` for primitive/known types. `null` is added if a property is deemed nullable according to configuration (`SchemaGeneratorConfigPart.withNullableCheck()`). Arrays and subtypes of `Collection<?>` are treated as `array`, everything else as `object`. A declared type may be interpreted as another type according to configuration (`SchemaGeneratorConfigPart.withTargetTypeOverrideResolver()`). |
+|    6 | `properties` | Listing all detected fields and/or methods in an `object`. Which ones are being included can be steered by various `Option`s or via one of the provided `OptionPreset`s as well as by ignoring individual ones via configuration (`SchemaGeneratorConfigPart.withIgnoreCheck()`). Names can be altered via configuration (`SchemaGeneratorConfigPart.withPropertyNameOverrideResolver()`). |
+|    7 | `items` | Indicating the type of `array`/`Collection` elements. |
+|    8 | `required` | Listing the names of fields/methods that are deemed mandatory according to configuration (`SchemaGeneratorConfigPart.withRequiredCheck()`). |
+|    9 | `allOf` | Used to combine general attributes derived from the type itself with attributes collected in the respective context of the associated field/method. |
+|   10 | `anyOf` | Used to list alternatives according to configuration (`SchemaGeneratorGeneralConfigPart.withSubtypeResolver()`). |
+|   11 | `oneOf` | Used to indicate when a particular field/method can be of `type` `null`. |
+|   12 | `title` | Collected value according to configuration (`SchemaGeneratorConfigPart.withTitleResolver()`). |
+|   13 | `description` | Collected value according to configuration (`SchemaGeneratorConfigPart.withDescriptionResolver()`). |
+|   14 | `const` | Collected value according to configuration (`SchemaGeneratorConfigPart.withEnumResolver()`) if only a single value was found. |
+|   15 | `enum` | Collected value according to configuration (`SchemaGeneratorConfigPart.withEnumResolver()`) if multiple values were found. |
+|   16 | `default` | Collected value according to configuration (`SchemaGeneratorConfigPart.withDefaultResolver()`). |
+|   17 | `additionalProperties` | Collected value according to configuration (`SchemaGeneratorConfigPart.withAdditionalPropertiesResolver()`). |
+|   18 | `patternProperties` | Collected value(s) according to configuration (`SchemaGeneratorConfigPart.withPatternPropertiesResolver()`). |
+|   19 | `minLength` | Collected value according to configuration (`SchemaGeneratorConfigPart.withStringMinLengthResolver()`). |
+|   20 | `maxLength` | Collected value according to configuration (`SchemaGeneratorConfigPart.withStringMaxLengthResolver()`). |
+|   21 | `format` | Collected value according to configuration (`SchemaGeneratorConfigPart.withStringFormatResolver()`). |
+|   22 | `pattern` | Collected value according to configuration (`SchemaGeneratorConfigPart.withStringPatternResolver()`). |
+|   23 | `minimum` | Collected value according to configuration (`SchemaGeneratorConfigPart.withNumberInclusiveMinimumResolver()`). |
+|   24 | `exclusiveMinimum` | Collected value according to configuration (`SchemaGeneratorConfigPart.withNumberExclusiveMinimumResolver()`). |
+|   25 | `maximum` | Collected value according to configuration (`SchemaGeneratorConfigPart.withNumberInclusiveMaximumResolver()`). |
+|   26 | `exclusiveMaximum` | Collected value according to configuration (`SchemaGeneratorConfigPart.withNumberExclusiveMaximumResolver()`). |
+|   27 | `multipleOf` | Collected value according to configuration (`SchemaGeneratorConfigPart.withNumberMultipleOfResolver()`). |
+|   28 | `minItems` | Collected value according to configuration (`SchemaGeneratorConfigPart.withArrayMinItemsResolver()`). |
+|   29 | `maxItems` | Collected value according to configuration (`SchemaGeneratorConfigPart.withArrayMaxItemsResolver()`). |
+|   30 | `uniqueItems` | Collected value according to configuration (`SchemaGeneratorConfigPart.withArrayUniqueItemsResolver()`). |
+|   31 | any other | You can directly manipulate the generated `ObjectNode` of a sub-schema – e.g. setting additional attributes – via configuration based on a given type in general (`SchemaGeneratorConfigBuilder.with(TypeAttributeOverride)`) and/or in the context of a particular field/method (`SchemaGeneratorConfigPart.withInstanceAttributeOverride()`). |
diff --git a/pom.xml b/pom.xml
index 72568f2..1e88313 100644
--- a/pom.xml
+++ b/pom.xml
@@ -8,7 +8,7 @@
 
     <groupId>com.github.victools</groupId>
     <artifactId>jsonschema-generator</artifactId>
-    <version>4.5.1-SNAPSHOT</version>
+    <version>4.6.0-SNAPSHOT</version>
     <packaging>jar</packaging>
 
     <licenses>
@@ -155,6 +155,13 @@
 
     <build>
         <plugins>
+            <plugin>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <version>2.3.2</version>
+                <configuration>
+                    <showDeprecation>true</showDeprecation>
+                </configuration>
+            </plugin>
             <plugin>
                 <artifactId>maven-checkstyle-plugin</artifactId>
                 <version>3.0.0</version>
diff --git a/src/main/java/com/github/victools/jsonschema/generator/InstanceAttributeOverride.java b/src/main/java/com/github/victools/jsonschema/generator/InstanceAttributeOverride.java
index a059039..35a847a 100644
--- a/src/main/java/com/github/victools/jsonschema/generator/InstanceAttributeOverride.java
+++ b/src/main/java/com/github/victools/jsonschema/generator/InstanceAttributeOverride.java
@@ -28,7 +28,7 @@ public interface InstanceAttributeOverride<M extends MemberScope<?, ?>> {
     /**
      * Add/remove attributes on the given JSON Schema node – this is specifically intended for attributes relating to a particular instance.
      * <br>
-     * E.g. "{@value SchemaConstants#TAG_DESCRIPTION}", "{@value SchemaConstants#TAG_MINIMUM}", "{@value SchemaConstants#TAG_MAXIMUM_EXCLUSIVE}"
+     * E.g. {@link SchemaKeyword#TAG_DESCRIPTION}, {@link SchemaKeyword#TAG_MINIMUM}, {@link SchemaKeyword#TAG_MAXIMUM_EXCLUSIVE}
      *
      * @param jsonSchemaAttributesNode node to modify
      * @param member reference/context to which the collected attributes in the JSON Schema node are referring
diff --git a/src/main/java/com/github/victools/jsonschema/generator/Option.java b/src/main/java/com/github/victools/jsonschema/generator/Option.java
index 028e102..346da44 100644
--- a/src/main/java/com/github/victools/jsonschema/generator/Option.java
+++ b/src/main/java/com/github/victools/jsonschema/generator/Option.java
@@ -35,16 +35,16 @@ import java.util.stream.Stream;
  */
 public enum Option {
     /**
-     * Whether the "{@value SchemaConstants#TAG_SCHEMA}" attribute with value "{@value SchemaConstants#TAG_SCHEMA_DRAFT7}" should be included.
+     * Whether the {@link SchemaKeyword#TAG_SCHEMA} attribute with {@link SchemaKeyword#TAG_SCHEMA_VALUE} should be included.
      */
     SCHEMA_VERSION_INDICATOR(null, null),
     /**
-     * Whether additional types (and not just primitives and their associated classes should be included as fixed schema with a "type" attribute of
-     * "string"/"boolean"/"integer"/"number".
+     * Whether additional types (and not just primitives and their associated classes should be included as fixed schema with a
+     * {@link SchemaKeyword#TAG_TYPE} attribute of "string"/"boolean"/"integer"/"number".
      */
     ADDITIONAL_FIXED_TYPES(SimpleTypeModule::forPrimitiveAndAdditionalTypes, SimpleTypeModule::forPrimitiveTypes),
     /**
-     * Whether enums should be treated as "{@value SchemaConstants#TAG_TYPE_OBJECT}", with all methods but {@link Enum#name() name()} being excluded.
+     * Whether enums should be treated as {@link SchemaKeyword#TAG_TYPE_OBJECT}, with all methods but {@link Enum#name() name()} being excluded.
      * <br>
      * This only takes effect if {@link Option#FLATTENED_ENUMS} and {@link Option#FLATTENED_ENUMS_FROM_TOSTRING} are disabled.
      *
@@ -53,7 +53,7 @@ public enum Option {
      */
     SIMPLIFIED_ENUMS(EnumModule::asObjects, null),
     /**
-     * Whether enums should be treated as plain "{@value SchemaConstants#TAG_TYPE_STRING}" values – derived from their respective constant name.
+     * Whether enums should be treated as plain {@link SchemaKeyword#TAG_TYPE_STRING} values – derived from their respective constant name.
      * <br>
      * This only takes effect if {@link Option#FLATTENED_ENUMS_FROM_TOSTRING} is disabled but takes priority over {@link Option#SIMPLIFIED_ENUMS}.
      *
@@ -62,7 +62,7 @@ public enum Option {
      */
     FLATTENED_ENUMS(EnumModule::asStringsFromName, null, Option.SIMPLIFIED_ENUMS),
     /**
-     * Whether enums should be treated as plain "{@value SchemaConstants#TAG_TYPE_STRING}" values – derived from their respective {@code toString()}.
+     * Whether enums should be treated as plain {@link SchemaKeyword#TAG_TYPE_STRING} values – derived from their respective {@code toString()}.
      * <br>
      * This takes priority over both {@link Option#FLATTENED_ENUMS} and {@link Option#SIMPLIFIED_ENUMS}.
      *
diff --git a/src/main/java/com/github/victools/jsonschema/generator/SchemaConstants.java b/src/main/java/com/github/victools/jsonschema/generator/SchemaConstants.java
index 92ed9b9..d3c343f 100644
--- a/src/main/java/com/github/victools/jsonschema/generator/SchemaConstants.java
+++ b/src/main/java/com/github/victools/jsonschema/generator/SchemaConstants.java
@@ -18,53 +18,56 @@ package com.github.victools.jsonschema.generator;
 
 /**
  * JSON Schema properties and their values.
+ *
+ * @deprecated this only covers Draft 7; use {@link SchemaKeyword} instead
  */
+@Deprecated
 public interface SchemaConstants {
 
-    String TAG_SCHEMA = "$schema";
-    String TAG_SCHEMA_DRAFT7 = "http://json-schema.org/draft-07/schema#";
-    String TAG_DEFINITIONS = "definitions";
-    String TAG_REF = "$ref";
-    String TAG_REF_MAIN = "#";
-    String TAG_REF_PREFIX = "#/definitions/";
+    String TAG_SCHEMA = SchemaKeyword.TAG_SCHEMA.forVersion(SchemaVersion.DRAFT_7);
+    String TAG_SCHEMA_DRAFT7 = SchemaKeyword.TAG_SCHEMA_VALUE.forVersion(SchemaVersion.DRAFT_7);
+    String TAG_DEFINITIONS = SchemaKeyword.TAG_DEFINITIONS.forVersion(SchemaVersion.DRAFT_7);
+    String TAG_REF = SchemaKeyword.TAG_REF.forVersion(SchemaVersion.DRAFT_7);
+    String TAG_REF_MAIN = SchemaKeyword.TAG_REF_MAIN.forVersion(SchemaVersion.DRAFT_7);
+    String TAG_REF_PREFIX = SchemaKeyword.TAG_REF_PREFIX.forVersion(SchemaVersion.DRAFT_7);
 
-    String TAG_TYPE = "type";
-    String TAG_TYPE_NULL = "null";
-    String TAG_TYPE_ARRAY = "array";
-    String TAG_TYPE_OBJECT = "object";
-    String TAG_TYPE_BOOLEAN = "boolean";
-    String TAG_TYPE_STRING = "string";
-    String TAG_TYPE_INTEGER = "integer";
-    String TAG_TYPE_NUMBER = "number";
+    String TAG_TYPE = SchemaKeyword.TAG_TYPE.forVersion(SchemaVersion.DRAFT_7);
+    String TAG_TYPE_NULL = SchemaKeyword.TAG_TYPE_NULL.forVersion(SchemaVersion.DRAFT_7);
+    String TAG_TYPE_ARRAY = SchemaKeyword.TAG_TYPE_ARRAY.forVersion(SchemaVersion.DRAFT_7);
+    String TAG_TYPE_OBJECT = SchemaKeyword.TAG_TYPE_OBJECT.forVersion(SchemaVersion.DRAFT_7);
+    String TAG_TYPE_BOOLEAN = SchemaKeyword.TAG_TYPE_BOOLEAN.forVersion(SchemaVersion.DRAFT_7);
+    String TAG_TYPE_STRING = SchemaKeyword.TAG_TYPE_STRING.forVersion(SchemaVersion.DRAFT_7);
+    String TAG_TYPE_INTEGER = SchemaKeyword.TAG_TYPE_INTEGER.forVersion(SchemaVersion.DRAFT_7);
+    String TAG_TYPE_NUMBER = SchemaKeyword.TAG_TYPE_NUMBER.forVersion(SchemaVersion.DRAFT_7);
 
-    String TAG_PROPERTIES = "properties";
-    String TAG_ITEMS = "items";
-    String TAG_REQUIRED = "required";
-    String TAG_ADDITIONAL_PROPERTIES = "additionalProperties";
-    String TAG_PATTERN_PROPERTIES = "patternProperties";
+    String TAG_PROPERTIES = SchemaKeyword.TAG_PROPERTIES.forVersion(SchemaVersion.DRAFT_7);
+    String TAG_ITEMS = SchemaKeyword.TAG_ITEMS.forVersion(SchemaVersion.DRAFT_7);
+    String TAG_REQUIRED = SchemaKeyword.TAG_REQUIRED.forVersion(SchemaVersion.DRAFT_7);
+    String TAG_ADDITIONAL_PROPERTIES = SchemaKeyword.TAG_ADDITIONAL_PROPERTIES.forVersion(SchemaVersion.DRAFT_7);
+    String TAG_PATTERN_PROPERTIES = SchemaKeyword.TAG_PATTERN_PROPERTIES.forVersion(SchemaVersion.DRAFT_7);
 
-    String TAG_ALLOF = "allOf";
-    String TAG_ANYOF = "anyOf";
-    String TAG_ONEOF = "oneOf";
+    String TAG_ALLOF = SchemaKeyword.TAG_ALLOF.forVersion(SchemaVersion.DRAFT_7);
+    String TAG_ANYOF = SchemaKeyword.TAG_ANYOF.forVersion(SchemaVersion.DRAFT_7);
+    String TAG_ONEOF = SchemaKeyword.TAG_ONEOF.forVersion(SchemaVersion.DRAFT_7);
 
-    String TAG_TITLE = "title";
-    String TAG_DESCRIPTION = "description";
-    String TAG_CONST = "const";
-    String TAG_ENUM = "enum";
-    String TAG_DEFAULT = "default";
+    String TAG_TITLE = SchemaKeyword.TAG_TITLE.forVersion(SchemaVersion.DRAFT_7);
+    String TAG_DESCRIPTION = SchemaKeyword.TAG_DESCRIPTION.forVersion(SchemaVersion.DRAFT_7);
+    String TAG_CONST = SchemaKeyword.TAG_CONST.forVersion(SchemaVersion.DRAFT_7);
+    String TAG_ENUM = SchemaKeyword.TAG_ENUM.forVersion(SchemaVersion.DRAFT_7);
+    String TAG_DEFAULT = SchemaKeyword.TAG_DEFAULT.forVersion(SchemaVersion.DRAFT_7);
 
-    String TAG_LENGTH_MIN = "minLength";
-    String TAG_LENGTH_MAX = "maxLength";
-    String TAG_FORMAT = "format";
-    String TAG_PATTERN = "pattern";
+    String TAG_LENGTH_MIN = SchemaKeyword.TAG_LENGTH_MIN.forVersion(SchemaVersion.DRAFT_7);
+    String TAG_LENGTH_MAX = SchemaKeyword.TAG_LENGTH_MAX.forVersion(SchemaVersion.DRAFT_7);
+    String TAG_FORMAT = SchemaKeyword.TAG_FORMAT.forVersion(SchemaVersion.DRAFT_7);
+    String TAG_PATTERN = SchemaKeyword.TAG_PATTERN.forVersion(SchemaVersion.DRAFT_7);
 
-    String TAG_MINIMUM = "minimum";
-    String TAG_MINIMUM_EXCLUSIVE = "exclusiveMinimum";
-    String TAG_MAXIMUM = "maximum";
-    String TAG_MAXIMUM_EXCLUSIVE = "exclusiveMaximum";
-    String TAG_MULTIPLE_OF = "multipleOf";
+    String TAG_MINIMUM = SchemaKeyword.TAG_MINIMUM.forVersion(SchemaVersion.DRAFT_7);
+    String TAG_MINIMUM_EXCLUSIVE = SchemaKeyword.TAG_MINIMUM_EXCLUSIVE.forVersion(SchemaVersion.DRAFT_7);
+    String TAG_MAXIMUM = SchemaKeyword.TAG_MAXIMUM.forVersion(SchemaVersion.DRAFT_7);
+    String TAG_MAXIMUM_EXCLUSIVE = SchemaKeyword.TAG_MAXIMUM_EXCLUSIVE.forVersion(SchemaVersion.DRAFT_7);
+    String TAG_MULTIPLE_OF = SchemaKeyword.TAG_MULTIPLE_OF.forVersion(SchemaVersion.DRAFT_7);
 
-    String TAG_ITEMS_MIN = "minItems";
-    String TAG_ITEMS_MAX = "maxItems";
-    String TAG_ITEMS_UNIQUE = "uniqueItems";
+    String TAG_ITEMS_MIN = SchemaKeyword.TAG_ITEMS_MIN.forVersion(SchemaVersion.DRAFT_7);
+    String TAG_ITEMS_MAX = SchemaKeyword.TAG_ITEMS_MAX.forVersion(SchemaVersion.DRAFT_7);
+    String TAG_ITEMS_UNIQUE = SchemaKeyword.TAG_ITEMS_UNIQUE.forVersion(SchemaVersion.DRAFT_7);
 }
diff --git a/src/main/java/com/github/victools/jsonschema/generator/SchemaGenerationContext.java b/src/main/java/com/github/victools/jsonschema/generator/SchemaGenerationContext.java
index 336859e..d668e68 100644
--- a/src/main/java/com/github/victools/jsonschema/generator/SchemaGenerationContext.java
+++ b/src/main/java/com/github/victools/jsonschema/generator/SchemaGenerationContext.java
@@ -31,6 +31,15 @@ public interface SchemaGenerationContext {
      */
     SchemaGeneratorConfig getGeneratorConfig();
 
+    /**
+     * Look-up a given keyword's associated tag name or value for the designated JSON Schema version.
+     *
+     * @param keyword reference to a tag name or value
+     * @return specific tag name or value in the designated JSON Schema version
+     * @see SchemaGeneratorConfig#getKeyword(SchemaKeyword)
+     */
+    String getKeyword(SchemaKeyword keyword);
+
     /**
      * Getter for the type resolution/introspection context in use.
      *
diff --git a/src/main/java/com/github/victools/jsonschema/generator/SchemaGenerator.java b/src/main/java/com/github/victools/jsonschema/generator/SchemaGenerator.java
index e303bb7..b899bb2 100644
--- a/src/main/java/com/github/victools/jsonschema/generator/SchemaGenerator.java
+++ b/src/main/java/com/github/victools/jsonschema/generator/SchemaGenerator.java
@@ -69,11 +69,12 @@ public class SchemaGenerator {
 
         ObjectNode jsonSchemaResult = this.config.createObjectNode();
         if (this.config.shouldIncludeSchemaVersionIndicator()) {
-            jsonSchemaResult.put(SchemaConstants.TAG_SCHEMA, SchemaConstants.TAG_SCHEMA_DRAFT7);
+            jsonSchemaResult.put(this.config.getKeyword(SchemaKeyword.TAG_SCHEMA),
+                    this.config.getKeyword(SchemaKeyword.TAG_SCHEMA_VALUE));
         }
         ObjectNode definitionsNode = this.buildDefinitionsAndResolveReferences(mainType, generationContext);
         if (definitionsNode.size() > 0) {
-            jsonSchemaResult.set(SchemaConstants.TAG_DEFINITIONS, definitionsNode);
+            jsonSchemaResult.set(this.config.getKeyword(SchemaKeyword.TAG_DEFINITIONS), definitionsNode);
         }
         ObjectNode mainSchemaNode = generationContext.getDefinition(mainType);
         jsonSchemaResult.setAll(mainSchemaNode);
@@ -117,34 +118,34 @@ public class SchemaGenerator {
                     && (referencingNodes.isEmpty() || (!createDefinitionsForAll && (referencingNodes.size() + nullableReferences.size()) < 2));
             if (referenceInline) {
                 // it is a simple type, just in-line the sub-schema everywhere
-                referencingNodes.forEach(referenceNode -> referenceNode.setAll(generationContext.getDefinition(types.get(0))));
+                referencingNodes.forEach(node -> node.setAll(generationContext.getDefinition(types.get(0))));
                 referenceKey = null;
             } else {
                 // the same sub-schema is referenced in multiple places
                 if (types.contains(mainSchemaTarget)) {
-                    referenceKey = SchemaConstants.TAG_REF_MAIN;
+                    referenceKey = this.config.getKeyword(SchemaKeyword.TAG_REF_MAIN);
                 } else {
                     // add it to the definitions (unless it is the main schema)
                     definitionsNode.set(alias, generationContext.getDefinition(types.get(0)));
-                    referenceKey = SchemaConstants.TAG_REF_PREFIX + alias;
+                    referenceKey = this.config.getKeyword(SchemaKeyword.TAG_REF_PREFIX) + alias;
                 }
-                referencingNodes.forEach(referenceNode -> referenceNode.put(SchemaConstants.TAG_REF, referenceKey));
+                referencingNodes.forEach(node -> node.put(this.config.getKeyword(SchemaKeyword.TAG_REF), referenceKey));
             }
             if (!nullableReferences.isEmpty()) {
                 ObjectNode definition;
                 if (referenceInline) {
                     definition = generationContext.getDefinition(types.get(0));
                 } else {
-                    definition = this.config.createObjectNode().put(SchemaConstants.TAG_REF, referenceKey);
+                    definition = this.config.createObjectNode().put(this.config.getKeyword(SchemaKeyword.TAG_REF), referenceKey);
                 }
                 generationContext.makeNullable(definition);
                 if (createDefinitionsForAll || nullableReferences.size() > 1) {
                     String nullableAlias = alias + "-nullable";
-                    String nullableReferenceKey = SchemaConstants.TAG_REF_PREFIX + nullableAlias;
+                    String nullableReferenceKey = this.config.getKeyword(SchemaKeyword.TAG_REF_PREFIX) + nullableAlias;
                     definitionsNode.set(nullableAlias, definition);
-                    nullableReferences.forEach(referenceNode -> referenceNode.put(SchemaConstants.TAG_REF, nullableReferenceKey));
+                    nullableReferences.forEach(node -> node.put(this.config.getKeyword(SchemaKeyword.TAG_REF), nullableReferenceKey));
                 } else {
-                    nullableReferences.forEach(referenceNode -> referenceNode.setAll(definition));
+                    nullableReferences.forEach(node -> node.setAll(definition));
                 }
             }
         }
diff --git a/src/main/java/com/github/victools/jsonschema/generator/SchemaGeneratorConfig.java b/src/main/java/com/github/victools/jsonschema/generator/SchemaGeneratorConfig.java
index 5064036..f032e67 100644
--- a/src/main/java/com/github/victools/jsonschema/generator/SchemaGeneratorConfig.java
+++ b/src/main/java/com/github/victools/jsonschema/generator/SchemaGeneratorConfig.java
@@ -31,6 +31,23 @@ import java.util.Map;
  */
 public interface SchemaGeneratorConfig {
 
+    /**
+     * Getter for the designated JSON Schema version.
+     *
+     * @return target version of the JSON Schema to generate
+     */
+    SchemaVersion getSchemaVersion();
+
+    /**
+     * Getter for the indicated keyword's value in the designated JSON Schema version.
+     *
+     * @param keyword reference to a tag name or value
+     * @return referenced tag name/value in the designated schema version
+     * @see #getSchemaVersion()
+     * @see SchemaKeyword#forVersion(SchemaVersion)
+     */
+    String getKeyword(SchemaKeyword keyword);
+
     /**
      * Determine whether all referenced objects should be listed in the schema's "definitions", even if they only occur once.
      *
@@ -39,7 +56,7 @@ public interface SchemaGeneratorConfig {
     boolean shouldCreateDefinitionsForAllObjects();
 
     /**
-     * Determine whether the "{@value SchemaConstants#TAG_SCHEMA}" attribute with value "{@value SchemaConstants#TAG_SCHEMA_DRAFT7}" should be added.
+     * Determine whether the {@link SchemaKeyword#TAG_SCHEMA} attribute with {@link SchemaKeyword#TAG_SCHEMA_VALUE} should be added.
      *
      * @return whether to add the schema version attribute
      */
@@ -96,7 +113,7 @@ public interface SchemaGeneratorConfig {
             CustomDefinitionProviderV2 ignoredDefinitionProvider);
 
     /**
-     * Look-up a declared type's subtypes in order to list those specifically (in an "{@value SchemaConstants#TAG_ANYOF}").
+     * Look-up a declared type's subtypes in order to list those specifically (in an {@link SchemaKeyword#TAG_ANYOF}).
      *
      * @param javaType declared type to look-up subtypes for
      * @param context generation context (including a reference to the {@code TypeContext} for deriving a {@link ResolvedType} from a {@link Class})
diff --git a/src/main/java/com/github/victools/jsonschema/generator/SchemaGeneratorConfigBuilder.java b/src/main/java/com/github/victools/jsonschema/generator/SchemaGeneratorConfigBuilder.java
index 5e339ca..bf223e6 100644
--- a/src/main/java/com/github/victools/jsonschema/generator/SchemaGeneratorConfigBuilder.java
+++ b/src/main/java/com/github/victools/jsonschema/generator/SchemaGeneratorConfigBuilder.java
@@ -33,6 +33,7 @@ public class SchemaGeneratorConfigBuilder {
 
     private final ObjectMapper objectMapper;
     private final OptionPreset preset;
+    private final SchemaVersion schemaVersion;
 
     private final Map<Option, Boolean> options = new HashMap<>();
     private final SchemaGeneratorGeneralConfigPart typesInGeneralConfigPart = new SchemaGeneratorGeneralConfigPart();
@@ -40,24 +41,53 @@ public class SchemaGeneratorConfigBuilder {
     private final SchemaGeneratorConfigPart<MethodScope> methodConfigPart = new SchemaGeneratorConfigPart<>();
 
     /**
-     * Constructor of an empty configuration builder. This is equivalent to calling:<br>
-     * {@code new SchemaGeneratorConfigBuilder(objectMapper, OptionPreset.FULL_DOCUMENTATION)}
+     * Constructor of an empty configuration builder for a {@link SchemaVersion#DRAFT_7 Draft 7} schema. This is equivalent to calling:<br>
+     * {@code new SchemaGeneratorConfigBuilder(objectMapper, SchemaVersion.DRAFT_7, OptionPreset.FULL_DOCUMENTATION)}
      *
      * @param objectMapper supplier for object and array nodes for the JSON structure being generated
-     * @see #SchemaGeneratorConfigBuilder(ObjectMapper, OptionPreset)
+     * @see #SchemaGeneratorConfigBuilder(ObjectMapper, SchemaVersion, OptionPreset)
+     * @deprecated use {@link #SchemaGeneratorConfigBuilder(ObjectMapper, SchemaVersion)} instead
      */
+    @Deprecated
     public SchemaGeneratorConfigBuilder(ObjectMapper objectMapper) {
-        this(objectMapper, OptionPreset.FULL_DOCUMENTATION);
+        this(objectMapper, SchemaVersion.DRAFT_7, OptionPreset.FULL_DOCUMENTATION);
     }
 
     /**
-     * Constructor of an empty configuration builder.
+     * Constructor of an empty configuration builder. This is equivalent to calling:<br>
+     * {@code new SchemaGeneratorConfigBuilder(objectMapper, schemaVersion, OptionPreset.FULL_DOCUMENTATION)}
+     *
+     * @param objectMapper supplier for object and array nodes for the JSON structure being generated
+     * @param schemaVersion designated JSON Schema version
+     * @see #SchemaGeneratorConfigBuilder(ObjectMapper, SchemaVersion, OptionPreset)
+     */
+    public SchemaGeneratorConfigBuilder(ObjectMapper objectMapper, SchemaVersion schemaVersion) {
+        this(objectMapper, schemaVersion, OptionPreset.FULL_DOCUMENTATION);
+    }
+
+    /**
+     * Constructor of an empty configuration builder for a {@link SchemaVersion#DRAFT_7 Draft 7} schema. This is equivalent to calling:<br>
+     * {@code new SchemaGeneratorConfigBuilder(objectMapper, SchemaVersion.DRAFT_7, preset)}
      *
      * @param objectMapper supplier for object and array nodes for the JSON structure being generated
      * @param preset default settings for standard {@link Option} values
+     * @deprecated use {@link #SchemaGeneratorConfigBuilder(ObjectMapper, SchemaVersion, OptionPreset)} instead
      */
+    @Deprecated
     public SchemaGeneratorConfigBuilder(ObjectMapper objectMapper, OptionPreset preset) {
+        this(objectMapper, SchemaVersion.DRAFT_7, preset);
+    }
+
+    /**
+     * Constructor of an empty configuration builder.
+     *
+     * @param objectMapper supplier for object and array nodes for the JSON structure being generated
+     * @param schemaVersion designated JSON Schema version
+     * @param preset default settings for standard {@link Option} values
+     */
+    public SchemaGeneratorConfigBuilder(ObjectMapper objectMapper, SchemaVersion schemaVersion, OptionPreset preset) {
         this.objectMapper = objectMapper;
+        this.schemaVersion = schemaVersion;
         this.preset = preset;
     }
 
@@ -84,6 +114,7 @@ public class SchemaGeneratorConfigBuilder {
         enabledOptions.retainAll(validOptions.keySet());
         // construct the actual configuration instance
         return new SchemaGeneratorConfigImpl(this.objectMapper,
+                this.schemaVersion,
                 enabledOptions,
                 this.typesInGeneralConfigPart,
                 this.fieldConfigPart,
diff --git a/src/main/java/com/github/victools/jsonschema/generator/SchemaKeyword.java b/src/main/java/com/github/victools/jsonschema/generator/SchemaKeyword.java
new file mode 100644
index 0000000..4154554
--- /dev/null
+++ b/src/main/java/com/github/victools/jsonschema/generator/SchemaKeyword.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2020 VicTools.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.github.victools.jsonschema.generator;
+
+import java.util.function.Function;
+
+/**
+ * JSON Schema properties and their values.
+ */
+public enum SchemaKeyword {
+    TAG_SCHEMA("$schema"),
+    TAG_SCHEMA_VALUE(SchemaVersion::getIdentifier),
+    TAG_DEFINITIONS(version -> version == SchemaVersion.DRAFT_7 ? "definitions" : "$defs"),
+    TAG_REF("$ref"),
+    TAG_REF_MAIN("#"),
+    TAG_REF_PREFIX(version -> version == SchemaVersion.DRAFT_7 ? "#/definitions/" : "#/$defs/"),
+
+    TAG_TYPE("type"),
+    TAG_TYPE_NULL("null"),
+    TAG_TYPE_ARRAY("array"),
+    TAG_TYPE_OBJECT("object"),
+    TAG_TYPE_BOOLEAN("boolean"),
+    TAG_TYPE_STRING("string"),
+    TAG_TYPE_INTEGER("integer"),
+    TAG_TYPE_NUMBER("number"),
+
+    TAG_PROPERTIES("properties"),
+    TAG_ITEMS("items"),
+    TAG_REQUIRED("required"),
+    TAG_ADDITIONAL_PROPERTIES("additionalProperties"),
+    TAG_PATTERN_PROPERTIES("patternProperties"),
+
+    TAG_ALLOF("allOf"),
+    TAG_ANYOF("anyOf"),
+    TAG_ONEOF("oneOf"),
+
+    TAG_TITLE("title"),
+    TAG_DESCRIPTION("description"),
+    TAG_CONST("const"),
+    TAG_ENUM("enum"),
+    TAG_DEFAULT("default"),
+
+    TAG_LENGTH_MIN("minLength"),
+    TAG_LENGTH_MAX("maxLength"),
+    TAG_FORMAT("format"),
+    TAG_PATTERN("pattern"),
+
+    TAG_MINIMUM("minimum"),
+    TAG_MINIMUM_EXCLUSIVE("exclusiveMinimum"),
+    TAG_MAXIMUM("maximum"),
+    TAG_MAXIMUM_EXCLUSIVE("exclusiveMaximum"),
+    TAG_MULTIPLE_OF("multipleOf"),
+
+    TAG_ITEMS_MIN("minItems"),
+    TAG_ITEMS_MAX("maxItems"),
+    TAG_ITEMS_UNIQUE("uniqueItems");
+
+    private final Function<SchemaVersion, String> valueProvider;
+
+    /**
+     * Constructor.
+     *
+     * @param fixedValue single value applying regardless of schema version
+     */
+    private SchemaKeyword(String fixedValue) {
+        this.valueProvider = _version -> fixedValue;
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param valueProvider dynamic value provider that may return different values base on specific JSON Schema versions
+     */
+    private SchemaKeyword(Function<SchemaVersion, String> valueProvider) {
+        this.valueProvider = valueProvider;
+    }
+
+    /**
+     * Provide the appropriate tag name/value, considering the specified schema version.
+     *
+     * @param version applicable JSON Schema version
+     * @return corresponding tag name/value for this keyword in the indicated JSON Schema version
+     */
+    public String forVersion(SchemaVersion version) {
+        return this.valueProvider.apply(version);
+    }
+}
diff --git a/src/main/java/com/github/victools/jsonschema/generator/SchemaVersion.java b/src/main/java/com/github/victools/jsonschema/generator/SchemaVersion.java
new file mode 100644
index 0000000..6ee91e5
--- /dev/null
+++ b/src/main/java/com/github/victools/jsonschema/generator/SchemaVersion.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2020 VicTools.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.github.victools.jsonschema.generator;
+
+/**
+ * Supported JSON Schema Versions.
+ */
+public enum SchemaVersion {
+    DRAFT_7("http://json-schema.org/draft-07/schema#"),
+    DRAFT_2019_09("https://json-schema.org/draft/2019-09/schema");
+
+    private final String identifier;
+
+    /**
+     * Constructor.
+     *
+     * @param schemaIdentifier value for the {@code $schema} tag
+     */
+    private SchemaVersion(String schemaIdentifier) {
+        this.identifier = schemaIdentifier;
+    }
+
+    /**
+     * Getter for the {@code $schema} tag's value.
+     *
+     * @return value for the {@code $schema} tag
+     */
+    public String getIdentifier() {
+        return this.identifier;
+    }
+}
diff --git a/src/main/java/com/github/victools/jsonschema/generator/SubtypeResolver.java b/src/main/java/com/github/victools/jsonschema/generator/SubtypeResolver.java
index 1e112af..c77509d 100644
--- a/src/main/java/com/github/victools/jsonschema/generator/SubtypeResolver.java
+++ b/src/main/java/com/github/victools/jsonschema/generator/SubtypeResolver.java
@@ -20,11 +20,11 @@ import com.fasterxml.classmate.ResolvedType;
 import java.util.List;
 
 /**
- * Resolver for looking up a declared type's subtypes in order to list those specifically (in an "{@value SchemaConstants#TAG_ANYOF}").
+ * Resolver for looking up a declared type's subtypes in order to list those specifically (in an {@link SchemaKeyword#TAG_ANYOF}).
  * <br>
- * Assumption being that "{@value SchemaConstants#TAG_ONEOF}" would require a schema validator to unnecessarily check against all listed sub-schemas
- * to ensure that only a single one is matching a given JSON instance. By making the sub-schemas mutually exclusive, the same semantics can be
- * achieved, but allowing the schema validator to ignore any sub-schemas after the first match was found.
+ * Assumption being that {@link SchemaKeyword#TAG_ONEOF} would require a schema validator to unnecessarily check against all listed sub-schemas to
+ * ensure that only a single one is matching a given JSON instance. By making the sub-schemas mutually exclusive, the same semantics can be achieved,
+ * but allowing the schema validator to ignore any sub-schemas after the first match was found.
  */
 @FunctionalInterface
 public interface SubtypeResolver {
diff --git a/src/main/java/com/github/victools/jsonschema/generator/TypeAttributeOverride.java b/src/main/java/com/github/victools/jsonschema/generator/TypeAttributeOverride.java
index 0408b68..1e72c2c 100644
--- a/src/main/java/com/github/victools/jsonschema/generator/TypeAttributeOverride.java
+++ b/src/main/java/com/github/victools/jsonschema/generator/TypeAttributeOverride.java
@@ -27,7 +27,7 @@ public interface TypeAttributeOverride {
     /**
      * Add/remove attributes on the given JSON Schema node – this is specifically intended for attributes relating to the type in general.
      * <br>
-     * E.g. "{@value SchemaConstants#TAG_FORMAT}", "{@value SchemaConstants#TAG_PATTERN}", "{@value SchemaConstants#TAG_REQUIRED}"
+     * E.g. {@link SchemaKeyword#TAG_FORMAT}, {@link SchemaKeyword#TAG_PATTERN}, {@link SchemaKeyword#TAG_REQUIRED}
      *
      * @param jsonSchemaTypeNode node to modify (the part that may be referenced multiple times)
      * @param scope the type representation associated with the JSON Schema node
diff --git a/src/main/java/com/github/victools/jsonschema/generator/TypeContext.java b/src/main/java/com/github/victools/jsonschema/generator/TypeContext.java
index d91f5c9..6968091 100644
--- a/src/main/java/com/github/victools/jsonschema/generator/TypeContext.java
+++ b/src/main/java/com/github/victools/jsonschema/generator/TypeContext.java
@@ -139,7 +139,7 @@ public class TypeContext {
     }
 
     /**
-     * Determine whether a given type should be treated as "{@value SchemaConstants#TAG_TYPE_ARRAY}" in the generated schema.
+     * Determine whether a given type should be treated as {@link SchemaKeyword#TAG_TYPE_ARRAY} in the generated schema.
      *
      * @param type type to check
      * @return whether the given type is an array or sub type of {@link Collection}
@@ -149,7 +149,7 @@ public class TypeContext {
     }
 
     /**
-     * Identify the element/item type of the given "{@value SchemaConstants#TAG_TYPE_ARRAY}".
+     * Identify the element/item type of the given {@link SchemaKeyword#TAG_TYPE_ARRAY}.
      *
      * @param containerType type to extract type of element/item from
      * @return type of elements/items
diff --git a/src/main/java/com/github/victools/jsonschema/generator/TypeScope.java b/src/main/java/com/github/victools/jsonschema/generator/TypeScope.java
index 9812ec3..82b1172 100644
--- a/src/main/java/com/github/victools/jsonschema/generator/TypeScope.java
+++ b/src/main/java/com/github/victools/jsonschema/generator/TypeScope.java
@@ -71,7 +71,7 @@ public class TypeScope {
     }
 
     /**
-     * Determine whether this targeted type should be treated as "{@value SchemaConstants#TAG_TYPE_ARRAY}" in the generated schema.
+     * Determine whether this targeted type should be treated as {@link SchemaKeyword#TAG_TYPE_ARRAY} in the generated schema.
      * <br>
      * This is equivalent to calling: {@code scope.getContext().isContainerType(scope.getType())}
      *
@@ -83,7 +83,7 @@ public class TypeScope {
     }
 
     /**
-     * Identify the element/item type of the given "{@value SchemaConstants#TAG_TYPE_ARRAY}".
+     * Identify the element/item type of the given {@link SchemaKeyword#TAG_TYPE_ARRAY}.
      * <br>
      * This is equivalent to calling: {@code scope.getContext().getContainerItemType(scope.getType())}
      *
diff --git a/src/main/java/com/github/victools/jsonschema/generator/impl/AttributeCollector.java b/src/main/java/com/github/victools/jsonschema/generator/impl/AttributeCollector.java
index f92e180..0614508 100644
--- a/src/main/java/com/github/victools/jsonschema/generator/impl/AttributeCollector.java
+++ b/src/main/java/com/github/victools/jsonschema/generator/impl/AttributeCollector.java
@@ -23,8 +23,10 @@ import com.fasterxml.jackson.databind.node.ArrayNode;
 import com.fasterxml.jackson.databind.node.ObjectNode;
 import com.github.victools.jsonschema.generator.FieldScope;
 import com.github.victools.jsonschema.generator.MethodScope;
-import com.github.victools.jsonschema.generator.SchemaConstants;
+import com.github.victools.jsonschema.generator.SchemaGenerationContext;
 import com.github.victools.jsonschema.generator.SchemaGeneratorConfig;
+import com.github.victools.jsonschema.generator.SchemaKeyword;
+import com.github.victools.jsonschema.generator.SchemaVersion;
 import com.github.victools.jsonschema.generator.TypeScope;
 import java.lang.reflect.Type;
 import java.math.BigDecimal;
@@ -62,28 +64,28 @@ public class AttributeCollector {
      * @param generationContext generation context, including configuration to apply when looking-up attribute values
      * @return node holding all collected attributes (possibly empty)
      */
-    public static ObjectNode collectFieldAttributes(FieldScope field, SchemaGenerationContextImpl generationContext) {
+    public static ObjectNode collectFieldAttributes(FieldScope field, SchemaGenerationContext generationContext) {
         SchemaGeneratorConfig config = generationContext.getGeneratorConfig();
         ObjectNode node = config.createObjectNode();
         AttributeCollector collector = new AttributeCollector(config.getObjectMapper());
-        collector.setTitle(node, config.resolveTitle(field));
-        collector.setDescription(node, config.resolveDescription(field));
-        collector.setDefault(node, config.resolveDefault(field));
-        collector.setEnum(node, config.resolveEnum(field));
+        collector.setTitle(node, config.resolveTitle(field), generationContext);
+        collector.setDescription(node, config.resolveDescription(field), generationContext);
+        collector.setDefault(node, config.resolveDefault(field), generationContext);
+        collector.setEnum(node, config.resolveEnum(field), generationContext);
         collector.setAdditionalProperties(node, config.resolveAdditionalProperties(field), generationContext);
         collector.setPatternProperties(node, config.resolvePatternProperties(field), generationContext);
-        collector.setStringMinLength(node, config.resolveStringMinLength(field));
-        collector.setStringMaxLength(node, config.resolveStringMaxLength(field));
-        collector.setStringFormat(node, config.resolveStringFormat(field));
-        collector.setStringPattern(node, config.resolveStringPattern(field));
-        collector.setNumberInclusiveMinimum(node, config.resolveNumberInclusiveMinimum(field));
-        collector.setNumberExclusiveMinimum(node, config.resolveNumberExclusiveMinimum(field));
-        collector.setNumberInclusiveMaximum(node, config.resolveNumberInclusiveMaximum(field));
-        collector.setNumberExclusiveMaximum(node, config.resolveNumberExclusiveMaximum(field));
-        collector.setNumberMultipleOf(node, config.resolveNumberMultipleOf(field));
-        collector.setArrayMinItems(node, config.resolveArrayMinItems(field));
-        collector.setArrayMaxItems(node, config.resolveArrayMaxItems(field));
-        collector.setArrayUniqueItems(node, config.resolveArrayUniqueItems(field));
+        collector.setStringMinLength(node, config.resolveStringMinLength(field), generationContext);
+        collector.setStringMaxLength(node, config.resolveStringMaxLength(field), generationContext);
+        collector.setStringFormat(node, config.resolveStringFormat(field), generationContext);
+        collector.setStringPattern(node, config.resolveStringPattern(field), generationContext);
+        collector.setNumberInclusiveMinimum(node, config.resolveNumberInclusiveMinimum(field), generationContext);
+        collector.setNumberExclusiveMinimum(node, config.resolveNumberExclusiveMinimum(field), generationContext);
+        collector.setNumberInclusiveMaximum(node, config.resolveNumberInclusiveMaximum(field), generationContext);
+        collector.setNumberExclusiveMaximum(node, config.resolveNumberExclusiveMaximum(field), generationContext);
+        collector.setNumberMultipleOf(node, config.resolveNumberMultipleOf(field), generationContext);
+        collector.setArrayMinItems(node, config.resolveArrayMinItems(field), generationContext);
+        collector.setArrayMaxItems(node, config.resolveArrayMaxItems(field), generationContext);
+        collector.setArrayUniqueItems(node, config.resolveArrayUniqueItems(field), generationContext);
         config.getFieldAttributeOverrides()
                 .forEach(override -> override.overrideInstanceAttributes(node, field));
         return node;
@@ -96,28 +98,28 @@ public class AttributeCollector {
      * @param generationContext generation context, including configuration to apply when looking-up attribute values
      * @return node holding all collected attributes (possibly empty)
      */
-    public static ObjectNode collectMethodAttributes(MethodScope method, SchemaGenerationContextImpl generationContext) {
+    public static ObjectNode collectMethodAttributes(MethodScope method, SchemaGenerationContext generationContext) {
         SchemaGeneratorConfig config = generationContext.getGeneratorConfig();
         ObjectNode node = config.createObjectNode();
         AttributeCollector collector = new AttributeCollector(config.getObjectMapper());
-        collector.setTitle(node, config.resolveTitle(method));
-        collector.setDescription(node, config.resolveDescription(method));
-        collector.setDefault(node, config.resolveDefault(method));
-        collector.setEnum(node, config.resolveEnum(method));
+        collector.setTitle(node, config.resolveTitle(method), generationContext);
+        collector.setDescription(node, config.resolveDescription(method), generationContext);
+        collector.setDefault(node, config.resolveDefault(method), generationContext);
+        collector.setEnum(node, config.resolveEnum(method), generationContext);
         collector.setAdditionalProperties(node, config.resolveAdditionalProperties(method), generationContext);
         collector.setPatternProperties(node, config.resolvePatternProperties(method), generationContext);
-        collector.setStringMinLength(node, config.resolveStringMinLength(method));
-        collector.setStringMaxLength(node, config.resolveStringMaxLength(method));
-        collector.setStringFormat(node, config.resolveStringFormat(method));
-        collector.setStringPattern(node, config.resolveStringPattern(method));
-        collector.setNumberInclusiveMinimum(node, config.resolveNumberInclusiveMinimum(method));
-        collector.setNumberExclusiveMinimum(node, config.resolveNumberExclusiveMinimum(method));
-        collector.setNumberInclusiveMaximum(node, config.resolveNumberInclusiveMaximum(method));
-        collector.setNumberExclusiveMaximum(node, config.resolveNumberExclusiveMaximum(method));
-        collector.setNumberMultipleOf(node, config.resolveNumberMultipleOf(method));
-        collector.setArrayMinItems(node, config.resolveArrayMinItems(method));
-        collector.setArrayMaxItems(node, config.resolveArrayMaxItems(method));
-        collector.setArrayUniqueItems(node, config.resolveArrayUniqueItems(method));
+        collector.setStringMinLength(node, config.resolveStringMinLength(method), generationContext);
+        collector.setStringMaxLength(node, config.resolveStringMaxLength(method), generationContext);
+        collector.setStringFormat(node, config.resolveStringFormat(method), generationContext);
+        collector.setStringPattern(node, config.resolveStringPattern(method), generationContext);
+        collector.setNumberInclusiveMinimum(node, config.resolveNumberInclusiveMinimum(method), generationContext);
+        collector.setNumberExclusiveMinimum(node, config.resolveNumberExclusiveMinimum(method), generationContext);
+        collector.setNumberInclusiveMaximum(node, config.resolveNumberInclusiveMaximum(method), generationContext);
+        collector.setNumberExclusiveMaximum(node, config.resolveNumberExclusiveMaximum(method), generationContext);
+        collector.setNumberMultipleOf(node, config.resolveNumberMultipleOf(method), generationContext);
+        collector.setArrayMinItems(node, config.resolveArrayMinItems(method), generationContext);
+        collector.setArrayMaxItems(node, config.resolveArrayMaxItems(method), generationContext);
+        collector.setArrayUniqueItems(node, config.resolveArrayUniqueItems(method), generationContext);
         config.getMethodAttributeOverrides()
                 .forEach(override -> override.overrideInstanceAttributes(node, method));
         return node;
@@ -131,110 +133,184 @@ public class AttributeCollector {
      * @param allowedSchemaTypes declared schema types determining which attributes are meaningful to be included
      * @return node holding all collected attributes (possibly empty)
      */
-    public static ObjectNode collectTypeAttributes(TypeScope scope, SchemaGenerationContextImpl generationContext,
+    public static ObjectNode collectTypeAttributes(TypeScope scope, SchemaGenerationContext generationContext,
             Set<String> allowedSchemaTypes) {
         SchemaGeneratorConfig config = generationContext.getGeneratorConfig();
         ObjectNode node = config.createObjectNode();
         AttributeCollector collector = new AttributeCollector(config.getObjectMapper());
-        collector.setTitle(node, config.resolveTitleForType(scope));
-        collector.setDescription(node, config.resolveDescriptionForType(scope));
-        collector.setDefault(node, config.resolveDefaultForType(scope));
-        collector.setEnum(node, config.resolveEnumForType(scope));
-        if (allowedSchemaTypes.isEmpty() || allowedSchemaTypes.contains(SchemaConstants.TAG_TYPE_OBJECT)) {
+        collector.setTitle(node, config.resolveTitleForType(scope), generationContext);
+        collector.setDescription(node, config.resolveDescriptionForType(scope), generationContext);
+        collector.setDefault(node, config.resolveDefaultForType(scope), generationContext);
+        collector.setEnum(node, config.resolveEnumForType(scope), generationContext);
+        if (allowedSchemaTypes.isEmpty() || allowedSchemaTypes.contains(config.getKeyword(SchemaKeyword.TAG_TYPE_OBJECT))) {
             collector.setAdditionalProperties(node, config.resolveAdditionalPropertiesForType(scope), generationContext);
             collector.setPatternProperties(node, config.resolvePatternPropertiesForType(scope), generationContext);
         }
-        if (allowedSchemaTypes.isEmpty() || allowedSchemaTypes.contains(SchemaConstants.TAG_TYPE_STRING)) {
-            collector.setStringMinLength(node, config.resolveStringMinLengthForType(scope));
-            collector.setStringMaxLength(node, config.resolveStringMaxLengthForType(scope));
-            collector.setStringFormat(node, config.resolveStringFormatForType(scope));
-            collector.setStringPattern(node, config.resolveStringPatternForType(scope));
-        }
-        if (allowedSchemaTypes.isEmpty() || allowedSchemaTypes.contains(SchemaConstants.TAG_TYPE_INTEGER)
-                || allowedSchemaTypes.contains(SchemaConstants.TAG_TYPE_NUMBER)) {
-            collector.setNumberInclusiveMinimum(node, config.resolveNumberInclusiveMinimumForType(scope));
-            collector.setNumberExclusiveMinimum(node, config.resolveNumberExclusiveMinimumForType(scope));
-            collector.setNumberInclusiveMaximum(node, config.resolveNumberInclusiveMaximumForType(scope));
-            collector.setNumberExclusiveMaximum(node, config.resolveNumberExclusiveMaximumForType(scope));
-            collector.setNumberMultipleOf(node, config.resolveNumberMultipleOfForType(scope));
-        }
-        if (allowedSchemaTypes.isEmpty() || allowedSchemaTypes.contains(SchemaConstants.TAG_TYPE_ARRAY)) {
-            collector.setArrayMinItems(node, config.resolveArrayMinItemsForType(scope));
-            collector.setArrayMaxItems(node, config.resolveArrayMaxItemsForType(scope));
-            collector.setArrayUniqueItems(node, config.resolveArrayUniqueItemsForType(scope));
+        if (allowedSchemaTypes.isEmpty() || allowedSchemaTypes.contains(config.getKeyword(SchemaKeyword.TAG_TYPE_STRING))) {
+            collector.setStringMinLength(node, config.resolveStringMinLengthForType(scope), generationContext);
+            collector.setStringMaxLength(node, config.resolveStringMaxLengthForType(scope), generationContext);
+            collector.setStringFormat(node, config.resolveStringFormatForType(scope), generationContext);
+            collector.setStringPattern(node, config.resolveStringPatternForType(scope), generationContext);
+        }
+        if (allowedSchemaTypes.isEmpty() || allowedSchemaTypes.contains(config.getKeyword(SchemaKeyword.TAG_TYPE_INTEGER))
+                || allowedSchemaTypes.contains(config.getKeyword(SchemaKeyword.TAG_TYPE_NUMBER))) {
+            collector.setNumberInclusiveMinimum(node, config.resolveNumberInclusiveMinimumForType(scope), generationContext);
+            collector.setNumberExclusiveMinimum(node, config.resolveNumberExclusiveMinimumForType(scope), generationContext);
+            collector.setNumberInclusiveMaximum(node, config.resolveNumberInclusiveMaximumForType(scope), generationContext);
+            collector.setNumberExclusiveMaximum(node, config.resolveNumberExclusiveMaximumForType(scope), generationContext);
+            collector.setNumberMultipleOf(node, config.resolveNumberMultipleOfForType(scope), generationContext);
+        }
+        if (allowedSchemaTypes.isEmpty() || allowedSchemaTypes.contains(config.getKeyword(SchemaKeyword.TAG_TYPE_ARRAY))) {
+            collector.setArrayMinItems(node, config.resolveArrayMinItemsForType(scope), generationContext);
+            collector.setArrayMaxItems(node, config.resolveArrayMaxItemsForType(scope), generationContext);
+            collector.setArrayUniqueItems(node, config.resolveArrayUniqueItemsForType(scope), generationContext);
         }
         return node;
     }
 
     /**
-     * Setter for "{@value SchemaConstants#TAG_TITLE}" attribute.
+     * Setter for "{@link SchemaKeyword#TAG_TITLE}" attribute (considering Draft 7).
      *
      * @param node schema node to set attribute on
      * @param title attribute value to set
      * @return this instance (for chaining)
+     * @deprecated use {@link #setTitle(ObjectNode, String, SchemaGenerationContext)} instead
      */
+    @Deprecated
     public AttributeCollector setTitle(ObjectNode node, String title) {
         if (title != null) {
-            node.put(SchemaConstants.TAG_TITLE, title);
+            node.put(SchemaKeyword.TAG_TITLE.forVersion(SchemaVersion.DRAFT_7), title);
         }
         return this;
     }
 
     /**
-     * Setter for "{@value SchemaConstants#TAG_DESCRIPTION}" attribute.
+     * Setter for "{@link SchemaKeyword#TAG_TITLE}" attribute.
+     *
+     * @param node schema node to set attribute on
+     * @param title attribute value to set
+     * @param generationContext generation context, including configuration to apply when looking-up attribute values
+     * @return this instance (for chaining)
+     */
+    public AttributeCollector setTitle(ObjectNode node, String title, SchemaGenerationContext generationContext) {
+        if (title != null) {
+            node.put(generationContext.getKeyword(SchemaKeyword.TAG_TITLE), title);
+        }
+        return this;
+    }
+
+    /**
+     * Setter for "{@link SchemaKeyword#TAG_DESCRIPTION}" attribute (considering Draft 7).
      *
      * @param node schema node to set attribute on
      * @param description attribute value to set
      * @return this instance (for chaining)
+     * @deprecated use {@link #setDescription(ObjectNode, String, SchemaGenerationContext)} instead
      */
+    @Deprecated
     public AttributeCollector setDescription(ObjectNode node, String description) {
         if (description != null) {
-            node.put(SchemaConstants.TAG_DESCRIPTION, description);
+            node.put(SchemaKeyword.TAG_DESCRIPTION.forVersion(SchemaVersion.DRAFT_7), description);
         }
         return this;
     }
 
     /**
-     * Setter for "{@value SchemaConstants#TAG_DEFAULT}" attribute.
+     * Setter for "{@link SchemaKeyword#TAG_DESCRIPTION}" attribute.
+     *
+     * @param node schema node to set attribute on
+     * @param description attribute value to set
+     * @param generationContext generation context, including configuration to apply when looking-up attribute values
+     * @return this instance (for chaining)
+     */
+    public AttributeCollector setDescription(ObjectNode node, String description, SchemaGenerationContext generationContext) {
+        if (description != null) {
+            node.put(generationContext.getKeyword(SchemaKeyword.TAG_DESCRIPTION), description);
+        }
+        return this;
+    }
+
+    /**
+     * Setter for "{@link SchemaKeyword#TAG_DEFAULT}" attribute (considering Draft 7).
      *
      * @param node schema node to set attribute on
      * @param defaultValue attribute value to set
      * @return this instance (for chaining)
+     * @deprecated use {@link #setDefault(ObjectNode, Object, SchemaGenerationContext)} instead
      */
+    @Deprecated
     public AttributeCollector setDefault(ObjectNode node, Object defaultValue) {
         if (defaultValue != null) {
+            final String defaultTag = SchemaKeyword.TAG_DEFAULT.forVersion(SchemaVersion.DRAFT_7);
             // need to specifically add simple/primitive values by type
             if (defaultValue instanceof String) {
-                node.put(SchemaConstants.TAG_DEFAULT, (String) defaultValue);
+                node.put(defaultTag, (String) defaultValue);
             } else if (defaultValue instanceof BigDecimal) {
-                node.put(SchemaConstants.TAG_DEFAULT, (BigDecimal) defaultValue);
+                node.put(defaultTag, (BigDecimal) defaultValue);
             } else if (defaultValue instanceof BigInteger) {
-                node.put(SchemaConstants.TAG_DEFAULT, (BigInteger) defaultValue);
+                node.put(defaultTag, (BigInteger) defaultValue);
             } else if (defaultValue instanceof Boolean) {
-                node.put(SchemaConstants.TAG_DEFAULT, (Boolean) defaultValue);
+                node.put(defaultTag, (Boolean) defaultValue);
             } else if (defaultValue instanceof Double) {
-                node.put(SchemaConstants.TAG_DEFAULT, (Double) defaultValue);
+                node.put(defaultTag, (Double) defaultValue);
             } else if (defaultValue instanceof Float) {
-                node.put(SchemaConstants.TAG_DEFAULT, (Float) defaultValue);
+                node.put(defaultTag, (Float) defaultValue);
             } else if (defaultValue instanceof Integer) {
-                node.put(SchemaConstants.TAG_DEFAULT, (Integer) defaultValue);
+                node.put(defaultTag, (Integer) defaultValue);
             } else {
                 // everything else is simply forwarded as-is to the JSON Schema, it's up to the configurator to ensure the value's correctness
-                node.putPOJO(SchemaConstants.TAG_DEFAULT, defaultValue);
+                node.putPOJO(defaultTag, defaultValue);
             }
         }
         return this;
     }
 
     /**
-     * Setter for "{@value SchemaConstants#TAG_CONST}"/"{@value SchemaConstants#TAG_ENUM}" attribute.
+     * Setter for "{@link SchemaKeyword#TAG_DEFAULT}" attribute.
+     *
+     * @param node schema node to set attribute on
+     * @param defaultValue attribute value to set
+     * @param generationContext generation context, including configuration to apply when looking-up attribute values
+     * @return this instance (for chaining)
+     */
+    public AttributeCollector setDefault(ObjectNode node, Object defaultValue, SchemaGenerationContext generationContext) {
+        if (defaultValue != null) {
+            final String defaultTag = generationContext.getKeyword(SchemaKeyword.TAG_DEFAULT);
+            // need to specifically add simple/primitive values by type
+            if (defaultValue instanceof String) {
+                node.put(defaultTag, (String) defaultValue);
+            } else if (defaultValue instanceof BigDecimal) {
+                node.put(defaultTag, (BigDecimal) defaultValue);
+            } else if (defaultValue instanceof BigInteger) {
+                node.put(defaultTag, (BigInteger) defaultValue);
+            } else if (defaultValue instanceof Boolean) {
+                node.put(defaultTag, (Boolean) defaultValue);
+            } else if (defaultValue instanceof Double) {
+                node.put(defaultTag, (Double) defaultValue);
+            } else if (defaultValue instanceof Float) {
+                node.put(defaultTag, (Float) defaultValue);
+            } else if (defaultValue instanceof Integer) {
+                node.put(defaultTag, (Integer) defaultValue);
+            } else {
+                // everything else is simply forwarded as-is to the JSON Schema, it's up to the configurator to ensure the value's correctness
+                node.putPOJO(defaultTag, defaultValue);
+            }
+        }
+        return this;
+    }
+
+    /**
+     * Setter for "{@link SchemaKeyword#TAG_CONST}"/"{@link SchemaKeyword#TAG_ENUM}" attribute (considering Draft 7).
      *
      * @param node schema node to set attribute on
      * @param enumValues attribute value to set
      * @return this instance (for chaining)
+     * @deprecated use {@link #setEnum(ObjectNode, Collection, SchemaGenerationContext)} instead
      */
+    @Deprecated
     public AttributeCollector setEnum(ObjectNode node, Collection<?> enumValues) {
         if (enumValues != null) {
+            SchemaVersion schemaVersion = SchemaVersion.DRAFT_7;
             List<Object> values = enumValues.stream()
                     .filter(this::isSupportedEnumValue)
                     .filter(this::canBeConvertedToString)
@@ -242,9 +318,9 @@ public class AttributeCollector {
             if (values.size() == 1) {
                 Object singleValue = values.get(0);
                 if (singleValue instanceof String) {
-                    node.put(SchemaConstants.TAG_CONST, (String) singleValue);
+                    node.put(SchemaKeyword.TAG_CONST.forVersion(schemaVersion), (String) singleValue);
                 } else {
-                    node.putPOJO(SchemaConstants.TAG_CONST, singleValue);
+                    node.putPOJO(SchemaKeyword.TAG_CONST.forVersion(schemaVersion), singleValue);
                 }
             } else if (!values.isEmpty()) {
                 ArrayNode array = node.arrayNode();
@@ -255,14 +331,50 @@ public class AttributeCollector {
                         array.addPOJO(singleValue);
                     }
                 }
-                node.set(SchemaConstants.TAG_ENUM, array);
+                node.set(SchemaKeyword.TAG_ENUM.forVersion(schemaVersion), array);
             }
         }
         return this;
     }
 
     /**
-     * Check whether the given object may be included in a "{@value SchemaConstants#TAG_CONST}"/"{@value SchemaConstants#TAG_ENUM}" attribute.
+     * Setter for "{@link SchemaKeyword#TAG_CONST}"/"{@link SchemaKeyword#TAG_ENUM}" attribute.
+     *
+     * @param node schema node to set attribute on
+     * @param enumValues attribute value to set
+     * @param generationContext generation context, including configuration to apply when looking-up attribute values
+     * @return this instance (for chaining)
+     */
+    public AttributeCollector setEnum(ObjectNode node, Collection<?> enumValues, SchemaGenerationContext generationContext) {
+        if (enumValues != null) {
+            List<Object> values = enumValues.stream()
+                    .filter(this::isSupportedEnumValue)
+                    .filter(this::canBeConvertedToString)
+                    .collect(Collectors.toList());
+            if (values.size() == 1) {
+                Object singleValue = values.get(0);
+                if (singleValue instanceof String) {
+                    node.put(generationContext.getKeyword(SchemaKeyword.TAG_CONST), (String) singleValue);
+                } else {
+                    node.putPOJO(generationContext.getKeyword(SchemaKeyword.TAG_CONST), singleValue);
+                }
+            } else if (!values.isEmpty()) {
+                ArrayNode array = node.arrayNode();
+                for (Object singleValue : values) {
+                    if (singleValue instanceof String) {
+                        array.add((String) singleValue);
+                    } else {
+                        array.addPOJO(singleValue);
+                    }
+                }
+                node.set(generationContext.getKeyword(SchemaKeyword.TAG_ENUM), array);
+            }
+        }
+        return this;
+    }
+
+    /**
+     * Check whether the given object may be included in a "{@link SchemaKeyword#TAG_CONST}"/"{@link SchemaKeyword#TAG_ENUM}" attribute.
      *
      * @param target object to check
      * @return whether the given object may be included, otherwise it should be ignored
@@ -294,214 +406,417 @@ public class AttributeCollector {
     }
 
     /**
-     * Setter for "{@value SchemaConstants#TAG_ADDITIONAL_PROPERTIES}" attribute.
+     * Setter for "{@link SchemaKeyword#TAG_ADDITIONAL_PROPERTIES}" attribute.
      *
      * @param node schema node to set attribute on
      * @param additionalProperties attribute value to set
-     * @param generationContext generation context allowing for standard definitions to be included as attributes
+     * @param generationContext generation context, including configuration to apply when looking-up attribute values
      * @return this instance (for chaining)
      */
-    public AttributeCollector setAdditionalProperties(ObjectNode node, Type additionalProperties, SchemaGenerationContextImpl generationContext) {
+    public AttributeCollector setAdditionalProperties(ObjectNode node, Type additionalProperties, SchemaGenerationContext generationContext) {
         if (additionalProperties == Void.class || additionalProperties == Void.TYPE) {
-            node.put(SchemaConstants.TAG_ADDITIONAL_PROPERTIES, false);
+            node.put(generationContext.getKeyword(SchemaKeyword.TAG_ADDITIONAL_PROPERTIES), false);
         } else if (additionalProperties != null) {
             ResolvedType targetType = generationContext.getTypeContext().resolve(additionalProperties);
             if (targetType.getErasedType() != Object.class) {
-                ObjectNode additionalPropertiesSchema = this.objectMapper.createObjectNode();
-                generationContext.traverseGenericType(targetType, additionalPropertiesSchema, false);
-                node.set(SchemaConstants.TAG_ADDITIONAL_PROPERTIES, additionalPropertiesSchema);
+                ObjectNode additionalPropertiesSchema = generationContext.createDefinitionReference(targetType);
+                node.set(generationContext.getKeyword(SchemaKeyword.TAG_ADDITIONAL_PROPERTIES),
+                        additionalPropertiesSchema);
             }
         }
         return this;
     }
 
     /**
-     * Setter for "{@value SchemaConstants#TAG_PATTERN_PROPERTIES}" attribute.
+     * Setter for "{@link SchemaKeyword#TAG_PATTERN_PROPERTIES}" attribute.
      *
      * @param node schema node to set attribute on
      * @param patternProperties resolved attribute value to set
-     * @param generationContext generation context allowing for standard definitions to be included as attributes
+     * @param generationContext generation context, including configuration to apply when looking-up attribute values
      * @return this instance (for chaining)
      */
     public AttributeCollector setPatternProperties(ObjectNode node, Map<String, Type> patternProperties,
-            SchemaGenerationContextImpl generationContext) {
+            SchemaGenerationContext generationContext) {
         if (patternProperties != null && !patternProperties.isEmpty()) {
             ObjectNode patternPropertiesNode = this.objectMapper.createObjectNode();
             for (Map.Entry<String, Type> entry : patternProperties.entrySet()) {
-                ObjectNode singlePatternSchema = this.objectMapper.createObjectNode();
                 ResolvedType targetType = generationContext.getTypeContext().resolve(entry.getValue());
-                generationContext.traverseGenericType(targetType, singlePatternSchema, false);
+                ObjectNode singlePatternSchema = generationContext.createDefinitionReference(targetType);
                 patternPropertiesNode.set(entry.getKey(), singlePatternSchema);
             }
-            node.set(SchemaConstants.TAG_PATTERN_PROPERTIES, patternPropertiesNode);
+            node.set(generationContext.getKeyword(SchemaKeyword.TAG_PATTERN_PROPERTIES), patternPropertiesNode);
         }
         return this;
     }
 
     /**
-     * Setter for "{@value SchemaConstants#TAG_LENGTH_MIN}" attribute.
+     * Setter for "{@link SchemaKeyword#TAG_LENGTH_MIN}" attribute (considering Draft 7).
      *
      * @param node schema node to set attribute on
      * @param minLength attribute value to set
      * @return this instance (for chaining)
+     * @deprecated use {@link #setStringMinLength(ObjectNode, Integer, SchemaGenerationContext)} instead
      */
+    @Deprecated
     public AttributeCollector setStringMinLength(ObjectNode node, Integer minLength) {
         if (minLength != null) {
-            node.put(SchemaConstants.TAG_LENGTH_MIN, minLength);
+            node.put(SchemaKeyword.TAG_LENGTH_MIN.forVersion(SchemaVersion.DRAFT_7), minLength);
+        }
+        return this;
+    }
+
+    /**
+     * Setter for "{@link SchemaKeyword#TAG_LENGTH_MIN}" attribute (considering Draft 7).
+     *
+     * @param node schema node to set attribute on
+     * @param minLength attribute value to set
+     * @param generationContext generation context, including configuration to apply when looking-up attribute values
+     * @return this instance (for chaining)
+     */
+    public AttributeCollector setStringMinLength(ObjectNode node, Integer minLength, SchemaGenerationContext generationContext) {
+        if (minLength != null) {
+            node.put(generationContext.getKeyword(SchemaKeyword.TAG_LENGTH_MIN), minLength);
         }
         return this;
     }
 
     /**
-     * Setter for "{@value SchemaConstants#TAG_LENGTH_MAX}" attribute.
+     * Setter for "{@link SchemaKeyword#TAG_LENGTH_MAX}" attribute (considering Draft 7).
      *
      * @param node schema node to set attribute on
      * @param maxLength attribute value to set
      * @return this instance (for chaining)
+     * @deprecated use {@link #setStringMaxLength(ObjectNode, Integer, SchemaGenerationContext)} instead
      */
+    @Deprecated
     public AttributeCollector setStringMaxLength(ObjectNode node, Integer maxLength) {
         if (maxLength != null) {
-            node.put(SchemaConstants.TAG_LENGTH_MAX, maxLength);
+            node.put(SchemaKeyword.TAG_LENGTH_MAX.forVersion(SchemaVersion.DRAFT_7), maxLength);
+        }
+        return this;
+    }
+
+    /**
+     * Setter for "{@link SchemaKeyword#TAG_LENGTH_MAX}" attribute (considering Draft 7).
+     *
+     * @param node schema node to set attribute on
+     * @param maxLength attribute value to set
+     * @param generationContext generation context, including configuration to apply when looking-up attribute values
+     * @return this instance (for chaining)
+     */
+    public AttributeCollector setStringMaxLength(ObjectNode node, Integer maxLength, SchemaGenerationContext generationContext) {
+        if (maxLength != null) {
+            node.put(generationContext.getKeyword(SchemaKeyword.TAG_LENGTH_MAX), maxLength);
         }
         return this;
     }
 
     /**
-     * Setter for "{@value SchemaConstants#TAG_FORMAT}" attribute.
+     * Setter for "{@link SchemaKeyword#TAG_FORMAT}" attribute (considering Draft 7).
      *
      * @param node schema node to set attribute on
      * @param format attribute value to set
      * @return this instance (for chaining)
+     * @deprecated use {@link #setStringFormat(ObjectNode, String, SchemaGenerationContext)} instead
      */
+    @Deprecated
     public AttributeCollector setStringFormat(ObjectNode node, String format) {
         if (format != null) {
-            node.put(SchemaConstants.TAG_FORMAT, format);
+            node.put(SchemaKeyword.TAG_FORMAT.forVersion(SchemaVersion.DRAFT_7), format);
+        }
+        return this;
+    }
+
+    /**
+     * Setter for "{@link SchemaKeyword#TAG_FORMAT}" attribute (considering Draft 7).
+     *
+     * @param node schema node to set attribute on
+     * @param format attribute value to set
+     * @param generationContext generation context, including configuration to apply when looking-up attribute values
+     * @return this instance (for chaining)
+     */
+    public AttributeCollector setStringFormat(ObjectNode node, String format, SchemaGenerationContext generationContext) {
+        if (format != null) {
+            node.put(generationContext.getKeyword(SchemaKeyword.TAG_FORMAT), format);
         }
         return this;
     }
 
     /**
-     * Setter for "{@value SchemaConstants#TAG_PATTERN}" attribute.
+     * Setter for "{@link SchemaKeyword#TAG_PATTERN}" attribute (considering Draft 7).
      *
      * @param node schema node to set attribute on
      * @param pattern attribute value to set
      * @return this instance (for chaining)
+     * @deprecated use {@link #setStringPattern(ObjectNode, String, SchemaGenerationContext)} instead
      */
+    @Deprecated
     public AttributeCollector setStringPattern(ObjectNode node, String pattern) {
         if (pattern != null) {
-            node.put(SchemaConstants.TAG_PATTERN, pattern);
+            node.put(SchemaKeyword.TAG_PATTERN.forVersion(SchemaVersion.DRAFT_7), pattern);
         }
         return this;
     }
 
     /**
-     * Setter for "{@value SchemaConstants#TAG_MINIMUM}" attribute.
+     * Setter for "{@link SchemaKeyword#TAG_PATTERN}" attribute (considering Draft 7).
+     *
+     * @param node schema node to set attribute on
+     * @param pattern attribute value to set
+     * @param generationContext generation context, including configuration to apply when looking-up attribute values
+     * @return this instance (for chaining)
+     */
+    public AttributeCollector setStringPattern(ObjectNode node, String pattern, SchemaGenerationContext generationContext) {
+        if (pattern != null) {
+            node.put(generationContext.getKeyword(SchemaKeyword.TAG_PATTERN), pattern);
+        }
+        return this;
+    }
+
+    /**
+     * Setter for "{@link SchemaKeyword#TAG_MINIMUM}" attribute (considering Draft 7).
      *
      * @param node schema node to set attribute on
      * @param inclusiveMinimum attribute value to set
      * @return this instance (for chaining)
+     * @deprecated use {@link #setNumberInclusiveMinimum(ObjectNode, BigDecimal, SchemaGenerationContext)} instead
      */
+    @Deprecated
     public AttributeCollector setNumberInclusiveMinimum(ObjectNode node, BigDecimal inclusiveMinimum) {
         if (inclusiveMinimum != null) {
-            node.put(SchemaConstants.TAG_MINIMUM, inclusiveMinimum);
+            node.put(SchemaKeyword.TAG_MINIMUM.forVersion(SchemaVersion.DRAFT_7), inclusiveMinimum);
+        }
+        return this;
+    }
+
+    /**
+     * Setter for "{@link SchemaKeyword#TAG_MINIMUM}" attribute (considering Draft 7).
+     *
+     * @param node schema node to set attribute on
+     * @param inclusiveMinimum attribute value to set
+     * @param generationContext generation context, including configuration to apply when looking-up attribute values
+     * @return this instance (for chaining)
+     */
+    public AttributeCollector setNumberInclusiveMinimum(ObjectNode node, BigDecimal inclusiveMinimum, SchemaGenerationContext generationContext) {
+        if (inclusiveMinimum != null) {
+            node.put(generationContext.getKeyword(SchemaKeyword.TAG_MINIMUM), inclusiveMinimum);
         }
         return this;
     }
 
     /**
-     * Setter for "{@value SchemaConstants#TAG_MINIMUM_EXCLUSIVE}" attribute.
+     * Setter for "{@link SchemaKeyword#TAG_MINIMUM_EXCLUSIVE}" attribute (considering Draft 7).
      *
      * @param node schema node to set attribute on
      * @param exclusiveMinimum attribute value to set
      * @return this instance (for chaining)
+     * @deprecated user {@link #setNumberExclusiveMinimum(ObjectNode, BigDecimal, SchemaGenerationContext)} instead
      */
+    @Deprecated
     public AttributeCollector setNumberExclusiveMinimum(ObjectNode node, BigDecimal exclusiveMinimum) {
         if (exclusiveMinimum != null) {
-            node.put(SchemaConstants.TAG_MINIMUM_EXCLUSIVE, exclusiveMinimum);
+            node.put(SchemaKeyword.TAG_MINIMUM_EXCLUSIVE.forVersion(SchemaVersion.DRAFT_7), exclusiveMinimum);
         }
         return this;
     }
 
     /**
-     * Setter for "{@value SchemaConstants#TAG_MAXIMUM}" attribute.
+     * Setter for "{@link SchemaKeyword#TAG_MINIMUM_EXCLUSIVE}" attribute (considering Draft 7).
+     *
+     * @param node schema node to set attribute on
+     * @param exclusiveMinimum attribute value to set
+     * @param generationContext generation context, including configuration to apply when looking-up attribute values
+     * @return this instance (for chaining)
+     */
+    public AttributeCollector setNumberExclusiveMinimum(ObjectNode node, BigDecimal exclusiveMinimum, SchemaGenerationContext generationContext) {
+        if (exclusiveMinimum != null) {
+            node.put(generationContext.getKeyword(SchemaKeyword.TAG_MINIMUM_EXCLUSIVE), exclusiveMinimum);
+        }
+        return this;
+    }
+
+    /**
+     * Setter for "{@link SchemaKeyword#TAG_MAXIMUM}" attribute (considering Draft 7).
      *
      * @param node schema node to set attribute on
      * @param inclusiveMaximum attribute value to set
      * @return this instance (for chaining)
+     * @deprecated use {@link #setNumberInclusiveMaximum(ObjectNode, BigDecimal, SchemaGenerationContext)} instead
      */
+    @Deprecated
     public AttributeCollector setNumberInclusiveMaximum(ObjectNode node, BigDecimal inclusiveMaximum) {
         if (inclusiveMaximum != null) {
-            node.put(SchemaConstants.TAG_MAXIMUM, inclusiveMaximum);
+            node.put(SchemaKeyword.TAG_MAXIMUM.forVersion(SchemaVersion.DRAFT_7), inclusiveMaximum);
+        }
+        return this;
+    }
+
+    /**
+     * Setter for "{@link SchemaKeyword#TAG_MAXIMUM}" attribute (considering Draft 7).
+     *
+     * @param node schema node to set attribute on
+     * @param inclusiveMaximum attribute value to set
+     * @param generationContext generation context, including configuration to apply when looking-up attribute values
+     * @return this instance (for chaining)
+     */
+    public AttributeCollector setNumberInclusiveMaximum(ObjectNode node, BigDecimal inclusiveMaximum, SchemaGenerationContext generationContext) {
+        if (inclusiveMaximum != null) {
+            node.put(generationContext.getKeyword(SchemaKeyword.TAG_MAXIMUM), inclusiveMaximum);
         }
         return this;
     }
 
     /**
-     * Setter for "{@value SchemaConstants#TAG_MAXIMUM_EXCLUSIVE}" attribute.
+     * Setter for "{@link SchemaKeyword#TAG_MAXIMUM_EXCLUSIVE}" attribute (considering Draft 7).
      *
      * @param node schema node to set attribute on
      * @param exclusiveMaximum attribute value to set
      * @return this instance (for chaining)
+     * @deprecated use {@link #setNumberExclusiveMaximum(ObjectNode, BigDecimal, SchemaGenerationContext)} instead
      */
+    @Deprecated
     public AttributeCollector setNumberExclusiveMaximum(ObjectNode node, BigDecimal exclusiveMaximum) {
         if (exclusiveMaximum != null) {
-            node.put(SchemaConstants.TAG_MAXIMUM_EXCLUSIVE, exclusiveMaximum);
+            node.put(SchemaKeyword.TAG_MAXIMUM_EXCLUSIVE.forVersion(SchemaVersion.DRAFT_7), exclusiveMaximum);
         }
         return this;
     }
 
     /**
-     * Setter for "{@value SchemaConstants#TAG_MULTIPLE_OF}" attribute.
+     * Setter for "{@link SchemaKeyword#TAG_MAXIMUM_EXCLUSIVE}" attribute.
+     *
+     * @param node schema node to set attribute on
+     * @param exclusiveMaximum attribute value to set
+     * @param generationContext generation context, including configuration to apply when looking-up attribute values
+     * @return this instance (for chaining)
+     */
+    public AttributeCollector setNumberExclusiveMaximum(ObjectNode node, BigDecimal exclusiveMaximum, SchemaGenerationContext generationContext) {
+        if (exclusiveMaximum != null) {
+            node.put(generationContext.getKeyword(SchemaKeyword.TAG_MAXIMUM_EXCLUSIVE), exclusiveMaximum);
+        }
+        return this;
+    }
+
+    /**
+     * Setter for "{@link SchemaKeyword#TAG_MULTIPLE_OF}" attribute (considering Draft 7).
      *
      * @param node schema node to set attribute on
      * @param multipleOf attribute value to set
      * @return this instance (for chaining)
+     * @deprecated use {@link #setNumberMultipleOf(ObjectNode, BigDecimal, SchemaGenerationContext)} instead
      */
+    @Deprecated
     public AttributeCollector setNumberMultipleOf(ObjectNode node, BigDecimal multipleOf) {
         if (multipleOf != null) {
-            node.put(SchemaConstants.TAG_MULTIPLE_OF, multipleOf);
+            node.put(SchemaKeyword.TAG_MULTIPLE_OF.forVersion(SchemaVersion.DRAFT_7), multipleOf);
+        }
+        return this;
+    }
+
+    /**
+     * Setter for "{@link SchemaKeyword#TAG_MULTIPLE_OF}" attribute.
+     *
+     * @param node schema node to set attribute on
+     * @param multipleOf attribute value to set
+     * @param generationContext generation context, including configuration to apply when looking-up attribute values
+     * @return this instance (for chaining)
+     */
+    public AttributeCollector setNumberMultipleOf(ObjectNode node, BigDecimal multipleOf, SchemaGenerationContext generationContext) {
+        if (multipleOf != null) {
+            node.put(generationContext.getKeyword(SchemaKeyword.TAG_MULTIPLE_OF), multipleOf);
         }
         return this;
     }
 
     /**
-     * Setter for "{@value SchemaConstants#TAG_ITEMS_MIN}" attribute.
+     * Setter for "{@link SchemaKeyword#TAG_ITEMS_MIN}" attribute (considering Draft 7).
      *
      * @param node schema node to set attribute on
      * @param minItemCount attribute value to set
      * @return this instance (for chaining)
+     * @deprecated use {@link #setArrayMinItems(ObjectNode, Integer, SchemaGenerationContext)} instead
      */
+    @Deprecated
     public AttributeCollector setArrayMinItems(ObjectNode node, Integer minItemCount) {
         if (minItemCount != null) {
-            node.put(SchemaConstants.TAG_ITEMS_MIN, minItemCount);
+            node.put(SchemaKeyword.TAG_ITEMS_MIN.forVersion(SchemaVersion.DRAFT_7), minItemCount);
+        }
+        return this;
+    }
+
+    /**
+     * Setter for "{@link SchemaKeyword#TAG_ITEMS_MIN}" attribute.
+     *
+     * @param node schema node to set attribute on
+     * @param minItemCount attribute value to set
+     * @param generationContext generation context, including configuration to apply when looking-up attribute values
+     * @return this instance (for chaining)
+     */
+    public AttributeCollector setArrayMinItems(ObjectNode node, Integer minItemCount, SchemaGenerationContext generationContext) {
+        if (minItemCount != null) {
+            node.put(generationContext.getKeyword(SchemaKeyword.TAG_ITEMS_MIN), minItemCount);
         }
         return this;
     }
 
     /**
-     * Setter for "{@value SchemaConstants#TAG_ITEMS_MAX}" attribute.
+     * Setter for "{@link SchemaKeyword#TAG_ITEMS_MAX}" attribute (considering Draft 7).
      *
      * @param node schema node to set attribute on
      * @param maxItemCount attribute value to set
      * @return this instance (for chaining)
+     * @deprecated use {@link #setArrayMaxItems(ObjectNode, Integer, SchemaGenerationContext)} instead
      */
+    @Deprecated
     public AttributeCollector setArrayMaxItems(ObjectNode node, Integer maxItemCount) {
         if (maxItemCount != null) {
-            node.put(SchemaConstants.TAG_ITEMS_MAX, maxItemCount);
+            node.put(SchemaKeyword.TAG_ITEMS_MAX.forVersion(SchemaVersion.DRAFT_7), maxItemCount);
+        }
+        return this;
+    }
+
+    /**
+     * Setter for "{@link SchemaKeyword#TAG_ITEMS_MAX}" attribute.
+     *
+     * @param node schema node to set attribute on
+     * @param maxItemCount attribute value to set
+     * @param generationContext generation context, including configuration to apply when looking-up attribute values
+     * @return this instance (for chaining)
+     */
+    public AttributeCollector setArrayMaxItems(ObjectNode node, Integer maxItemCount, SchemaGenerationContext generationContext) {
+        if (maxItemCount != null) {
+            node.put(generationContext.getKeyword(SchemaKeyword.TAG_ITEMS_MAX), maxItemCount);
         }
         return this;
     }
 
     /**
-     * Setter for "{@value SchemaConstants#TAG_ITEMS_UNIQUE}" attribute.
+     * Setter for "{@link SchemaKeyword#TAG_ITEMS_UNIQUE}" attribute (considering Draft 7).
      *
      * @param node schema node to set attribute on
      * @param uniqueItems attribute value to set
      * @return this instance (for chaining)
+     * @deprecated use {@link #setArrayUniqueItems(ObjectNode, Boolean, SchemaGenerationContext)} instead
      */
+    @Deprecated
     public AttributeCollector setArrayUniqueItems(ObjectNode node, Boolean uniqueItems) {
         if (uniqueItems != null) {
-            node.put(SchemaConstants.TAG_ITEMS_UNIQUE, uniqueItems);
+            node.put(SchemaKeyword.TAG_ITEMS_UNIQUE.forVersion(SchemaVersion.DRAFT_7), uniqueItems);
+        }
+        return this;
+    }
+
+    /**
+     * Setter for "{@link SchemaKeyword#TAG_ITEMS_UNIQUE}" attribute.
+     *
+     * @param node schema node to set attribute on
+     * @param uniqueItems attribute value to set
+     * @param generationContext generation context, including configuration to apply when looking-up attribute values
+     * @return this instance (for chaining)
+     */
+    public AttributeCollector setArrayUniqueItems(ObjectNode node, Boolean uniqueItems, SchemaGenerationContext generationContext) {
+        if (uniqueItems != null) {
+            node.put(generationContext.getKeyword(SchemaKeyword.TAG_ITEMS_UNIQUE), uniqueItems);
         }
         return this;
     }
diff --git a/src/main/java/com/github/victools/jsonschema/generator/impl/SchemaGenerationContextImpl.java b/src/main/java/com/github/victools/jsonschema/generator/impl/SchemaGenerationContextImpl.java
index a1d9e33..1855764 100644
--- a/src/main/java/com/github/victools/jsonschema/generator/impl/SchemaGenerationContextImpl.java
+++ b/src/main/java/com/github/victools/jsonschema/generator/impl/SchemaGenerationContextImpl.java
@@ -30,9 +30,9 @@ import com.github.victools.jsonschema.generator.CustomDefinition;
 import com.github.victools.jsonschema.generator.CustomDefinitionProviderV2;
 import com.github.victools.jsonschema.generator.FieldScope;
 import com.github.victools.jsonschema.generator.MethodScope;
-import com.github.victools.jsonschema.generator.SchemaConstants;
 import com.github.victools.jsonschema.generator.SchemaGenerationContext;
 import com.github.victools.jsonschema.generator.SchemaGeneratorConfig;
+import com.github.victools.jsonschema.generator.SchemaKeyword;
 import com.github.victools.jsonschema.generator.TypeContext;
 import com.github.victools.jsonschema.generator.TypeScope;
 import java.util.ArrayList;
@@ -308,7 +308,7 @@ public class SchemaGenerationContextImpl implements SchemaGenerationContext {
                 this.traverseGenericType(subtype, subtypeSchema, false);
                 anyOfArrayNode.add(subtypeSchema);
             }
-            definition.set(SchemaConstants.TAG_ANYOF, anyOfArrayNode);
+            definition.set(this.getKeyword(SchemaKeyword.TAG_ANYOF), anyOfArrayNode);
         }
         return true;
     }
@@ -320,7 +320,7 @@ public class SchemaGenerationContextImpl implements SchemaGenerationContext {
      * @return extracted "{@value SchemaConstants#TAG_TYPE}" values (may be empty)
      */
     private Set<String> collectAllowedSchemaTypes(ObjectNode definition) {
-        JsonNode declaredTypes = definition.get(SchemaConstants.TAG_TYPE);
+        JsonNode declaredTypes = definition.get(this.getKeyword(SchemaKeyword.TAG_TYPE));
         final Set<String> allowedSchemaTypes;
         if (declaredTypes == null) {
             allowedSchemaTypes = Collections.emptySet();
@@ -343,13 +343,15 @@ public class SchemaGenerationContextImpl implements SchemaGenerationContext {
      */
     private void generateArrayDefinition(ResolvedType targetType, ObjectNode definition, boolean isNullable) {
         if (isNullable) {
-            definition.set(SchemaConstants.TAG_TYPE,
-                    this.generatorConfig.createArrayNode().add(SchemaConstants.TAG_TYPE_ARRAY).add(SchemaConstants.TAG_TYPE_NULL));
+            ArrayNode typeArray = this.generatorConfig.createArrayNode()
+                    .add(this.getKeyword(SchemaKeyword.TAG_TYPE_ARRAY))
+                    .add(this.getKeyword(SchemaKeyword.TAG_TYPE_NULL));
+            definition.set(this.getKeyword(SchemaKeyword.TAG_TYPE), typeArray);
         } else {
-            definition.put(SchemaConstants.TAG_TYPE, SchemaConstants.TAG_TYPE_ARRAY);
+            definition.put(this.getKeyword(SchemaKeyword.TAG_TYPE), this.getKeyword(SchemaKeyword.TAG_TYPE_ARRAY));
         }
         ObjectNode arrayItemTypeRef = this.generatorConfig.createObjectNode();
-        definition.set(SchemaConstants.TAG_ITEMS, arrayItemTypeRef);
+        definition.set(this.getKeyword(SchemaKeyword.TAG_ITEMS), arrayItemTypeRef);
         ResolvedType itemType = this.typeContext.getContainerItemType(targetType);
         this.traverseGenericType(itemType, arrayItemTypeRef, false);
     }
@@ -361,7 +363,7 @@ public class SchemaGenerationContextImpl implements SchemaGenerationContext {
      * @param definition node in the JSON schema to which all collected attributes should be added
      */
     private void generateObjectDefinition(ResolvedType targetType, ObjectNode definition) {
-        definition.put(SchemaConstants.TAG_TYPE, SchemaConstants.TAG_TYPE_OBJECT);
+        definition.put(this.getKeyword(SchemaKeyword.TAG_TYPE), this.getKeyword(SchemaKeyword.TAG_TYPE_OBJECT));
 
         final Map<String, JsonNode> targetFields = new TreeMap<>();
         final Map<String, JsonNode> targetMethods = new TreeMap<>();
@@ -373,12 +375,12 @@ public class SchemaGenerationContextImpl implements SchemaGenerationContext {
             ObjectNode propertiesNode = this.generatorConfig.createObjectNode();
             propertiesNode.setAll(targetFields);
             propertiesNode.setAll(targetMethods);
-            definition.set(SchemaConstants.TAG_PROPERTIES, propertiesNode);
+            definition.set(this.getKeyword(SchemaKeyword.TAG_PROPERTIES), propertiesNode);
 
             if (!requiredProperties.isEmpty()) {
                 ArrayNode requiredNode = this.generatorConfig.createArrayNode();
                 requiredProperties.forEach(requiredNode::add);
-                definition.set(SchemaConstants.TAG_REQUIRED, requiredNode);
+                definition.set(this.getKeyword(SchemaKeyword.TAG_REQUIRED), requiredNode);
             }
         }
     }
@@ -560,7 +562,7 @@ public class SchemaGenerationContextImpl implements SchemaGenerationContext {
             } else {
                 // avoid mixing potential "$ref" element with contextual attributes by introducing an "allOf" wrapper
                 referenceContainer = this.generatorConfig.createObjectNode();
-                targetNode.set(SchemaConstants.TAG_ALLOF, this.generatorConfig.createArrayNode()
+                targetNode.set(this.getKeyword(SchemaKeyword.TAG_ALLOF), this.generatorConfig.createArrayNode()
                         .add(referenceContainer)
                         .add(collectedAttributes));
             }
@@ -575,12 +577,13 @@ public class SchemaGenerationContextImpl implements SchemaGenerationContext {
 
     @Override
     public ObjectNode makeNullable(ObjectNode node) {
-        if (node.has(SchemaConstants.TAG_REF)
-                || node.has(SchemaConstants.TAG_ALLOF)
-                || node.has(SchemaConstants.TAG_ANYOF)
-                || node.has(SchemaConstants.TAG_ONEOF)) {
+        if (node.has(this.getKeyword(SchemaKeyword.TAG_REF))
+                || node.has(this.getKeyword(SchemaKeyword.TAG_ALLOF))
+                || node.has(this.getKeyword(SchemaKeyword.TAG_ANYOF))
+                || node.has(this.getKeyword(SchemaKeyword.TAG_ONEOF))) {
             // cannot be sure what is specified in those other schema parts, instead simply create a oneOf wrapper
-            ObjectNode nullSchema = this.generatorConfig.createObjectNode().put(SchemaConstants.TAG_TYPE, SchemaConstants.TAG_TYPE_NULL);
+            ObjectNode nullSchema = this.generatorConfig.createObjectNode()
+                    .put(this.getKeyword(SchemaKeyword.TAG_TYPE), this.getKeyword(SchemaKeyword.TAG_TYPE_NULL));
             ArrayNode oneOf = this.generatorConfig.createArrayNode()
                     // one option in the oneOf should be null
                     .add(nullSchema)
@@ -588,31 +591,37 @@ public class SchemaGenerationContextImpl implements SchemaGenerationContext {
                     .add(this.generatorConfig.createObjectNode().setAll(node));
             // replace all existing (and already copied properties with the oneOf wrapper
             node.removeAll();
-            node.set(SchemaConstants.TAG_ONEOF, oneOf);
+            node.set(this.getKeyword(SchemaKeyword.TAG_ONEOF), oneOf);
         } else {
             // given node is a simple schema, we can simply adjust its "type" attribute
-            JsonNode fixedJsonSchemaType = node.get(SchemaConstants.TAG_TYPE);
+            JsonNode fixedJsonSchemaType = node.get(this.getKeyword(SchemaKeyword.TAG_TYPE));
             if (fixedJsonSchemaType instanceof ArrayNode) {
                 // there are already multiple "type" values
                 ArrayNode arrayOfTypes = (ArrayNode) fixedJsonSchemaType;
                 // one of the existing "type" values could be null
                 boolean alreadyContainsNull = false;
                 for (JsonNode arrayEntry : arrayOfTypes) {
-                    alreadyContainsNull = alreadyContainsNull || SchemaConstants.TAG_TYPE_NULL.equals(arrayEntry.textValue());
+                    alreadyContainsNull = alreadyContainsNull || this.getKeyword(SchemaKeyword.TAG_TYPE_NULL).equals(arrayEntry.textValue());
                 }
 
                 if (!alreadyContainsNull) {
                     // null "type" was not mentioned before, we simply add it to the existing list
-                    arrayOfTypes.add(SchemaConstants.TAG_TYPE_NULL);
+                    arrayOfTypes.add(this.getKeyword(SchemaKeyword.TAG_TYPE_NULL));
                 }
-            } else if (fixedJsonSchemaType instanceof TextNode && !SchemaConstants.TAG_TYPE_NULL.equals(fixedJsonSchemaType.textValue())) {
+            } else if (fixedJsonSchemaType instanceof TextNode
+                    && !this.getKeyword(SchemaKeyword.TAG_TYPE_NULL).equals(fixedJsonSchemaType.textValue())) {
                 // add null as second "type" option
-                node.replace(SchemaConstants.TAG_TYPE, this.generatorConfig.createArrayNode()
+                node.replace(this.getKeyword(SchemaKeyword.TAG_TYPE), this.generatorConfig.createArrayNode()
                         .add(fixedJsonSchemaType)
-                        .add(SchemaConstants.TAG_TYPE_NULL));
+                        .add(this.getKeyword(SchemaKeyword.TAG_TYPE_NULL)));
             }
             // if no "type" is specified, null is allowed already
         }
         return node;
     }
+
+    @Override
+    public String getKeyword(SchemaKeyword keyword) {
+        return this.generatorConfig.getKeyword(keyword);
+    }
 }
diff --git a/src/main/java/com/github/victools/jsonschema/generator/impl/SchemaGeneratorConfigImpl.java b/src/main/java/com/github/victools/jsonschema/generator/impl/SchemaGeneratorConfigImpl.java
index 8501c18..68b9946 100644
--- a/src/main/java/com/github/victools/jsonschema/generator/impl/SchemaGeneratorConfigImpl.java
+++ b/src/main/java/com/github/victools/jsonschema/generator/impl/SchemaGeneratorConfigImpl.java
@@ -30,6 +30,8 @@ import com.github.victools.jsonschema.generator.SchemaGenerationContext;
 import com.github.victools.jsonschema.generator.SchemaGeneratorConfig;
 import com.github.victools.jsonschema.generator.SchemaGeneratorConfigPart;
 import com.github.victools.jsonschema.generator.SchemaGeneratorGeneralConfigPart;
+import com.github.victools.jsonschema.generator.SchemaKeyword;
+import com.github.victools.jsonschema.generator.SchemaVersion;
 import com.github.victools.jsonschema.generator.TypeAttributeOverride;
 import com.github.victools.jsonschema.generator.TypeScope;
 import java.lang.reflect.Type;
@@ -48,6 +50,7 @@ import java.util.Set;
 public class SchemaGeneratorConfigImpl implements SchemaGeneratorConfig {
 
     private final ObjectMapper objectMapper;
+    private final SchemaVersion schemaVersion;
     private final Set<Option> enabledOptions;
     private final SchemaGeneratorGeneralConfigPart typesInGeneralConfigPart;
     private final SchemaGeneratorConfigPart<FieldScope> fieldConfigPart;
@@ -57,17 +60,20 @@ public class SchemaGeneratorConfigImpl implements SchemaGeneratorConfig {
      * Constructor of a configuration instance.
      *
      * @param objectMapper supplier for object and array nodes for the JSON structure being generated
+     * @param schemaVersion  designated JSON Schema version
      * @param enabledOptions enabled settings/options (either by default or explicitly set)
      * @param typesInGeneralConfigPart configuration part for context-independent attribute collection
      * @param fieldConfigPart configuration part for fields
      * @param methodConfigPart configuration part for methods
      */
     public SchemaGeneratorConfigImpl(ObjectMapper objectMapper,
+            SchemaVersion schemaVersion,
             Set<Option> enabledOptions,
             SchemaGeneratorGeneralConfigPart typesInGeneralConfigPart,
             SchemaGeneratorConfigPart<FieldScope> fieldConfigPart,
             SchemaGeneratorConfigPart<MethodScope> methodConfigPart) {
         this.objectMapper = objectMapper;
+        this.schemaVersion = schemaVersion;
         this.enabledOptions = enabledOptions;
         this.typesInGeneralConfigPart = typesInGeneralConfigPart;
         this.fieldConfigPart = fieldConfigPart;
@@ -84,6 +90,16 @@ public class SchemaGeneratorConfigImpl implements SchemaGeneratorConfig {
         return this.enabledOptions.contains(setting);
     }
 
+    @Override
+    public SchemaVersion getSchemaVersion() {
+        return this.schemaVersion;
+    }
+
+    @Override
+    public String getKeyword(SchemaKeyword keyword) {
+        return keyword.forVersion(this.getSchemaVersion());
+    }
+
     @Override
     public boolean shouldCreateDefinitionsForAllObjects() {
         return this.isOptionEnabled(Option.DEFINITIONS_FOR_ALL_OBJECTS);
diff --git a/src/main/java/com/github/victools/jsonschema/generator/impl/module/EnumModule.java b/src/main/java/com/github/victools/jsonschema/generator/impl/module/EnumModule.java
index 605fa47..9912338 100644
--- a/src/main/java/com/github/victools/jsonschema/generator/impl/module/EnumModule.java
+++ b/src/main/java/com/github/victools/jsonschema/generator/impl/module/EnumModule.java
@@ -23,9 +23,9 @@ import com.github.victools.jsonschema.generator.CustomDefinition;
 import com.github.victools.jsonschema.generator.CustomDefinitionProviderV2;
 import com.github.victools.jsonschema.generator.MethodScope;
 import com.github.victools.jsonschema.generator.Module;
-import com.github.victools.jsonschema.generator.SchemaConstants;
 import com.github.victools.jsonschema.generator.SchemaGenerationContext;
 import com.github.victools.jsonschema.generator.SchemaGeneratorConfigBuilder;
+import com.github.victools.jsonschema.generator.SchemaKeyword;
 import com.github.victools.jsonschema.generator.impl.AttributeCollector;
 import java.util.List;
 import java.util.function.Function;
@@ -149,9 +149,9 @@ public class EnumModule implements Module {
         public CustomDefinition provideCustomSchemaDefinition(ResolvedType javaType, SchemaGenerationContext context) {
             if (javaType.isInstanceOf(Enum.class)) {
                 ObjectNode customNode = this.objectMapper.createObjectNode()
-                        .put(SchemaConstants.TAG_TYPE, SchemaConstants.TAG_TYPE_STRING);
+                        .put(context.getKeyword(SchemaKeyword.TAG_TYPE), context.getKeyword(SchemaKeyword.TAG_TYPE_STRING));
                 new AttributeCollector(this.objectMapper)
-                        .setEnum(customNode, EnumModule.extractEnumValues(javaType, this.enumConstantToString));
+                        .setEnum(customNode, EnumModule.extractEnumValues(javaType, this.enumConstantToString), context);
                 return new CustomDefinition(customNode);
             }
             return null;
diff --git a/src/main/java/com/github/victools/jsonschema/generator/impl/module/SimpleTypeModule.java b/src/main/java/com/github/victools/jsonschema/generator/impl/module/SimpleTypeModule.java
index ada7c00..0434c31 100644
--- a/src/main/java/com/github/victools/jsonschema/generator/impl/module/SimpleTypeModule.java
+++ b/src/main/java/com/github/victools/jsonschema/generator/impl/module/SimpleTypeModule.java
@@ -23,9 +23,9 @@ import com.github.victools.jsonschema.generator.CustomDefinition;
 import com.github.victools.jsonschema.generator.CustomDefinitionProviderV2;
 import com.github.victools.jsonschema.generator.MemberScope;
 import com.github.victools.jsonschema.generator.Module;
-import com.github.victools.jsonschema.generator.SchemaConstants;
 import com.github.victools.jsonschema.generator.SchemaGenerationContext;
 import com.github.victools.jsonschema.generator.SchemaGeneratorConfigBuilder;
+import com.github.victools.jsonschema.generator.SchemaKeyword;
 import com.github.victools.jsonschema.generator.TypeScope;
 import java.lang.reflect.Type;
 import java.math.BigDecimal;
@@ -101,16 +101,16 @@ public class SimpleTypeModule implements Module {
         return module;
     }
 
-    private final Map<Class<?>, String> fixedJsonSchemaTypes = new HashMap<>();
+    private final Map<Class<?>, SchemaKeyword> fixedJsonSchemaTypes = new HashMap<>();
 
     /**
      * Add the given mapping for a (simple) java class to its JSON schema equivalent "type" attribute.
      *
      * @param javaType java class to map to a fixed JSON schema definition
-     * @param jsonSchemaTypeValue "type" attribute value to set
+     * @param jsonSchemaTypeValue "type" attribute value to set or {@link SchemaKeyword#TAG_TYPE_NULL} to indicate empty schema being desired
      * @return this module instance (for chaining)
      */
-    private SimpleTypeModule with(Class<?> javaType, String jsonSchemaTypeValue) {
+    private SimpleTypeModule with(Class<?> javaType, SchemaKeyword jsonSchemaTypeValue) {
         this.fixedJsonSchemaTypes.put(javaType, jsonSchemaTypeValue);
         return this;
     }
@@ -122,11 +122,11 @@ public class SimpleTypeModule implements Module {
      * @return this module instance (for chaining)
      */
     public final SimpleTypeModule withEmptySchema(Class<?> javaType) {
-        return this.with(javaType, "");
+        return this.with(javaType, SchemaKeyword.TAG_TYPE_NULL);
     }
 
     /**
-     * Add the given mapping for a (simple) java class to its JSON schema equivalent "type" attribute: "{@value SchemaConstants#TAG_TYPE_OBJECT}".
+     * Add the given mapping for a (simple) java class to its JSON schema equivalent "type" attribute: "{@link SchemaKeyword#TAG_TYPE_OBJECT}".
      *
      * @param javaType java class to map to a fixed JSON schema definition
      * @return this module instance (for chaining)
@@ -134,47 +134,47 @@ public class SimpleTypeModule implements Module {
      */
     @Deprecated
     public final SimpleTypeModule withObjectType(Class<?> javaType) {
-        return this.with(javaType, SchemaConstants.TAG_TYPE_OBJECT);
+        return this.with(javaType, SchemaKeyword.TAG_TYPE_OBJECT);
     }
 
     /**
-     * Add the given mapping for a (simple) java class to its JSON schema equivalent "type" attribute: "{@value SchemaConstants#TAG_TYPE_STRING}".
+     * Add the given mapping for a (simple) java class to its JSON schema equivalent "type" attribute: "{@link SchemaKeyword#TAG_TYPE_STRING}".
      *
      * @param javaType java class to map to a fixed JSON schema definition
      * @return this module instance (for chaining)
      */
     public final SimpleTypeModule withStringType(Class<?> javaType) {
-        return this.with(javaType, SchemaConstants.TAG_TYPE_STRING);
+        return this.with(javaType, SchemaKeyword.TAG_TYPE_STRING);
     }
 
     /**
-     * Add the given mapping for a (simple) java class to its JSON schema equivalent "type" attribute: "{@value SchemaConstants#TAG_TYPE_BOOLEAN}".
+     * Add the given mapping for a (simple) java class to its JSON schema equivalent "type" attribute: "{@link SchemaKeyword#TAG_TYPE_BOOLEAN}".
      *
      * @param javaType java class to map to a fixed JSON schema definition
      * @return this module instance (for chaining)
      */
     public final SimpleTypeModule withBooleanType(Class<?> javaType) {
-        return this.with(javaType, SchemaConstants.TAG_TYPE_BOOLEAN);
+        return this.with(javaType, SchemaKeyword.TAG_TYPE_BOOLEAN);
     }
 
     /**
-     * Add the given mapping for a (simple) java class to its JSON schema equivalent "type" attribute: "{@value SchemaConstants#TAG_TYPE_INTEGER}".
+     * Add the given mapping for a (simple) java class to its JSON schema equivalent "type" attribute: "{@link SchemaKeyword#TAG_TYPE_INTEGER}".
      *
      * @param javaType java class to map to a fixed JSON schema definition
      * @return this module instance (for chaining)
      */
     public final SimpleTypeModule withIntegerType(Class<?> javaType) {
-        return this.with(javaType, SchemaConstants.TAG_TYPE_INTEGER);
+        return this.with(javaType, SchemaKeyword.TAG_TYPE_INTEGER);
     }
 
     /**
-     * Add the given mapping for a (simple) java class to its JSON schema equivalent "type" attribute: "{@value SchemaConstants#TAG_TYPE_NUMBER}".
+     * Add the given mapping for a (simple) java class to its JSON schema equivalent "type" attribute: "{@link SchemaKeyword#TAG_TYPE_NUMBER}".
      *
      * @param javaType java class to map to a fixed JSON schema definition
      * @return this module instance (for chaining)
      */
     public final SimpleTypeModule withNumberType(Class<?> javaType) {
-        return this.with(javaType, SchemaConstants.TAG_TYPE_NUMBER);
+        return this.with(javaType, SchemaKeyword.TAG_TYPE_NUMBER);
     }
 
     /**
@@ -208,7 +208,8 @@ public class SimpleTypeModule implements Module {
      * @return either Object.class to cause omission of the "additonalProperties" keyword or null to leave it up to following configurations
      */
     private Type resolveAdditionalProperties(TypeScope scope) {
-        if (scope.getType().getTypeParameters().isEmpty() && "".equals(this.fixedJsonSchemaTypes.get(scope.getType().getErasedType()))) {
+        if (scope.getType().getTypeParameters().isEmpty()
+                && SchemaKeyword.TAG_TYPE_NULL == this.fixedJsonSchemaTypes.get(scope.getType().getErasedType())) {
             // indicate no specific additionalProperties type - thereby causing it to be omitted from the generated schema
             return Object.class;
         }
@@ -222,7 +223,8 @@ public class SimpleTypeModule implements Module {
      * @return either an empty map to cause omission of the "patternProperties" keyword or null to leave it up to following configurations
      */
     private Map<String, Type> resolvePatternProperties(TypeScope scope) {
-        if (scope.getType().getTypeParameters().isEmpty() && "".equals(this.fixedJsonSchemaTypes.get(scope.getType().getErasedType()))) {
+        if (scope.getType().getTypeParameters().isEmpty()
+                && SchemaKeyword.TAG_TYPE_NULL == this.fixedJsonSchemaTypes.get(scope.getType().getErasedType())) {
             // indicate no specific patternProperties - thereby causing it to be omitted from the generated schema
             return Collections.emptyMap();
         }
@@ -250,14 +252,14 @@ public class SimpleTypeModule implements Module {
             if (!javaType.getTypeParameters().isEmpty()) {
                 return null;
             }
-            String jsonSchemaTypeValue = SimpleTypeModule.this.fixedJsonSchemaTypes.get(javaType.getErasedType());
+            SchemaKeyword jsonSchemaTypeValue = SimpleTypeModule.this.fixedJsonSchemaTypes.get(javaType.getErasedType());
             if (jsonSchemaTypeValue == null) {
                 return null;
             }
             // create fixed JSON schema definition, containing only the corresponding "type" attribute
             ObjectNode customSchema = this.objectMapper.createObjectNode();
-            if (!jsonSchemaTypeValue.isEmpty()) {
-                customSchema.put(SchemaConstants.TAG_TYPE, jsonSchemaTypeValue);
+            if (jsonSchemaTypeValue != SchemaKeyword.TAG_TYPE_NULL) {
+                customSchema.put(context.getKeyword(SchemaKeyword.TAG_TYPE), context.getKeyword(jsonSchemaTypeValue));
             }
             // set true as second parameter to indicate simple types to be always in-lined (i.e. not put into definitions)
             return new CustomDefinition(customSchema, true);


Test patch:
diff --git a/src/test/java/com/github/victools/jsonschema/generator/AbstractTypeAwareTest.java b/src/test/java/com/github/victools/jsonschema/generator/AbstractTypeAwareTest.java
index a7ff82f..946ab45 100644
--- a/src/test/java/com/github/victools/jsonschema/generator/AbstractTypeAwareTest.java
+++ b/src/test/java/com/github/victools/jsonschema/generator/AbstractTypeAwareTest.java
@@ -22,8 +22,8 @@ import com.fasterxml.classmate.members.ResolvedField;
 import com.fasterxml.classmate.members.ResolvedMethod;
 import com.github.victools.jsonschema.generator.impl.TypeContextFactory;
 import java.util.stream.Stream;
-import org.junit.Before;
 import org.mockito.Mockito;
+import org.mockito.stubbing.Answer;
 
 /**
  * Abstract test class catering for the type resolution of a dummy/test class to perform tests against introspected fields/methods.
@@ -38,13 +38,22 @@ public class AbstractTypeAwareTest {
         this.testClass = testClass;
     }
 
-    @Before
-    public final void abstractSetUp() {
+    /**
+     * Override generation context mock methods that are version dependent.
+     *
+     * @param schemaVersion designated JSON Schema version
+     */
+    protected void prepareContextForVersion(SchemaVersion schemaVersion) {
         TypeContext typeContext = TypeContextFactory.createDefaultTypeContext();
         ResolvedType resolvedTestClass = typeContext.resolve(this.testClass);
         this.testClassMembers = typeContext.resolveWithMembers(resolvedTestClass);
-        this.context = Mockito.mock(SchemaGenerationContext.class);
+        this.context = Mockito.mock(SchemaGenerationContext.class, Mockito.RETURNS_DEEP_STUBS);
         Mockito.when(this.context.getTypeContext()).thenReturn(typeContext);
+        Mockito.when(this.context.getGeneratorConfig().getSchemaVersion()).thenReturn(schemaVersion);
+
+        Answer<String> keywordLookup = invocation -> ((SchemaKeyword) invocation.getArgument(0)).forVersion(schemaVersion);
+        Mockito.when(this.context.getGeneratorConfig().getKeyword(Mockito.any())).thenAnswer(keywordLookup);
+        Mockito.when(this.context.getKeyword(Mockito.any())).thenAnswer(keywordLookup);
     }
 
     protected SchemaGenerationContext getContext() {
diff --git a/src/test/java/com/github/victools/jsonschema/generator/FieldScopeTest.java b/src/test/java/com/github/victools/jsonschema/generator/FieldScopeTest.java
index 7463124..e0561b2 100644
--- a/src/test/java/com/github/victools/jsonschema/generator/FieldScopeTest.java
+++ b/src/test/java/com/github/victools/jsonschema/generator/FieldScopeTest.java
@@ -23,6 +23,7 @@ import java.lang.annotation.Target;
 import junitparams.JUnitParamsRunner;
 import junitparams.Parameters;
 import org.junit.Assert;
+import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -36,6 +37,11 @@ public class FieldScopeTest extends AbstractTypeAwareTest {
         super(TestClass.class);
     }
 
+    @Before
+    public void setUp() {
+        this.prepareContextForVersion(SchemaVersion.DRAFT_2019_09);
+    }
+
     Object parametersForTestFindGetter() {
         return new String[][]{
             {"fieldWithoutGetter", null},
diff --git a/src/test/java/com/github/victools/jsonschema/generator/MemberScopeTest.java b/src/test/java/com/github/victools/jsonschema/generator/MemberScopeTest.java
index 7f35f49..da7076f 100644
--- a/src/test/java/com/github/victools/jsonschema/generator/MemberScopeTest.java
+++ b/src/test/java/com/github/victools/jsonschema/generator/MemberScopeTest.java
@@ -24,6 +24,7 @@ import java.util.Set;
 import junitparams.JUnitParamsRunner;
 import junitparams.Parameters;
 import org.junit.Assert;
+import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -37,6 +38,11 @@ public class MemberScopeTest extends AbstractTypeAwareTest {
         super(TestClass.class);
     }
 
+    @Before
+    public void setUp() {
+        this.prepareContextForVersion(SchemaVersion.DRAFT_2019_09);
+    }
+
     @Test
     @Parameters({
         "privateVisibleField, true, false, false",
diff --git a/src/test/java/com/github/victools/jsonschema/generator/MethodScopeTest.java b/src/test/java/com/github/victools/jsonschema/generator/MethodScopeTest.java
index 75169b7..ae23457 100644
--- a/src/test/java/com/github/victools/jsonschema/generator/MethodScopeTest.java
+++ b/src/test/java/com/github/victools/jsonschema/generator/MethodScopeTest.java
@@ -23,6 +23,7 @@ import java.lang.annotation.Target;
 import junitparams.JUnitParamsRunner;
 import junitparams.Parameters;
 import org.junit.Assert;
+import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -36,6 +37,11 @@ public class MethodScopeTest extends AbstractTypeAwareTest {
         super(TestClass.class);
     }
 
+    @Before
+    public void setUp() {
+        this.prepareContextForVersion(SchemaVersion.DRAFT_2019_09);
+    }
+
     Object parametersForTestFindGetterField() {
         return new String[][]{
             {"getFieldWithPrivateGetter", null},
diff --git a/src/test/java/com/github/victools/jsonschema/generator/SchemaGeneratorComplexTypesTest.java b/src/test/java/com/github/victools/jsonschema/generator/SchemaGeneratorComplexTypesTest.java
index 7c29349..b4b40c6 100644
--- a/src/test/java/com/github/victools/jsonschema/generator/SchemaGeneratorComplexTypesTest.java
+++ b/src/test/java/com/github/victools/jsonschema/generator/SchemaGeneratorComplexTypesTest.java
@@ -136,13 +136,14 @@ public class SchemaGeneratorComplexTypesTest {
     @Parameters
     @TestCaseName(value = "{method}({0}) [{index}]")
     public void testGenerateSchema(String caseTitle, OptionPreset preset, Class<?> targetType, Module testModule) throws Exception {
-        SchemaGeneratorConfigBuilder configBuilder = new SchemaGeneratorConfigBuilder(new ObjectMapper(), preset);
+        final SchemaVersion schemaVersion = SchemaVersion.DRAFT_7;
+        SchemaGeneratorConfigBuilder configBuilder = new SchemaGeneratorConfigBuilder(new ObjectMapper(), schemaVersion, preset);
         configBuilder.with(testModule);
         SchemaGenerator generator = new SchemaGenerator(configBuilder.build());
 
         JsonNode result = generator.generateSchema(targetType);
         // ensure that the generated definition keys are valid URIs without any characters requiring encoding
-        JsonNode definitions = result.get(SchemaConstants.TAG_DEFINITIONS);
+        JsonNode definitions = result.get(SchemaKeyword.TAG_DEFINITIONS.forVersion(schemaVersion));
         if (definitions instanceof ObjectNode) {
             Iterator<String> definitionKeys = ((ObjectNode) definitions).fieldNames();
             while (definitionKeys.hasNext()) {
diff --git a/src/test/java/com/github/victools/jsonschema/generator/SchemaGeneratorConfigBuilderTest.java b/src/test/java/com/github/victools/jsonschema/generator/SchemaGeneratorConfigBuilderTest.java
index fa803c1..55644f9 100644
--- a/src/test/java/com/github/victools/jsonschema/generator/SchemaGeneratorConfigBuilderTest.java
+++ b/src/test/java/com/github/victools/jsonschema/generator/SchemaGeneratorConfigBuilderTest.java
@@ -30,7 +30,7 @@ public class SchemaGeneratorConfigBuilderTest {
 
     @Before
     public void setUp() {
-        this.builder = new SchemaGeneratorConfigBuilder(new ObjectMapper());
+        this.builder = new SchemaGeneratorConfigBuilder(new ObjectMapper(), SchemaVersion.DRAFT_2019_09);
     }
 
     @Test
diff --git a/src/test/java/com/github/victools/jsonschema/generator/SchemaGeneratorCustomDefinitionsTest.java b/src/test/java/com/github/victools/jsonschema/generator/SchemaGeneratorCustomDefinitionsTest.java
index b4872a8..8730467 100644
--- a/src/test/java/com/github/victools/jsonschema/generator/SchemaGeneratorCustomDefinitionsTest.java
+++ b/src/test/java/com/github/victools/jsonschema/generator/SchemaGeneratorCustomDefinitionsTest.java
@@ -27,61 +27,71 @@ import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
 import java.util.Scanner;
+import junitparams.JUnitParamsRunner;
+import junitparams.Parameters;
 import org.junit.Assert;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 import org.skyscreamer.jsonassert.JSONAssert;
 import org.skyscreamer.jsonassert.JSONCompareMode;
 
 /**
  * Test for {@link SchemaGenerator} class.
  */
+@RunWith(JUnitParamsRunner.class)
 public class SchemaGeneratorCustomDefinitionsTest {
 
     @Test
-    public void testGenerateSchema_CustomDefinition() throws Exception {
+    @Parameters(source = SchemaVersion.class)
+    public void testGenerateSchema_CustomDefinition(SchemaVersion schemaVersion) throws Exception {
         CustomDefinitionProviderV2 customDefinitionProvider = (javaType, context) -> javaType.getErasedType() == Integer.class
                 ? new CustomDefinition(context.createDefinition(context.getTypeContext().resolve(String.class)))
                 : null;
-        SchemaGeneratorConfig config = new SchemaGeneratorConfigBuilder(new ObjectMapper())
+        SchemaGeneratorConfig config = new SchemaGeneratorConfigBuilder(new ObjectMapper(), schemaVersion)
                 .with(customDefinitionProvider)
                 .build();
         SchemaGenerator generator = new SchemaGenerator(config);
         JsonNode result = generator.generateSchema(Integer.class);
         Assert.assertEquals(1, result.size());
-        Assert.assertEquals(SchemaConstants.TAG_TYPE_STRING, result.get(SchemaConstants.TAG_TYPE).asText());
+        Assert.assertEquals(SchemaKeyword.TAG_TYPE_STRING.forVersion(schemaVersion),
+                result.get(SchemaKeyword.TAG_TYPE.forVersion(schemaVersion)).asText());
     }
 
     @Test
-    public void testGenerateSchema_CustomCollectionDefinition() throws Exception {
-        final ObjectMapper objectMapper = new ObjectMapper();
+    @Parameters(source = SchemaVersion.class)
+    public void testGenerateSchema_CustomCollectionDefinition(SchemaVersion schemaVersion) throws Exception {
         String accessProperty = "stream().findFirst().orElse(null)";
         CustomDefinitionProviderV2 customDefinitionProvider = (javaType, context) -> {
             if (!javaType.isInstanceOf(Collection.class)) {
                 return null;
             }
             ResolvedType generic = context.getTypeContext().getContainerItemType(javaType);
-            return new CustomDefinition(objectMapper.createObjectNode()
-                    .put(SchemaConstants.TAG_TYPE, SchemaConstants.TAG_TYPE_OBJECT)
-                    .set(SchemaConstants.TAG_PROPERTIES, objectMapper.createObjectNode()
+            SchemaGeneratorConfig config = context.getGeneratorConfig();
+            return new CustomDefinition(context.getGeneratorConfig().createObjectNode()
+                    .put(config.getKeyword(SchemaKeyword.TAG_TYPE), config.getKeyword(SchemaKeyword.TAG_TYPE_OBJECT))
+                    .set(config.getKeyword(SchemaKeyword.TAG_PROPERTIES), config.createObjectNode()
                             .set(accessProperty, context.makeNullable(context.createDefinition(generic)))));
         };
-        SchemaGeneratorConfig config = new SchemaGeneratorConfigBuilder(objectMapper)
+        SchemaGeneratorConfig config = new SchemaGeneratorConfigBuilder(new ObjectMapper(), schemaVersion)
                 .with(customDefinitionProvider)
                 .build();
         SchemaGenerator generator = new SchemaGenerator(config);
         JsonNode result = generator.generateSchema(ArrayList.class, String.class);
         Assert.assertEquals(2, result.size());
-        Assert.assertEquals(SchemaConstants.TAG_TYPE_OBJECT, result.get(SchemaConstants.TAG_TYPE).asText());
-        Assert.assertNotNull(result.get(SchemaConstants.TAG_PROPERTIES));
-        Assert.assertNotNull(result.get(SchemaConstants.TAG_PROPERTIES).get(accessProperty));
-        JsonNode accessPropertyType = result.get(SchemaConstants.TAG_PROPERTIES).get(accessProperty).get(SchemaConstants.TAG_TYPE);
+        Assert.assertEquals(SchemaKeyword.TAG_TYPE_OBJECT.forVersion(schemaVersion),
+                result.get(SchemaKeyword.TAG_TYPE.forVersion(schemaVersion)).asText());
+        Assert.assertNotNull(result.get(SchemaKeyword.TAG_PROPERTIES.forVersion(schemaVersion)));
+        Assert.assertNotNull(result.get(SchemaKeyword.TAG_PROPERTIES.forVersion(schemaVersion)).get(accessProperty));
+        JsonNode accessPropertyType = result.get(SchemaKeyword.TAG_PROPERTIES.forVersion(schemaVersion))
+                .get(accessProperty).get(SchemaKeyword.TAG_TYPE.forVersion(schemaVersion));
         Assert.assertNotNull(accessPropertyType);
-        Assert.assertEquals(SchemaConstants.TAG_TYPE_STRING, accessPropertyType.get(0).asText());
-        Assert.assertEquals(SchemaConstants.TAG_TYPE_NULL, accessPropertyType.get(1).asText());
+        Assert.assertEquals(SchemaKeyword.TAG_TYPE_STRING.forVersion(schemaVersion), accessPropertyType.get(0).asText());
+        Assert.assertEquals(SchemaKeyword.TAG_TYPE_NULL.forVersion(schemaVersion), accessPropertyType.get(1).asText());
     }
 
     @Test
-    public void testGenerateSchema_CustomStandardDefinition() throws Exception {
+    @Parameters(source = SchemaVersion.class)
+    public void testGenerateSchema_CustomStandardDefinition(SchemaVersion schemaVersion) throws Exception {
         CustomDefinitionProviderV2 customDefinitionProvider = new CustomDefinitionProviderV2() {
             @Override
             public CustomDefinition provideCustomSchemaDefinition(ResolvedType javaType, SchemaGenerationContext context) {
@@ -94,36 +104,39 @@ public class SchemaGeneratorCustomDefinitionsTest {
                 return null;
             }
         };
-        SchemaGeneratorConfig config = new SchemaGeneratorConfigBuilder(new ObjectMapper())
+        SchemaGeneratorConfig config = new SchemaGeneratorConfigBuilder(new ObjectMapper(), schemaVersion)
                 .with(customDefinitionProvider)
                 .build();
         SchemaGenerator generator = new SchemaGenerator(config);
         JsonNode result = generator.generateSchema(Integer.class);
         Assert.assertEquals(2, result.size());
-        Assert.assertEquals(SchemaConstants.TAG_TYPE_INTEGER, result.get(SchemaConstants.TAG_TYPE).asText());
+        Assert.assertEquals(SchemaKeyword.TAG_TYPE_INTEGER.forVersion(schemaVersion),
+                result.get(SchemaKeyword.TAG_TYPE.forVersion(schemaVersion)).asText());
         Assert.assertEquals("custom override of Integer", result.get("$comment").asText());
     }
 
     @Test
-    public void testGenerateSchema_CircularCustomStandardDefinition() throws Exception {
+    @Parameters(source = SchemaVersion.class)
+    public void testGenerateSchema_CircularCustomStandardDefinition(SchemaVersion schemaVersion) throws Exception {
         String accessProperty = "get(0)";
         CustomDefinitionProviderV2 customDefinitionProvider = (javaType, context) -> {
             if (!javaType.isInstanceOf(List.class)) {
                 return null;
             }
             ResolvedType generic = context.getTypeContext().getContainerItemType(javaType);
+            SchemaGeneratorConfig config = context.getGeneratorConfig();
             return new CustomDefinition(context.getGeneratorConfig().createObjectNode()
-                    .put(SchemaConstants.TAG_TYPE, SchemaConstants.TAG_TYPE_OBJECT)
-                    .set(SchemaConstants.TAG_PROPERTIES, context.getGeneratorConfig().createObjectNode()
+                    .put(config.getKeyword(SchemaKeyword.TAG_TYPE), config.getKeyword(SchemaKeyword.TAG_TYPE_OBJECT))
+                    .set(config.getKeyword(SchemaKeyword.TAG_PROPERTIES), context.getGeneratorConfig().createObjectNode()
                             .set(accessProperty, context.createDefinitionReference(generic))));
         };
-        SchemaGeneratorConfig config = new SchemaGeneratorConfigBuilder(new ObjectMapper())
+        SchemaGeneratorConfig config = new SchemaGeneratorConfigBuilder(new ObjectMapper(), schemaVersion)
                 .with(customDefinitionProvider)
                 .build();
         SchemaGenerator generator = new SchemaGenerator(config);
         JsonNode result = generator.generateSchema(TestCircularClass1.class);
         JSONAssert.assertEquals('\n' + result.toString() + '\n',
-                loadResource("circular-custom-definition.json"), result.toString(), JSONCompareMode.STRICT);
+                loadResource("circular-custom-definition-" + schemaVersion.name() + ".json"), result.toString(), JSONCompareMode.STRICT);
     }
 
     private static String loadResource(String resourcePath) throws IOException {
diff --git a/src/test/java/com/github/victools/jsonschema/generator/SchemaGeneratorSimpleTypesTest.java b/src/test/java/com/github/victools/jsonschema/generator/SchemaGeneratorSimpleTypesTest.java
index 164008c..d882b87 100644
--- a/src/test/java/com/github/victools/jsonschema/generator/SchemaGeneratorSimpleTypesTest.java
+++ b/src/test/java/com/github/victools/jsonschema/generator/SchemaGeneratorSimpleTypesTest.java
@@ -18,6 +18,8 @@ package com.github.victools.jsonschema.generator;
 
 import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.ObjectMapper;
+import java.util.Arrays;
+import java.util.EnumSet;
 import junitparams.JUnitParamsRunner;
 import junitparams.Parameters;
 import org.junit.Assert;
@@ -31,47 +33,53 @@ import org.junit.runner.RunWith;
 public class SchemaGeneratorSimpleTypesTest {
 
     Object parametersForTestGenerateSchema_SimpleType() {
-        return new Object[][]{
+        Object[][] typeCombinations = new Object[][]{
             {Object.class, null},
-            {String.class, SchemaConstants.TAG_TYPE_STRING},
-            {Character.class, SchemaConstants.TAG_TYPE_STRING},
-            {char.class, SchemaConstants.TAG_TYPE_STRING},
-            {CharSequence.class, SchemaConstants.TAG_TYPE_STRING},
-            {Boolean.class, SchemaConstants.TAG_TYPE_BOOLEAN},
-            {boolean.class, SchemaConstants.TAG_TYPE_BOOLEAN},
-            {Integer.class, SchemaConstants.TAG_TYPE_INTEGER},
-            {int.class, SchemaConstants.TAG_TYPE_INTEGER},
-            {Long.class, SchemaConstants.TAG_TYPE_INTEGER},
-            {long.class, SchemaConstants.TAG_TYPE_INTEGER},
-            {Short.class, SchemaConstants.TAG_TYPE_INTEGER},
-            {short.class, SchemaConstants.TAG_TYPE_INTEGER},
-            {Byte.class, SchemaConstants.TAG_TYPE_INTEGER},
-            {byte.class, SchemaConstants.TAG_TYPE_INTEGER},
-            {Double.class, SchemaConstants.TAG_TYPE_NUMBER},
-            {double.class, SchemaConstants.TAG_TYPE_NUMBER},
-            {Float.class, SchemaConstants.TAG_TYPE_NUMBER},
-            {float.class, SchemaConstants.TAG_TYPE_NUMBER}
+            {String.class, SchemaKeyword.TAG_TYPE_STRING},
+            {Character.class, SchemaKeyword.TAG_TYPE_STRING},
+            {char.class, SchemaKeyword.TAG_TYPE_STRING},
+            {CharSequence.class, SchemaKeyword.TAG_TYPE_STRING},
+            {Boolean.class, SchemaKeyword.TAG_TYPE_BOOLEAN},
+            {boolean.class, SchemaKeyword.TAG_TYPE_BOOLEAN},
+            {Integer.class, SchemaKeyword.TAG_TYPE_INTEGER},
+            {int.class, SchemaKeyword.TAG_TYPE_INTEGER},
+            {Long.class, SchemaKeyword.TAG_TYPE_INTEGER},
+            {long.class, SchemaKeyword.TAG_TYPE_INTEGER},
+            {Short.class, SchemaKeyword.TAG_TYPE_INTEGER},
+            {short.class, SchemaKeyword.TAG_TYPE_INTEGER},
+            {Byte.class, SchemaKeyword.TAG_TYPE_INTEGER},
+            {byte.class, SchemaKeyword.TAG_TYPE_INTEGER},
+            {Double.class, SchemaKeyword.TAG_TYPE_NUMBER},
+            {double.class, SchemaKeyword.TAG_TYPE_NUMBER},
+            {Float.class, SchemaKeyword.TAG_TYPE_NUMBER},
+            {float.class, SchemaKeyword.TAG_TYPE_NUMBER}
         };
+        return EnumSet.allOf(SchemaVersion.class).stream()
+                .flatMap(schemaVersion -> Arrays.stream(typeCombinations).map(entry -> new Object[]{entry[0], entry[1], schemaVersion}))
+                .toArray();
     }
 
     @Test
     @Parameters
-    public void testGenerateSchema_SimpleType(Class<?> targetType, String expectedJsonSchemaType) throws Exception {
-        SchemaGeneratorConfig config = new SchemaGeneratorConfigBuilder(new ObjectMapper()).build();
+    public void testGenerateSchema_SimpleType(Class<?> targetType, SchemaKeyword expectedJsonSchemaType, SchemaVersion schemaVersion)
+            throws Exception {
+        SchemaGeneratorConfig config = new SchemaGeneratorConfigBuilder(new ObjectMapper(), schemaVersion).build();
         SchemaGenerator generator = new SchemaGenerator(config);
         JsonNode result = generator.generateSchema(targetType);
         if (expectedJsonSchemaType == null) {
             Assert.assertTrue(result.isEmpty());
         } else {
             Assert.assertEquals(1, result.size());
-            Assert.assertEquals(expectedJsonSchemaType, result.get(SchemaConstants.TAG_TYPE).asText());
+            Assert.assertEquals(expectedJsonSchemaType.forVersion(schemaVersion),
+                    result.get(SchemaKeyword.TAG_TYPE.forVersion(schemaVersion)).asText());
         }
     }
 
     @Test
     @Parameters(method = "parametersForTestGenerateSchema_SimpleType")
-    public void testGenerateSchema_SimpleType_withAdditionalPropertiesOption(Class<?> targetType, String expectedJsonSchemaType) throws Exception {
-        SchemaGeneratorConfig config = new SchemaGeneratorConfigBuilder(new ObjectMapper())
+    public void testGenerateSchema_SimpleType_withAdditionalPropertiesOption(Class<?> targetType, SchemaKeyword expectedJsonSchemaType,
+            SchemaVersion schemaVersion) throws Exception {
+        SchemaGeneratorConfig config = new SchemaGeneratorConfigBuilder(new ObjectMapper(), schemaVersion)
                 .with(Option.FORBIDDEN_ADDITIONAL_PROPERTIES_BY_DEFAULT)
                 .build();
         SchemaGenerator generator = new SchemaGenerator(config);
@@ -80,7 +88,8 @@ public class SchemaGeneratorSimpleTypesTest {
             Assert.assertTrue(result.isEmpty());
         } else {
             Assert.assertEquals(1, result.size());
-            Assert.assertEquals(expectedJsonSchemaType, result.get(SchemaConstants.TAG_TYPE).asText());
+            Assert.assertEquals(expectedJsonSchemaType.forVersion(schemaVersion),
+                    result.get(SchemaKeyword.TAG_TYPE.forVersion(schemaVersion)).asText());
         }
     }
 }
diff --git a/src/test/java/com/github/victools/jsonschema/generator/SchemaGeneratorSubtypesTest.java b/src/test/java/com/github/victools/jsonschema/generator/SchemaGeneratorSubtypesTest.java
index 7e67605..481b3f2 100644
--- a/src/test/java/com/github/victools/jsonschema/generator/SchemaGeneratorSubtypesTest.java
+++ b/src/test/java/com/github/victools/jsonschema/generator/SchemaGeneratorSubtypesTest.java
@@ -44,19 +44,20 @@ public class SchemaGeneratorSubtypesTest {
 
     Object parametersForTestGenerateSchema() {
         return new Object[][]{
-            {"testclass-withsupertypereferences-NO_SUBTYPES", Collections.emptyList()},
-            {"testclass-withsupertypereferences-ONE_SUBTYPE_VALID", Collections.singletonList(TestSubClass1.class)},
-            {"testclass-withsupertypereferences-ONE_SUBTYPE_INVALID", Collections.singletonList(TestSubClass3.class)},
-            {"testclass-withsupertypereferences-TWO_SUBTYPES", Arrays.asList(TestSubClass2.class, TestSubClass3.class)},
-            {"testclass-withsupertypereferences-THREE_SUBTYPES", Arrays.asList(TestSubClass1.class, TestSubClass2.class, TestSubClass3.class)}
+            {"testclass-withsupertypereferences-NO_SUBTYPES", Collections.emptyList(), SchemaVersion.DRAFT_7},
+            {"testclass-withsupertypereferences-ONE_SUBTYPE_VALID", Collections.singletonList(TestSubClass1.class), SchemaVersion.DRAFT_7},
+            {"testclass-withsupertypereferences-ONE_SUBTYPE_INVALID", Collections.singletonList(TestSubClass3.class), SchemaVersion.DRAFT_7},
+            {"testclass-withsupertypereferences-TWO_SUBTYPES", Arrays.asList(TestSubClass2.class, TestSubClass3.class), SchemaVersion.DRAFT_7},
+            {"testclass-withsupertypereferences-THREE_SUBTYPES", Arrays.asList(TestSubClass1.class, TestSubClass2.class, TestSubClass3.class),
+                SchemaVersion.DRAFT_7}
         };
     }
 
     @Test
     @Parameters
     @TestCaseName(value = "{method}({0}) [{index}]")
-    public void testGenerateSchema(String caseTitle, List<Class<?>> subtypes) throws Exception {
-        SchemaGeneratorConfigBuilder configBuilder = new SchemaGeneratorConfigBuilder(new ObjectMapper(), OptionPreset.PLAIN_JSON)
+    public void testGenerateSchema(String caseTitle, List<Class<?>> subtypes, SchemaVersion schemaVersion) throws Exception {
+        SchemaGeneratorConfigBuilder configBuilder = new SchemaGeneratorConfigBuilder(new ObjectMapper(), schemaVersion, OptionPreset.PLAIN_JSON)
                 .with(Option.DEFINITIONS_FOR_ALL_OBJECTS, Option.NULLABLE_FIELDS_BY_DEFAULT);
         configBuilder.forTypesInGeneral()
                 .withSubtypeResolver(new TestSubtypeResolver(subtypes))
diff --git a/src/test/java/com/github/victools/jsonschema/generator/impl/AttributeCollectorTest.java b/src/test/java/com/github/victools/jsonschema/generator/impl/AttributeCollectorTest.java
index 8a99762..12fd93a 100644
--- a/src/test/java/com/github/victools/jsonschema/generator/impl/AttributeCollectorTest.java
+++ b/src/test/java/com/github/victools/jsonschema/generator/impl/AttributeCollectorTest.java
@@ -20,143 +20,176 @@ import com.fasterxml.classmate.ResolvedType;
 import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fasterxml.jackson.databind.node.ObjectNode;
-import com.github.victools.jsonschema.generator.SchemaConstants;
+import com.github.victools.jsonschema.generator.SchemaGenerationContext;
+import com.github.victools.jsonschema.generator.SchemaGeneratorConfig;
 import com.github.victools.jsonschema.generator.SchemaGeneratorConfigPart;
 import com.github.victools.jsonschema.generator.SchemaGeneratorGeneralConfigPart;
+import com.github.victools.jsonschema.generator.SchemaKeyword;
+import com.github.victools.jsonschema.generator.SchemaVersion;
 import java.lang.reflect.Type;
 import java.util.Collections;
 import java.util.LinkedHashMap;
+import junitparams.JUnitParamsRunner;
+import junitparams.Parameters;
 import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 
 /**
  * Test for the {@link AttributeCollector} class.
  */
+@RunWith(JUnitParamsRunner.class)
 public class AttributeCollectorTest {
 
     private AttributeCollector collector;
+    private ObjectMapper objectMapper;
     private ObjectNode definitionNode;
-    private SchemaGenerationContextImpl generationContext;
 
     @Before
     public void setUp() {
-        ObjectMapper objectMapper = new ObjectMapper();
-        this.collector = new AttributeCollector(objectMapper);
-        this.definitionNode = objectMapper.createObjectNode();
-        SchemaGeneratorConfigImpl generatorConfig = new SchemaGeneratorConfigImpl(
-                objectMapper,
+        this.objectMapper = new ObjectMapper();
+        this.collector = new AttributeCollector(this.objectMapper);
+        this.definitionNode = this.objectMapper.createObjectNode();
+    }
+
+    /**
+     * Helper method for creating the test context for a given JSON Schema version.
+     *
+     * @param schemaVersion designated JSON Schema version
+     * @return schema generation context for the given JSON Schema version
+     */
+    private SchemaGenerationContextImpl generateContext(SchemaVersion schemaVersion) {
+        SchemaGeneratorConfig generatorConfig = new SchemaGeneratorConfigImpl(
+                this.objectMapper,
+                schemaVersion,
                 Collections.emptySet(),
                 new SchemaGeneratorGeneralConfigPart(),
                 new SchemaGeneratorConfigPart<>(),
                 new SchemaGeneratorConfigPart<>());
-        this.generationContext = new SchemaGenerationContextImpl(generatorConfig, TypeContextFactory.createDefaultTypeContext());
+        return new SchemaGenerationContextImpl(generatorConfig, TypeContextFactory.createDefaultTypeContext());
     }
 
     @Test
-    public void testSetAdditionalProperties_Null() {
-        this.collector.setAdditionalProperties(this.definitionNode, null, this.generationContext);
+    @Parameters(source = SchemaVersion.class)
+    public void testSetAdditionalProperties_Null(SchemaVersion schemaVersion) {
+        this.collector.setAdditionalProperties(this.definitionNode, null, this.generateContext(schemaVersion));
         Assert.assertTrue(this.definitionNode.isEmpty());
     }
 
     @Test
-    public void testSetAdditionalProperties_ObjectClass() {
-        this.collector.setAdditionalProperties(this.definitionNode, Object.class, this.generationContext);
+    @Parameters(source = SchemaVersion.class)
+    public void testSetAdditionalProperties_ObjectClass(SchemaVersion schemaVersion) {
+        this.collector.setAdditionalProperties(this.definitionNode, Object.class, this.generateContext(schemaVersion));
         Assert.assertTrue(this.definitionNode.isEmpty());
     }
 
     @Test
-    public void testSetAdditionalProperties_ResolvedObjectClass() {
-        ResolvedType resolvedObjectClass = this.generationContext.getTypeContext().resolve(Object.class);
-        this.collector.setAdditionalProperties(this.definitionNode, resolvedObjectClass, this.generationContext);
+    @Parameters(source = SchemaVersion.class)
+    public void testSetAdditionalProperties_ResolvedObjectClass(SchemaVersion schemaVersion) {
+        SchemaGenerationContext generationContext = this.generateContext(schemaVersion);
+        ResolvedType resolvedObjectClass = generationContext.getTypeContext().resolve(Object.class);
+        this.collector.setAdditionalProperties(this.definitionNode, resolvedObjectClass, generationContext);
         Assert.assertTrue(this.definitionNode.isEmpty());
     }
 
     @Test
-    public void testSetAdditionalProperties_VoidClass() {
-        this.collector.setAdditionalProperties(this.definitionNode, Void.class, this.generationContext);
+    @Parameters(source = SchemaVersion.class)
+    public void testSetAdditionalProperties_VoidClass(SchemaVersion schemaVersion) {
+        this.collector.setAdditionalProperties(this.definitionNode, Void.class, this.generateContext(schemaVersion));
         Assert.assertEquals(1, this.definitionNode.size());
-        JsonNode additionalPropertiesNode = this.definitionNode.get(SchemaConstants.TAG_ADDITIONAL_PROPERTIES);
+        JsonNode additionalPropertiesNode = this.definitionNode.get(SchemaKeyword.TAG_ADDITIONAL_PROPERTIES.forVersion(schemaVersion));
         Assert.assertNotNull(additionalPropertiesNode);
         Assert.assertTrue(additionalPropertiesNode.isBoolean());
         Assert.assertFalse(additionalPropertiesNode.asBoolean());
     }
 
     @Test
-    public void testSetAdditionalProperties_VoidType() {
-        this.collector.setAdditionalProperties(this.definitionNode, Void.TYPE, this.generationContext);
+    @Parameters(source = SchemaVersion.class)
+    public void testSetAdditionalProperties_VoidType(SchemaVersion schemaVersion) {
+        this.collector.setAdditionalProperties(this.definitionNode, Void.TYPE, this.generateContext(schemaVersion));
         Assert.assertEquals(1, this.definitionNode.size());
-        JsonNode additionalPropertiesNode = this.definitionNode.get(SchemaConstants.TAG_ADDITIONAL_PROPERTIES);
+        JsonNode additionalPropertiesNode = this.definitionNode.get(SchemaKeyword.TAG_ADDITIONAL_PROPERTIES.forVersion(schemaVersion));
         Assert.assertNotNull(additionalPropertiesNode);
         Assert.assertTrue(additionalPropertiesNode.isBoolean());
         Assert.assertFalse(additionalPropertiesNode.asBoolean());
     }
 
     @Test
-    public void testSetAdditionalProperties_SpecificClass() {
-        this.collector.setAdditionalProperties(this.definitionNode, int.class, this.generationContext);
+    @Parameters(source = SchemaVersion.class)
+    public void testSetAdditionalProperties_SpecificClass(SchemaVersion schemaVersion) {
+        SchemaGenerationContextImpl generationContext = this.generateContext(schemaVersion);
+        this.collector.setAdditionalProperties(this.definitionNode, int.class, generationContext);
         Assert.assertEquals(1, this.definitionNode.size());
-        JsonNode additionalPropertiesNode = this.definitionNode.get(SchemaConstants.TAG_ADDITIONAL_PROPERTIES);
+        JsonNode additionalPropertiesNode = this.definitionNode.get(SchemaKeyword.TAG_ADDITIONAL_PROPERTIES.forVersion(schemaVersion));
         Assert.assertNotNull(additionalPropertiesNode);
         Assert.assertTrue(additionalPropertiesNode.isObject());
-        ResolvedType resolvedType = this.generationContext.getTypeContext().resolve(int.class);
-        Assert.assertTrue(this.generationContext.containsDefinition(resolvedType));
+        ResolvedType resolvedType = generationContext.getTypeContext().resolve(int.class);
+        Assert.assertTrue(generationContext.containsDefinition(resolvedType));
     }
 
     @Test
-    public void testSetPatternProperties_Null() {
-        this.collector.setPatternProperties(this.definitionNode, null, this.generationContext);
+    @Parameters(source = SchemaVersion.class)
+    public void testSetPatternProperties_Null(SchemaVersion schemaVersion) {
+        this.collector.setPatternProperties(this.definitionNode, null, this.generateContext(schemaVersion));
         Assert.assertTrue(this.definitionNode.isEmpty());
     }
 
     @Test
-    public void testSetPatternProperties_Empty() {
-        this.collector.setPatternProperties(this.definitionNode, Collections.emptyMap(), this.generationContext);
+    @Parameters(source = SchemaVersion.class)
+    public void testSetPatternProperties_Empty(SchemaVersion schemaVersion) {
+        this.collector.setPatternProperties(this.definitionNode, Collections.emptyMap(), this.generateContext(schemaVersion));
         Assert.assertTrue(this.definitionNode.isEmpty());
     }
 
     @Test
-    public void testSetPatternProperties_ObjectClass() {
+    @Parameters(source = SchemaVersion.class)
+    public void testSetPatternProperties_ObjectClass(SchemaVersion schemaVersion) {
+        SchemaGenerationContextImpl generationContext = this.generateContext(schemaVersion);
         LinkedHashMap<String, Type> patterns = new LinkedHashMap<>();
         patterns.put("^objectClass.*$", Object.class);
-        this.collector.setPatternProperties(this.definitionNode, patterns, this.generationContext);
+        this.collector.setPatternProperties(this.definitionNode, patterns, generationContext);
         Assert.assertEquals(1, this.definitionNode.size());
-        JsonNode patternPropertiesNode = this.definitionNode.get(SchemaConstants.TAG_PATTERN_PROPERTIES);
+        JsonNode patternPropertiesNode = this.definitionNode.get(SchemaKeyword.TAG_PATTERN_PROPERTIES.forVersion(schemaVersion));
         Assert.assertNotNull(patternPropertiesNode);
         Assert.assertTrue(patternPropertiesNode.isObject());
         Assert.assertEquals(1, patternPropertiesNode.size());
         Assert.assertTrue(patternPropertiesNode.get("^objectClass.*$").isObject());
-        Assert.assertTrue(this.generationContext.containsDefinition(this.generationContext.getTypeContext().resolve(Object.class)));
+        Assert.assertTrue(generationContext.containsDefinition(generationContext.getTypeContext().resolve(Object.class)));
     }
 
     @Test
-    public void testSetPatternProperties_ResolvedObjectClass() {
+    @Parameters(source = SchemaVersion.class)
+    public void testSetPatternProperties_ResolvedObjectClass(SchemaVersion schemaVersion) {
+        SchemaGenerationContextImpl generationContext = this.generateContext(schemaVersion);
         LinkedHashMap<String, Type> patterns = new LinkedHashMap<>();
-        patterns.put("^resolvedObjectClass.*$", this.generationContext.getTypeContext().resolve(Object.class));
-        this.collector.setPatternProperties(this.definitionNode, patterns, this.generationContext);
+        patterns.put("^resolvedObjectClass.*$", generationContext.getTypeContext().resolve(Object.class));
+        this.collector.setPatternProperties(this.definitionNode, patterns, generationContext);
         Assert.assertEquals(1, this.definitionNode.size());
-        JsonNode patternPropertiesNode = this.definitionNode.get(SchemaConstants.TAG_PATTERN_PROPERTIES);
+        JsonNode patternPropertiesNode = this.definitionNode.get(SchemaKeyword.TAG_PATTERN_PROPERTIES.forVersion(schemaVersion));
         Assert.assertNotNull(patternPropertiesNode);
         Assert.assertTrue(patternPropertiesNode.isObject());
         Assert.assertEquals(1, patternPropertiesNode.size());
         Assert.assertTrue(patternPropertiesNode.get("^resolvedObjectClass.*$").isObject());
-        Assert.assertTrue(this.generationContext.containsDefinition(this.generationContext.getTypeContext().resolve(Object.class)));
+        Assert.assertTrue(generationContext.containsDefinition(generationContext.getTypeContext().resolve(Object.class)));
     }
 
     @Test
-    public void testSetPatternProperties_SpecificEntries() {
+    @Parameters(source = SchemaVersion.class)
+    public void testSetPatternProperties_SpecificEntries(SchemaVersion schemaVersion) {
+        SchemaGenerationContextImpl generationContext = this.generateContext(schemaVersion);
         LinkedHashMap<String, Type> patterns = new LinkedHashMap<>();
         patterns.put("^intClass.*$", int.class);
-        patterns.put("^resolvedStringClass.*$", this.generationContext.getTypeContext().resolve(String.class));
-        this.collector.setPatternProperties(this.definitionNode, patterns, this.generationContext);
+        patterns.put("^resolvedStringClass.*$", generationContext.getTypeContext().resolve(String.class));
+        this.collector.setPatternProperties(this.definitionNode, patterns, generationContext);
         Assert.assertEquals(1, this.definitionNode.size());
-        JsonNode patternPropertiesNode = this.definitionNode.get(SchemaConstants.TAG_PATTERN_PROPERTIES);
+        JsonNode patternPropertiesNode = this.definitionNode.get(SchemaKeyword.TAG_PATTERN_PROPERTIES.forVersion(schemaVersion));
         Assert.assertNotNull(patternPropertiesNode);
         Assert.assertTrue(patternPropertiesNode.isObject());
         Assert.assertEquals(2, patternPropertiesNode.size());
         Assert.assertTrue(patternPropertiesNode.get("^intClass.*$").isObject());
         Assert.assertTrue(patternPropertiesNode.get("^resolvedStringClass.*$").isObject());
-        Assert.assertTrue(this.generationContext.containsDefinition(this.generationContext.getTypeContext().resolve(int.class)));
-        Assert.assertTrue(this.generationContext.containsDefinition(this.generationContext.getTypeContext().resolve(String.class)));
+        Assert.assertTrue(generationContext.containsDefinition(generationContext.getTypeContext().resolve(int.class)));
+        Assert.assertTrue(generationContext.containsDefinition(generationContext.getTypeContext().resolve(String.class)));
     }
 }
diff --git a/src/test/java/com/github/victools/jsonschema/generator/impl/SchemaGeneratorConfigImplTest.java b/src/test/java/com/github/victools/jsonschema/generator/impl/SchemaGeneratorConfigImplTest.java
index b44d803..06f64ba 100644
--- a/src/test/java/com/github/victools/jsonschema/generator/impl/SchemaGeneratorConfigImplTest.java
+++ b/src/test/java/com/github/victools/jsonschema/generator/impl/SchemaGeneratorConfigImplTest.java
@@ -29,6 +29,7 @@ import com.github.victools.jsonschema.generator.MethodScope;
 import com.github.victools.jsonschema.generator.Option;
 import com.github.victools.jsonschema.generator.SchemaGeneratorConfigPart;
 import com.github.victools.jsonschema.generator.SchemaGeneratorGeneralConfigPart;
+import com.github.victools.jsonschema.generator.SchemaVersion;
 import com.github.victools.jsonschema.generator.TypeAttributeOverride;
 import java.util.Arrays;
 import java.util.Collections;
@@ -67,13 +68,13 @@ public class SchemaGeneratorConfigImplTest extends AbstractTypeAwareTest {
     }
 
     @Before
-    @SuppressWarnings("unchecked")
     public void setUp() {
         MockitoAnnotations.initMocks(this);
         this.enabledOptions = new HashSet<>();
 
-        this.instance = new SchemaGeneratorConfigImpl(this.objectMapper, this.enabledOptions, this.typesInGeneralConfigPart,
-                this.fieldConfigPart, this.methodConfigPart);
+        this.instance = new SchemaGeneratorConfigImpl(this.objectMapper, SchemaVersion.DRAFT_2019_09, this.enabledOptions,
+                this.typesInGeneralConfigPart, this.fieldConfigPart, this.methodConfigPart);
+        this.prepareContextForVersion(SchemaVersion.DRAFT_2019_09);
     }
 
     @Test
diff --git a/src/test/java/com/github/victools/jsonschema/generator/impl/module/ConstantValueModuleTest.java b/src/test/java/com/github/victools/jsonschema/generator/impl/module/ConstantValueModuleTest.java
index 6983bb5..44f729c 100644
--- a/src/test/java/com/github/victools/jsonschema/generator/impl/module/ConstantValueModuleTest.java
+++ b/src/test/java/com/github/victools/jsonschema/generator/impl/module/ConstantValueModuleTest.java
@@ -16,10 +16,11 @@
 
 package com.github.victools.jsonschema.generator.impl.module;
 
-import com.github.victools.jsonschema.generator.SchemaGeneratorConfigBuilder;
-import com.github.victools.jsonschema.generator.SchemaGeneratorConfigPart;
 import com.github.victools.jsonschema.generator.AbstractTypeAwareTest;
 import com.github.victools.jsonschema.generator.FieldScope;
+import com.github.victools.jsonschema.generator.SchemaGeneratorConfigBuilder;
+import com.github.victools.jsonschema.generator.SchemaGeneratorConfigPart;
+import com.github.victools.jsonschema.generator.SchemaVersion;
 import java.util.Collection;
 import java.util.Collections;
 import junitparams.JUnitParamsRunner;
@@ -50,6 +51,7 @@ public class ConstantValueModuleTest extends AbstractTypeAwareTest {
         this.builder = Mockito.mock(SchemaGeneratorConfigBuilder.class);
         this.fieldConfigPart = Mockito.spy(new SchemaGeneratorConfigPart<>());
         Mockito.when(builder.forFields()).thenReturn(this.fieldConfigPart);
+        this.prepareContextForVersion(SchemaVersion.DRAFT_2019_09);
     }
 
     @Test
diff --git a/src/test/java/com/github/victools/jsonschema/generator/impl/module/EnumModuleTest.java b/src/test/java/com/github/victools/jsonschema/generator/impl/module/EnumModuleTest.java
index 376e4b2..10ab985 100644
--- a/src/test/java/com/github/victools/jsonschema/generator/impl/module/EnumModuleTest.java
+++ b/src/test/java/com/github/victools/jsonschema/generator/impl/module/EnumModuleTest.java
@@ -28,18 +28,23 @@ import com.github.victools.jsonschema.generator.CustomDefinitionProviderV2;
 import com.github.victools.jsonschema.generator.FieldScope;
 import com.github.victools.jsonschema.generator.MethodScope;
 import com.github.victools.jsonschema.generator.OptionPreset;
-import com.github.victools.jsonschema.generator.SchemaConstants;
 import com.github.victools.jsonschema.generator.SchemaGeneratorConfigBuilder;
 import com.github.victools.jsonschema.generator.SchemaGeneratorConfigPart;
+import com.github.victools.jsonschema.generator.SchemaKeyword;
+import com.github.victools.jsonschema.generator.SchemaVersion;
+import junitparams.JUnitParamsRunner;
+import junitparams.Parameters;
 import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mockito;
 
 /**
  * Test for the {@link EnumModule} class.
  */
+@RunWith(JUnitParamsRunner.class)
 public class EnumModuleTest extends AbstractTypeAwareTest {
 
     private EnumModule instanceAsStringsFromName;
@@ -58,16 +63,27 @@ public class EnumModuleTest extends AbstractTypeAwareTest {
         this.instanceAsStringsFromName = EnumModule.asStringsFromName();
         this.instanceAsStringsFromToString = EnumModule.asStringsFromToString();
         this.instanceAsObjects = EnumModule.asObjects();
-        this.builder = Mockito.spy(new SchemaGeneratorConfigBuilder(new ObjectMapper(), new OptionPreset()));
-        this.fieldConfigPart = Mockito.spy(new SchemaGeneratorConfigPart<>());
-        this.methodConfigPart = Mockito.spy(new SchemaGeneratorConfigPart<>());
+    }
+
+    /**
+     * Initialise configuration builder instance for the given JSON Schema version.
+     *
+     * @param schemaVersion designated JSON Schema version
+     */
+    private void initConfigBuilder(SchemaVersion schemaVersion) {
+        this.prepareContextForVersion(schemaVersion);
+        SchemaGeneratorConfigBuilder configBuilder = new SchemaGeneratorConfigBuilder(new ObjectMapper(), schemaVersion, new OptionPreset());
+        this.fieldConfigPart = Mockito.spy(configBuilder.forFields());
+        this.methodConfigPart = Mockito.spy(configBuilder.forMethods());
+        this.builder = Mockito.spy(configBuilder);
         Mockito.when(this.builder.forFields()).thenReturn(this.fieldConfigPart);
         Mockito.when(this.builder.forMethods()).thenReturn(this.methodConfigPart);
     }
 
     @Test
-    @SuppressWarnings("unchecked")
-    public void testApplyToConfigBuilder_asStringsFromName() {
+    @Parameters(source = SchemaVersion.class)
+    public void testApplyToConfigBuilder_asStringsFromName(SchemaVersion schemaVersion) {
+        this.initConfigBuilder(schemaVersion);
         this.instanceAsStringsFromName.applyToConfigBuilder(this.builder);
 
         Mockito.verify(this.builder).getObjectMapper();
@@ -76,8 +92,9 @@ public class EnumModuleTest extends AbstractTypeAwareTest {
     }
 
     @Test
-    @SuppressWarnings("unchecked")
-    public void testApplyToConfigBuilder_asStringsFromToString() {
+    @Parameters(source = SchemaVersion.class)
+    public void testApplyToConfigBuilder_asStringsFromToString(SchemaVersion schemaVersion) {
+        this.initConfigBuilder(schemaVersion);
         this.instanceAsStringsFromToString.applyToConfigBuilder(this.builder);
 
         Mockito.verify(this.builder).getObjectMapper();
@@ -86,8 +103,9 @@ public class EnumModuleTest extends AbstractTypeAwareTest {
     }
 
     @Test
-    @SuppressWarnings("unchecked")
-    public void testApplyToConfigBuilder_asObjects() {
+    @Parameters(source = SchemaVersion.class)
+    public void testApplyToConfigBuilder_asObjects(SchemaVersion schemaVersion) {
+        this.initConfigBuilder(schemaVersion);
         this.instanceAsObjects.applyToConfigBuilder(this.builder);
 
         Mockito.verify(this.builder).forFields();
@@ -102,7 +120,9 @@ public class EnumModuleTest extends AbstractTypeAwareTest {
     }
 
     @Test
-    public void testCustomSchemaDefinition_asStringsFromName() {
+    @Parameters(source = SchemaVersion.class)
+    public void testCustomSchemaDefinition_asStringsFromName(SchemaVersion schemaVersion) {
+        this.initConfigBuilder(schemaVersion);
         this.instanceAsStringsFromName.applyToConfigBuilder(this.builder);
         ArgumentCaptor<CustomDefinitionProviderV2> captor = ArgumentCaptor.forClass(CustomDefinitionProviderV2.class);
         Mockito.verify(this.builder).with(captor.capture());
@@ -113,11 +133,11 @@ public class EnumModuleTest extends AbstractTypeAwareTest {
         ObjectNode node = schemaDefinition.getValue();
         Assert.assertEquals(2, node.size());
 
-        JsonNode typeNode = node.get(SchemaConstants.TAG_TYPE);
+        JsonNode typeNode = node.get(SchemaKeyword.TAG_TYPE.forVersion(schemaVersion));
         Assert.assertEquals(JsonNodeType.STRING, typeNode.getNodeType());
-        Assert.assertEquals(SchemaConstants.TAG_TYPE_STRING, typeNode.textValue());
+        Assert.assertEquals(SchemaKeyword.TAG_TYPE_STRING.forVersion(schemaVersion), typeNode.textValue());
 
-        JsonNode enumNode = node.get(SchemaConstants.TAG_ENUM);
+        JsonNode enumNode = node.get(SchemaKeyword.TAG_ENUM.forVersion(schemaVersion));
         Assert.assertEquals(JsonNodeType.ARRAY, enumNode.getNodeType());
         Assert.assertEquals(3, ((ArrayNode) enumNode).size());
         Assert.assertEquals(JsonNodeType.STRING, enumNode.get(0).getNodeType());
@@ -129,7 +149,9 @@ public class EnumModuleTest extends AbstractTypeAwareTest {
     }
 
     @Test
-    public void testCustomSchemaDefinition_asStringsFromToString() {
+    @Parameters(source = SchemaVersion.class)
+    public void testCustomSchemaDefinition_asStringsFromToString(SchemaVersion schemaVersion) {
+        this.initConfigBuilder(schemaVersion);
         this.instanceAsStringsFromToString.applyToConfigBuilder(this.builder);
         ArgumentCaptor<CustomDefinitionProviderV2> captor = ArgumentCaptor.forClass(CustomDefinitionProviderV2.class);
         Mockito.verify(this.builder).with(captor.capture());
@@ -140,11 +162,11 @@ public class EnumModuleTest extends AbstractTypeAwareTest {
         ObjectNode node = schemaDefinition.getValue();
         Assert.assertEquals(2, node.size());
 
-        JsonNode typeNode = node.get(SchemaConstants.TAG_TYPE);
+        JsonNode typeNode = node.get(SchemaKeyword.TAG_TYPE.forVersion(schemaVersion));
         Assert.assertEquals(JsonNodeType.STRING, typeNode.getNodeType());
-        Assert.assertEquals(SchemaConstants.TAG_TYPE_STRING, typeNode.textValue());
+        Assert.assertEquals(SchemaKeyword.TAG_TYPE_STRING.forVersion(schemaVersion), typeNode.textValue());
 
-        JsonNode enumNode = node.get(SchemaConstants.TAG_ENUM);
+        JsonNode enumNode = node.get(SchemaKeyword.TAG_ENUM.forVersion(schemaVersion));
         Assert.assertEquals(JsonNodeType.ARRAY, enumNode.getNodeType());
         Assert.assertEquals(3, ((ArrayNode) enumNode).size());
         Assert.assertEquals(JsonNodeType.STRING, enumNode.get(0).getNodeType());
diff --git a/src/test/java/com/github/victools/jsonschema/generator/impl/module/FieldExclusionModuleTest.java b/src/test/java/com/github/victools/jsonschema/generator/impl/module/FieldExclusionModuleTest.java
index 74d6040..03b579f 100644
--- a/src/test/java/com/github/victools/jsonschema/generator/impl/module/FieldExclusionModuleTest.java
+++ b/src/test/java/com/github/victools/jsonschema/generator/impl/module/FieldExclusionModuleTest.java
@@ -16,10 +16,11 @@
 
 package com.github.victools.jsonschema.generator.impl.module;
 
+import com.github.victools.jsonschema.generator.AbstractTypeAwareTest;
 import com.github.victools.jsonschema.generator.FieldScope;
 import com.github.victools.jsonschema.generator.SchemaGeneratorConfigBuilder;
 import com.github.victools.jsonschema.generator.SchemaGeneratorConfigPart;
-import com.github.victools.jsonschema.generator.AbstractTypeAwareTest;
+import com.github.victools.jsonschema.generator.SchemaVersion;
 import java.util.function.Predicate;
 import junitparams.JUnitParamsRunner;
 import junitparams.Parameters;
@@ -48,6 +49,7 @@ public class FieldExclusionModuleTest extends AbstractTypeAwareTest {
         this.builder = Mockito.mock(SchemaGeneratorConfigBuilder.class);
         this.fieldConfigPart = Mockito.spy(new SchemaGeneratorConfigPart<>());
         Mockito.when(builder.forFields()).thenReturn(this.fieldConfigPart);
+        this.prepareContextForVersion(SchemaVersion.DRAFT_2019_09);
     }
 
     @Test
diff --git a/src/test/java/com/github/victools/jsonschema/generator/impl/module/MethodExclusionModuleTest.java b/src/test/java/com/github/victools/jsonschema/generator/impl/module/MethodExclusionModuleTest.java
index 0caad5c..6799a5c 100644
--- a/src/test/java/com/github/victools/jsonschema/generator/impl/module/MethodExclusionModuleTest.java
+++ b/src/test/java/com/github/victools/jsonschema/generator/impl/module/MethodExclusionModuleTest.java
@@ -16,10 +16,11 @@
 
 package com.github.victools.jsonschema.generator.impl.module;
 
+import com.github.victools.jsonschema.generator.AbstractTypeAwareTest;
 import com.github.victools.jsonschema.generator.MethodScope;
 import com.github.victools.jsonschema.generator.SchemaGeneratorConfigBuilder;
 import com.github.victools.jsonschema.generator.SchemaGeneratorConfigPart;
-import com.github.victools.jsonschema.generator.AbstractTypeAwareTest;
+import com.github.victools.jsonschema.generator.SchemaVersion;
 import java.util.function.Predicate;
 import junitparams.JUnitParamsRunner;
 import junitparams.Parameters;
@@ -48,6 +49,7 @@ public class MethodExclusionModuleTest extends AbstractTypeAwareTest {
         this.builder = Mockito.mock(SchemaGeneratorConfigBuilder.class);
         this.methodConfigPart = Mockito.spy(new SchemaGeneratorConfigPart<>());
         Mockito.when(this.builder.forMethods()).thenReturn(this.methodConfigPart);
+        this.prepareContextForVersion(SchemaVersion.DRAFT_2019_09);
     }
 
     @Test
diff --git a/src/test/resources/com/github/victools/jsonschema/generator/circular-custom-definition-DRAFT_2019_09.json b/src/test/resources/com/github/victools/jsonschema/generator/circular-custom-definition-DRAFT_2019_09.json
new file mode 100644
index 0000000..f803957
--- /dev/null
+++ b/src/test/resources/com/github/victools/jsonschema/generator/circular-custom-definition-DRAFT_2019_09.json
@@ -0,0 +1,34 @@
+{
+    "$defs": {
+        "List(TestCircularClass1)-nullable": {
+            "type": ["object", "null"],
+            "properties": {
+                "get(0)": {
+                    "$ref": "#"
+                }
+            }
+        },
+        "List(TestCircularClass2)-nullable": {
+            "type": ["object", "null"],
+            "properties": {
+                "get(0)": {
+                    "$ref": "#/$defs/TestCircularClass2"
+                }
+            }
+        },
+        "TestCircularClass2": {
+            "type": "object",
+            "properties": {
+                "list1": {
+                    "$ref": "#/$defs/List(TestCircularClass1)-nullable"
+                }
+            }
+        }
+    },
+    "type": "object",
+    "properties": {
+        "list2": {
+            "$ref": "#/$defs/List(TestCircularClass2)-nullable"
+        }
+    }
+}