Repository: victools/jsonschema-generator
Pull request #58 (2020-03-27 23:20:10)

Jackson subtype resolution
Introducing subtype lookup in Jackson module:
- [x] Supporting `@JsonTypeInfo` and `@JsonSubTypes` on supertype
    - [x] Considering `@JsonTypeInfo.include` with values `As.PROPERTY`, `As.EXISTING_PROPERTY`, `As.WRAPPER_ARRAY`, `As.WRAPPER_OBJECT`
    - [x] Considering `@JsonTypeInfo.use` with values `Id.CLASS`, `Id.NAME` (expecting `@JsonTypeName` on subtype)
- [x] Supporting `@JsonTypeInfo` and `@JsonSubTypes` on properties as override of the per-type behaviour
    - [x] Considering `@JsonTypeInfo.include` with values `As.PROPERTY`, `As.EXISTING_PROPERTY`, `As.WRAPPER_ARRAY`, `As.WRAPPER_OBJECT`
    - [x] Considering `@JsonTypeInfo.use` with values `Id.CLASS`, `Id.NAME` (expecting `@JsonTypeName` on subtype)
    - [x] Applying per-property override even if field/method is a container type (i.e. array or collection)
- [x] Include unit tests for various combinations
- [x] extend module integration tests to also cover container items


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index e95b348..c3d0c34 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -10,6 +10,26 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 - Support for custom definitions in the scope of a particular field/method via `SchemaGeneratorConfigPart.withCustomDefinitionProvider()`
 - Ability to opt-out of normal "attribute collection" for custom definitions through new `CustomDefinition` constructor parameters
 
+#### Changed
+- Consolidate `anyOf` entries that only contain an `anyOf` themselves into the outer `anyOf` (mostly relevant for nullable entries with subtypes)
+- If a field/method is of a container type: apply the per-property configurations on its items; with `MemberScope.isFakeContainerItemScope()` flag
+
+#### Fixed
+- Should consider per-type attributes even on inline custom definitions
+- Use less strict `anyOf` instead of `oneOf` when indicating that a sub-schema may be of `"type": "null"`
+
+#### Dependency Update
+- `com.fasterxml.jackson.core`:`jackson-core`/`jackson-databind` from `2.10.2` to `2.10.3`
+- Remove dependencies to `log4j` implementation (only `slf4j-api` remains)
+
+### `jsonschema-module-jackson`
+#### Added
+- Look-up subtypes according to `@JsonTypeInfo` and `@JsonSubTypes` annotations per-type or overridden per-property:
+    - Considering `@JsonTypeInfo.include` with values `As.PROPERTY`, `As.EXISTING_PROPERTY`, `As.WRAPPER_ARRAY`, `As.WRAPPER_OBJECT`
+    - Considering `@JsonTypeInfo.use` with values `Id.NAME` (from `@JsonTypeName`) and `Id.CLASS`
+- New `JacksonOption.SKIP_SUBTYPE_LOOKUP` for disabling the new look-up of subtypes (i.e. to regain previous behaviour) if required
+- New `JacksonOption.IGNORE_TYPE_INFO_TRANSFORM` for disabling addition of extra property or wrapping in an array/object according to `@JsonTypeInfo`
+
 ## [4.7.0] - 2020-03-20
 ### `jsonschema-generator`
 #### Added
diff --git a/README.md b/README.md
index f74e3f0..cdd5108 100644
--- a/README.md
+++ b/README.md
@@ -7,11 +7,11 @@ Creating JSON Schema (Draft 7 or Draft 2019-09) from your Java classes utilising
 ----
 
 This project consists of:
-- the `jsonschema-generator` (the only thing you need to get started) – [find its README here](jsonschema-generator/README.md)
-- a few modules bundling some standard configurations for your convenience:
-    - [victools/jsonschema-module-jackson](jsonschema-module-jackson/README.md) – deriving JSON Schema attributes from `jackson` annotations (e.g. "description", property name overrides, what properties to ignore)
-    - [victools/jsonschema-module-javax-validation](jsonschema-module-javax-validation/README.md) – deriving JSON Schema attributes from `javax.validation` annotations (e.g. which properties are nullable or not, their "minimum"/"maximum", "minItems"/"maxItems", "minLength"/"maxLength")
-    - [victools/jsonschema-module-swagger-1.5](jsonschema-module-swagger-1.5/README.md) – deriving JSON Schema attributes from `swagger` (1.5.x) annotations (e.g. "description", property name overrides, what properties to ignore, their "minimum"/"maximum", "const"/"enum")
+- the [victools/jsonschema-generator](jsonschema-generator) (the only thing you need to get started)
+- a few modules bundling standard configurations for your convenience:
+    - [victools/jsonschema-module-jackson](jsonschema-module-jackson) – deriving JSON Schema attributes from `jackson` annotations (e.g. "description", property name overrides, what properties to ignore) as well as looking up appropriate (annotated) subtypes
+    - [victools/jsonschema-module-javax-validation](jsonschema-module-javax-validation) – deriving JSON Schema attributes from `javax.validation` annotations (e.g. which properties are nullable or not, their "minimum"/"maximum", "minItems"/"maxItems", "minLength"/"maxLength")
+    - [victools/jsonschema-module-swagger-1.5](jsonschema-module-swagger-1.5) – deriving JSON Schema attributes from `swagger` (1.5.x) annotations (e.g. "description", property name overrides, what properties to ignore, their "minimum"/"maximum", "const"/"enum")
 
 Another example for such a module is:
 - [imIfOu/jsonschema-module-addon](https://github.com/imIfOu/jsonschema-module-addon) – deriving JSON Schema attributes from a custom annotation with various parameters, which is part of the module.
@@ -29,6 +29,9 @@ Another example for such a module is:
 </dependency>
 ```
 
+Since version 4.7, the release versions of the main generator library and the (standard) `victools` modules listed above are aligned.
+It is recommended to use identical versions for all of them to ensure compatibility.
+
 ### Code
 #### Complete/Minimal Example
 ```java
diff --git a/jsonschema-generator/README.md b/jsonschema-generator/README.md
index 5ceb118..8902bc2 100644
--- a/jsonschema-generator/README.md
+++ b/jsonschema-generator/README.md
@@ -161,8 +161,8 @@ configBuilder.forTypesInGeneral()
 |    9 | `items` | Indicating the type of `array`/`Collection` elements. |
 |   10 | `required` | Listing the names of fields/methods that are deemed mandatory according to configuration (`SchemaGeneratorConfigPart.withRequiredCheck()`). |
 |   11 | `allOf` | Used to combine general attributes derived from the type itself with attributes collected in the respective context of the associated field/method. |
-|   12 | `anyOf` | Used to list alternatives according to configuration (`SchemaGeneratorGeneralConfigPart.withSubtypeResolver()`). |
-|   13 | `oneOf` | Used to indicate when a particular field/method can be of `type` `null`. |
+|   12 | `anyOf` | Used to list alternatives according to configuration (`SchemaGeneratorGeneralConfigPart.withSubtypeResolver()`) and to indicate when a particular field/method can be of `type` `null` according to configuration (`SchemaGeneratorConfigPart.withNullableCheck()`). |
+|   13 | `oneOf` | Is being considered when coming from a custom definition, but not added by default – the less strict `anyOf` is used instead. |
 |   14 | `title` | Collected value according to configuration (`SchemaGeneratorConfigPart.withTitleResolver()`). |
 |   15 | `description` | Collected value according to configuration (`SchemaGeneratorConfigPart.withDescriptionResolver()`). |
 |   16 | `const` | Collected value according to configuration (`SchemaGeneratorConfigPart.withEnumResolver()`) if only a single value was found. |
diff --git a/jsonschema-generator/pom.xml b/jsonschema-generator/pom.xml
index 5395d7f..2f590c6 100644
--- a/jsonschema-generator/pom.xml
+++ b/jsonschema-generator/pom.xml
@@ -26,27 +26,6 @@
             <groupId>com.fasterxml.jackson.core</groupId>
             <artifactId>jackson-databind</artifactId>
         </dependency>
-        <!-- log4j implementation is optional and left up to the library users to determine whether it is desired -->
-        <dependency>
-            <groupId>org.apache.logging.log4j</groupId>
-            <artifactId>log4j-api</artifactId>
-            <optional>true</optional>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.logging.log4j</groupId>
-            <artifactId>log4j-core</artifactId>
-            <optional>true</optional>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.logging.log4j</groupId>
-            <artifactId>log4j-slf4j-impl</artifactId>
-            <optional>true</optional>
-        </dependency>
-        <!-- slf4j api is included to define trace/debug logging -->
-        <dependency>
-            <groupId>org.slf4j</groupId>
-            <artifactId>slf4j-api</artifactId>
-        </dependency>
     </dependencies>
 
     <build>
diff --git a/jsonschema-generator/src/main/java/com/github/victools/jsonschema/generator/FieldScope.java b/jsonschema-generator/src/main/java/com/github/victools/jsonschema/generator/FieldScope.java
index 3b6af98..e79c9da 100644
--- a/jsonschema-generator/src/main/java/com/github/victools/jsonschema/generator/FieldScope.java
+++ b/jsonschema-generator/src/main/java/com/github/victools/jsonschema/generator/FieldScope.java
@@ -37,7 +37,7 @@ public class FieldScope extends MemberScope<ResolvedField, Field> {
      * @param context the overall type resolution context
      */
     protected FieldScope(ResolvedField field, ResolvedTypeWithMembers declaringTypeMembers, TypeContext context) {
-        this(field, null, null, declaringTypeMembers, context);
+        this(field, null, null, declaringTypeMembers, false, context);
     }
 
     /**
@@ -47,21 +47,24 @@ public class FieldScope extends MemberScope<ResolvedField, Field> {
      * @param overriddenType alternative type for this field
      * @param overriddenName alternative name for this field
      * @param declaringTypeMembers collection of the declaring type's (other) fields and methods
+     * @param fakeContainerItemScope whether this field/method scope represents only the container item type of the actual field/method
      * @param context the overall type resolution context
      */
     protected FieldScope(ResolvedField field, ResolvedType overriddenType, String overriddenName,
-            ResolvedTypeWithMembers declaringTypeMembers, TypeContext context) {
-        super(field, overriddenType, overriddenName, declaringTypeMembers, context);
+            ResolvedTypeWithMembers declaringTypeMembers, boolean fakeContainerItemScope, TypeContext context) {
+        super(field, overriddenType, overriddenName, declaringTypeMembers, fakeContainerItemScope, context);
     }
 
     @Override
     public FieldScope withOverriddenType(ResolvedType overriddenType) {
-        return new FieldScope(this.getMember(), overriddenType, this.getOverriddenName(), this.getDeclaringTypeMembers(), this.getContext());
+        return new FieldScope(this.getMember(), overriddenType, this.getOverriddenName(), this.getDeclaringTypeMembers(),
+                this.isFakeContainerItemScope(), this.getContext());
     }
 
     @Override
     public FieldScope withOverriddenName(String overriddenName) {
-        return new FieldScope(this.getMember(), this.getOverriddenType(), overriddenName, this.getDeclaringTypeMembers(), this.getContext());
+        return new FieldScope(this.getMember(), this.getOverriddenType(), overriddenName, this.getDeclaringTypeMembers(),
+                this.isFakeContainerItemScope(), this.getContext());
     }
 
     /**
diff --git a/jsonschema-generator/src/main/java/com/github/victools/jsonschema/generator/MemberScope.java b/jsonschema-generator/src/main/java/com/github/victools/jsonschema/generator/MemberScope.java
index 4b58859..7469f8e 100644
--- a/jsonschema-generator/src/main/java/com/github/victools/jsonschema/generator/MemberScope.java
+++ b/jsonschema-generator/src/main/java/com/github/victools/jsonschema/generator/MemberScope.java
@@ -37,6 +37,7 @@ public abstract class MemberScope<M extends ResolvedMember<T>, T extends Member>
     private final ResolvedType overriddenType;
     private final String overriddenName;
     private final ResolvedTypeWithMembers declaringTypeMembers;
+    private boolean fakeContainerItemScope;
     private final TypeContext context;
 
     /**
@@ -46,18 +47,29 @@ public abstract class MemberScope<M extends ResolvedMember<T>, T extends Member>
      * @param overriddenType alternative type for this field or method's return value
      * @param overriddenName alternative name for this field or method
      * @param declaringTypeMembers collection of the declaring type's (other) fields and methods
+     * @param fakeContainerItemScope whether this field/method scope represents only the container item type of the actual field/method
      * @param context the overall type resolution context
      */
     protected MemberScope(M member, ResolvedType overriddenType, String overriddenName,
-            ResolvedTypeWithMembers declaringTypeMembers, TypeContext context) {
+            ResolvedTypeWithMembers declaringTypeMembers, boolean fakeContainerItemScope, TypeContext context) {
         super(Optional.ofNullable(overriddenType).orElseGet(member::getType), context);
         this.member = member;
         this.overriddenType = overriddenType;
         this.overriddenName = overriddenName;
         this.declaringTypeMembers = declaringTypeMembers;
+        this.fakeContainerItemScope = fakeContainerItemScope;
         this.context = context;
     }
 
+    /**
+     * Set the {@code fakeContainerItemScope} flag to {@code true}.
+     *
+     * @see #isFakeContainerItemScope()
+     */
+    protected void markAsFakeContainerItemScope() {
+        this.fakeContainerItemScope = true;
+    }
+
     /**
      * Create another instance for this field or method and context, but overriding the declared field/method return type with the given one.
      *
@@ -68,6 +80,23 @@ public abstract class MemberScope<M extends ResolvedMember<T>, T extends Member>
      */
     public abstract MemberScope<M, T> withOverriddenType(ResolvedType overriddenType);
 
+    /**
+     * Create another instance for this field or method and context, representing its field/method return type's container item type.
+     *
+     * @return new instance with the container item type as override (or an identical copy if this is not a container type)
+     * @see #withOverriddenType(ResolvedType)
+     * @see #getContainerItemType()
+     * @see #isFakeContainerItemScope()
+     */
+    public MemberScope<M, T> asFakeContainerItemScope() {
+        if (!this.isContainerType()) {
+            return this.withOverriddenType(this.getOverriddenType());
+        }
+        MemberScope<M, T> result = this.withOverriddenType(this.getContainerItemType());
+        result.markAsFakeContainerItemScope();
+        return result;
+    }
+
     /**
      * Create another instance for this field or method and context, but overriding the declared field/method name with the given one.
      *
@@ -116,6 +145,15 @@ public abstract class MemberScope<M extends ResolvedMember<T>, T extends Member>
         return this.overriddenType;
     }
 
+    /**
+     * Check whether this field/method scope represents only the container item type of the actual field/method.
+     *
+     * @return whether this is not the actual field/method but a representation of its container item type
+     */
+    public boolean isFakeContainerItemScope() {
+        return this.fakeContainerItemScope;
+    }
+
     /**
      * Returns the member's name as specified in the declaring class.
      *
diff --git a/jsonschema-generator/src/main/java/com/github/victools/jsonschema/generator/MethodScope.java b/jsonschema-generator/src/main/java/com/github/victools/jsonschema/generator/MethodScope.java
index 019721a..1735cc8 100644
--- a/jsonschema-generator/src/main/java/com/github/victools/jsonschema/generator/MethodScope.java
+++ b/jsonschema-generator/src/main/java/com/github/victools/jsonschema/generator/MethodScope.java
@@ -39,7 +39,7 @@ public class MethodScope extends MemberScope<ResolvedMethod, Method> {
      * @param context the overall type resolution context
      */
     protected MethodScope(ResolvedMethod method, ResolvedTypeWithMembers declaringTypeMembers, TypeContext context) {
-        this(method, null, null, declaringTypeMembers, context);
+        this(method, null, null, declaringTypeMembers, false, context);
     }
 
     /**
@@ -49,21 +49,24 @@ public class MethodScope extends MemberScope<ResolvedMethod, Method> {
      * @param overriddenType alternative type for this method's return value
      * @param overriddenName alternative name for this method
      * @param declaringTypeMembers collection of the declaring type's fields and (other) methods
+     * @param fakeContainerItemScope whether this field/method scope represents only the container item type of the actual field/method
      * @param context the overall type resolution context
      */
     protected MethodScope(ResolvedMethod method, ResolvedType overriddenType, String overriddenName,
-            ResolvedTypeWithMembers declaringTypeMembers, TypeContext context) {
-        super(method, overriddenType, overriddenName, declaringTypeMembers, context);
+            ResolvedTypeWithMembers declaringTypeMembers, boolean fakeContainerItemScope, TypeContext context) {
+        super(method, overriddenType, overriddenName, declaringTypeMembers, fakeContainerItemScope, context);
     }
 
     @Override
     public MethodScope withOverriddenType(ResolvedType overriddenType) {
-        return new MethodScope(this.getMember(), overriddenType, this.getOverriddenName(), this.getDeclaringTypeMembers(), this.getContext());
+        return new MethodScope(this.getMember(), overriddenType, this.getOverriddenName(), this.getDeclaringTypeMembers(),
+                this.isFakeContainerItemScope(), this.getContext());
     }
 
     @Override
     public MethodScope withOverriddenName(String overriddenName) {
-        return new MethodScope(this.getMember(), this.getOverriddenType(), overriddenName, this.getDeclaringTypeMembers(), this.getContext());
+        return new MethodScope(this.getMember(), this.getOverriddenType(), overriddenName, this.getDeclaringTypeMembers(),
+                this.isFakeContainerItemScope(), this.getContext());
     }
 
     /**
diff --git a/jsonschema-generator/src/main/java/com/github/victools/jsonschema/generator/SchemaGenerator.java b/jsonschema-generator/src/main/java/com/github/victools/jsonschema/generator/SchemaGenerator.java
index c92199f..cc510e8 100644
--- a/jsonschema-generator/src/main/java/com/github/victools/jsonschema/generator/SchemaGenerator.java
+++ b/jsonschema-generator/src/main/java/com/github/victools/jsonschema/generator/SchemaGenerator.java
@@ -89,8 +89,12 @@ public class SchemaGenerator {
         ObjectNode mainSchemaNode = generationContext.getDefinition(mainKey);
         jsonSchemaResult.setAll(mainSchemaNode);
         if (this.config.shouldCleanupUnnecessaryAllOfElements()) {
-            this.discardUnnecessaryAllOfWrappers(jsonSchemaResult);
+            String allOfTagName = this.config.getKeyword(SchemaKeyword.TAG_ALLOF);
+            this.finaliseSchemaParts(jsonSchemaResult, nodeToCheck -> this.mergeAllOfPartsIfPossible(nodeToCheck, allOfTagName));
         }
+        String anyOfTagName = this.config.getKeyword(SchemaKeyword.TAG_ANYOF);
+        this.finaliseSchemaParts(jsonSchemaResult, nodeToCheck -> this.reduceAnyOfWrappersIfPossible(nodeToCheck, anyOfTagName));
+
         return jsonSchemaResult;
     }
 
@@ -237,14 +241,15 @@ public class SchemaGenerator {
     }
 
     /**
-     * Iterate through a generated and fully populated schema and remove extraneous {@link SchemaKeyword#TAG_ALLOF} nodes, that are included due to
-     * the way how type references are handled during schema generation but are strictly not necessary. This makes for more readable schemas being
-     * generated but has the side-effect that any manually added {@link SchemaKeyword#TAG_ALLOF} (e.g. through a custom definition of attribute
-     * overrides) may be removed as well if it isn't strictly speaking necessary.
+     * Iterate through a generated and fully populated schema and remove extraneous {@link SchemaKeyword#TAG_ANYOF} nodes, where one entry of the
+     * array is again a {@link SchemaKeyword#TAG_ANYOF} wrapper and nothing else. This makes for more readable schemas being generated but has the
+     * side-effect that any manually added {@link SchemaKeyword#TAG_ANYOF} (e.g. through a custom definition of attribute overrides) may be removed as
+     * well if it isn't strictly speaking necessary.
      *
      * @param schemaNode generated schema to clean-up
+     * @param performCleanUpOnSingleSchemaNode clean up task to execute before looking for deeper nested sub-schemas for which to apply the same
      */
-    private void discardUnnecessaryAllOfWrappers(ObjectNode schemaNode) {
+    private void finaliseSchemaParts(ObjectNode schemaNode, Consumer<ObjectNode> performCleanUpOnSingleSchemaNode) {
         List<ObjectNode> nextNodesToCheck = new ArrayList<>();
         Consumer<JsonNode> addNodeToCheck = node -> {
             if (node instanceof ObjectNode) {
@@ -252,12 +257,10 @@ public class SchemaGenerator {
             }
         };
         nextNodesToCheck.add(schemaNode);
-        SchemaVersion schemaVersion = this.config.getSchemaVersion();
-        Optional.ofNullable(schemaNode.get(SchemaKeyword.TAG_DEFINITIONS.forVersion(schemaVersion)))
+        Optional.ofNullable(schemaNode.get(this.config.getKeyword(SchemaKeyword.TAG_DEFINITIONS)))
                 .filter(definitions -> definitions instanceof ObjectNode)
                 .ifPresent(definitions -> ((ObjectNode) definitions).forEach(addNodeToCheck));
 
-        String allOfTagName = SchemaKeyword.TAG_ALLOF.forVersion(schemaVersion);
         Set<String> tagsWithSchemas = this.getTagNamesContainingSchema();
         Set<String> tagsWithSchemaArrays = this.getTagNamesContainingSchemaArray();
         Set<String> tagsWithSchemaObjects = this.getTagNamesContainingSchemaObject();
@@ -265,7 +268,7 @@ public class SchemaGenerator {
             List<ObjectNode> currentNodesToCheck = new ArrayList<>(nextNodesToCheck);
             nextNodesToCheck.clear();
             for (ObjectNode nodeToCheck : currentNodesToCheck) {
-                this.mergeAllOfPartsIfPossible(nodeToCheck, allOfTagName);
+                performCleanUpOnSingleSchemaNode.accept(nodeToCheck);
                 tagsWithSchemas.stream().map(nodeToCheck::get).forEach(addNodeToCheck);
                 tagsWithSchemaArrays.stream()
                         .map(nodeToCheck::get)
@@ -282,6 +285,9 @@ public class SchemaGenerator {
     /**
      * Check whether the given schema node and its {@link SchemaKeyword#TAG_ALLOF} elements (if there are any) are distinct. If yes, remove the
      * {@link SchemaKeyword#TAG_ALLOF} node and merge all its elements with the given schema node instead.
+     * <br>
+     * This makes for more readable schemas being generated but has the side-effect that manually added {@link SchemaKeyword#TAG_ALLOF} (e.g. from a
+     * custom definition or attribute overrides) may be removed as well if it isn't strictly speaking necessary.
      *
      * @param schemaNode single node representing a sub-schema to consolidate contained {@link SchemaKeyword#TAG_ALLOF} for (if present)
      * @param allOfTagName name of the {@link SchemaKeyword#TAG_ALLOF} in the designated JSON Schema version
@@ -323,4 +329,40 @@ public class SchemaGenerator {
             parts.forEach(schemaObjectNode::setAll);
         }
     }
+
+    /**
+     * Check whether the given schema node contains a {@link SchemaKeyword#TAG_ANYOF} element which in turn contains an entry with only another
+     * {@link SchemaKeyword#TAG_ANYOF} inside. If yes, move the entries from the inner array up to the outer one.
+     * <br>
+     * This makes for more readable schemas being generated but has the side-effect that manually added {@link SchemaKeyword#TAG_ANYOF} entries (e.g.
+     * from a custom definition or attribute overrides) may be removed as well if it isn't strictly speaking necessary.
+     *
+     * @param schemaNode single node representing a sub-schema to consolidate contained {@link SchemaKeyword#TAG_ANYOF} for (if present)
+     * @param anyOfTagName name of the {@link SchemaKeyword#TAG_ANYOF} in the designated JSON Schema version
+     */
+    private void reduceAnyOfWrappersIfPossible(JsonNode schemaNode, String anyOfTagName) {
+        if (!(schemaNode instanceof ObjectNode)) {
+            return;
+        }
+        JsonNode anyOfTag = schemaNode.get(anyOfTagName);
+        if (!(anyOfTag instanceof ArrayNode)) {
+            return;
+        }
+        anyOfTag.forEach(part -> this.reduceAnyOfWrappersIfPossible(part, anyOfTagName));
+
+        for (int index = anyOfTag.size() - 1; index > -1; index--) {
+            JsonNode arrayEntry = anyOfTag.get(index);
+            if (!(arrayEntry instanceof ObjectNode) || arrayEntry.size() != 1) {
+                continue;
+            }
+            JsonNode nestedAnyOf = arrayEntry.get(anyOfTagName);
+            if (!(nestedAnyOf instanceof ArrayNode)) {
+                continue;
+            }
+            ((ArrayNode) anyOfTag).remove(index);
+            for (int nestedEntryIndex = nestedAnyOf.size() - 1; nestedEntryIndex > -1; nestedEntryIndex--) {
+                ((ArrayNode) anyOfTag).insert(index, nestedAnyOf.get(nestedEntryIndex));
+            }
+        }
+    }
 }
diff --git a/jsonschema-generator/src/main/java/com/github/victools/jsonschema/generator/impl/SchemaGenerationContextImpl.java b/jsonschema-generator/src/main/java/com/github/victools/jsonschema/generator/impl/SchemaGenerationContextImpl.java
index 5d0d05d..054827b 100644
--- a/jsonschema-generator/src/main/java/com/github/victools/jsonschema/generator/impl/SchemaGenerationContextImpl.java
+++ b/jsonschema-generator/src/main/java/com/github/victools/jsonschema/generator/impl/SchemaGenerationContextImpl.java
@@ -293,7 +293,7 @@ public class SchemaGenerationContextImpl implements SchemaGenerationContext {
                 includeTypeAttributes = customDefinition.shouldIncludeAttributes();
             } else if (isContainerType) {
                 logger.debug("generating array definition for {}", targetType);
-                this.generateArrayDefinition(targetType, definition, isNullable);
+                this.generateArrayDefinition(scope, definition, isNullable);
                 includeTypeAttributes = true;
             } else {
                 logger.debug("generating definition for {}", targetType);
@@ -339,10 +339,10 @@ public class SchemaGenerationContextImpl implements SchemaGenerationContext {
     }
 
     /**
-     * Collect the specified value(s) from the given definition's "{@value SchemaConstants#TAG_TYPE}" attribute.
+     * Collect the specified value(s) from the given definition's {@link SchemaKeyword#TAG_TYPE} attribute.
      *
-     * @param definition type definition to extract specified "{@value SchemaConstants#TAG_TYPE}" values from
-     * @return extracted "{@value SchemaConstants#TAG_TYPE}" values (may be empty)
+     * @param definition type definition to extract specified {@link SchemaKeyword#TAG_TYPE} values from
+     * @return extracted {@link SchemaKeyword#TAG_TYPE} – values (may be empty)
      */
     private Set<String> collectAllowedSchemaTypes(ObjectNode definition) {
         JsonNode declaredTypes = definition.get(this.getKeyword(SchemaKeyword.TAG_TYPE));
@@ -362,11 +362,11 @@ public class SchemaGenerationContextImpl implements SchemaGenerationContext {
     /**
      * Preparation Step: add the given targetType (which was previously determined to be an array type).
      *
-     * @param targetType (possibly generic) array type to add
+     * @param targetScope (possibly generic) array type to add
      * @param definition node in the JSON schema to which all collected attributes should be added
      * @param isNullable whether the field/method's return value the targetType refers to is allowed to be null in the declaring type
      */
-    private void generateArrayDefinition(ResolvedType targetType, ObjectNode definition, boolean isNullable) {
+    private void generateArrayDefinition(TypeScope targetScope, ObjectNode definition, boolean isNullable) {
         if (isNullable) {
             ArrayNode typeArray = this.generatorConfig.createArrayNode()
                     .add(this.getKeyword(SchemaKeyword.TAG_TYPE_ARRAY))
@@ -375,10 +375,22 @@ public class SchemaGenerationContextImpl implements SchemaGenerationContext {
         } else {
             definition.put(this.getKeyword(SchemaKeyword.TAG_TYPE), this.getKeyword(SchemaKeyword.TAG_TYPE_ARRAY));
         }
-        ObjectNode arrayItemTypeRef = this.generatorConfig.createObjectNode();
-        definition.set(this.getKeyword(SchemaKeyword.TAG_ITEMS), arrayItemTypeRef);
-        ResolvedType itemType = this.typeContext.getContainerItemType(targetType);
-        this.traverseGenericType(itemType, arrayItemTypeRef, false);
+        if (targetScope instanceof MemberScope<?, ?> && !((MemberScope<?, ?>) targetScope).isFakeContainerItemScope()) {
+            MemberScope<?, ?> fakeArrayItemMember = ((MemberScope<?, ?>) targetScope).asFakeContainerItemScope();
+            Map<String, JsonNode> fakeItemDefinition = new HashMap<>();
+            if (targetScope instanceof FieldScope) {
+                this.populateField((FieldScope) fakeArrayItemMember, fakeItemDefinition, null);
+            } else if (targetScope instanceof MethodScope) {
+                this.populateMethod((MethodScope) fakeArrayItemMember, fakeItemDefinition, null);
+            } else {
+                throw new IllegalStateException("Unsupported member type: " + targetScope.getClass().getName());
+            }
+            definition.set(this.getKeyword(SchemaKeyword.TAG_ITEMS), fakeItemDefinition.values().iterator().next());
+        } else {
+            ObjectNode arrayItemTypeRef = this.generatorConfig.createObjectNode();
+            definition.set(this.getKeyword(SchemaKeyword.TAG_ITEMS), arrayItemTypeRef);
+            this.traverseGenericType(targetScope.getContainerItemType(), arrayItemTypeRef, false);
+        }
     }
 
     /**
@@ -495,18 +507,28 @@ public class SchemaGenerationContextImpl implements SchemaGenerationContext {
      * @param requiredProperties set of properties value required
      */
     private void populateField(FieldScope field, Map<String, JsonNode> collectedFields, Set<String> requiredProperties) {
-        String propertyNameOverride = this.generatorConfig.resolvePropertyNameOverride(field);
-        FieldScope fieldWithNameOverride = propertyNameOverride == null ? field : field.withOverriddenName(propertyNameOverride);
-        String propertyName = fieldWithNameOverride.getSchemaPropertyName();
-        if (this.generatorConfig.isRequired(field)) {
-            requiredProperties.add(propertyName);
-        }
-        if (collectedFields.containsKey(propertyName)) {
-            logger.debug("ignoring overridden {}.{}", fieldWithNameOverride.getDeclaringType(), fieldWithNameOverride.getDeclaredName());
-            return;
+        final FieldScope fieldWithNameOverride;
+        final String propertyName;
+        if (field.isFakeContainerItemScope()) {
+            fieldWithNameOverride = field;
+            propertyName = field.getSchemaPropertyName();
+        } else {
+            String propertyNameOverride = this.generatorConfig.resolvePropertyNameOverride(field);
+            fieldWithNameOverride = propertyNameOverride == null ? field : field.withOverriddenName(propertyNameOverride);
+            propertyName = fieldWithNameOverride.getSchemaPropertyName();
+            if (this.generatorConfig.isRequired(field)) {
+                requiredProperties.add(propertyName);
+            }
+            if (collectedFields.containsKey(propertyName)) {
+                logger.debug("ignoring overridden {}.{}", fieldWithNameOverride.getDeclaringType(), fieldWithNameOverride.getDeclaredName());
+                return;
+            }
         }
 
         List<ResolvedType> typeOverrides = this.generatorConfig.resolveTargetTypeOverrides(fieldWithNameOverride);
+        if (typeOverrides == null) {
+            typeOverrides = this.generatorConfig.resolveSubtypes(fieldWithNameOverride.getType(), this);
+        }
         List<FieldScope> fieldOptions;
         if (typeOverrides == null || typeOverrides.isEmpty()) {
             fieldOptions = Collections.singletonList(fieldWithNameOverride);
@@ -516,7 +538,7 @@ public class SchemaGenerationContextImpl implements SchemaGenerationContext {
                     .collect(Collectors.toList());
         }
         // consider declared type (instead of overridden one) for determining null-ability
-        boolean isNullable = !field.getRawMember().isEnumConstant() && this.generatorConfig.isNullable(field);
+        boolean isNullable = !field.getRawMember().isEnumConstant() && !field.isFakeContainerItemScope() && this.generatorConfig.isNullable(field);
         if (fieldOptions.size() == 1) {
             collectedFields.put(propertyName, this.createFieldSchema(fieldOptions.get(0), isNullable, null));
         } else {
@@ -524,8 +546,8 @@ public class SchemaGenerationContextImpl implements SchemaGenerationContext {
             collectedFields.put(propertyName, subSchema);
             ArrayNode anyOfArray = subSchema.withArray(this.getKeyword(SchemaKeyword.TAG_ANYOF));
             if (isNullable) {
-                anyOfArray.add(this.generatorConfig.createObjectNode()
-                        .put(this.getKeyword(SchemaKeyword.TAG_TYPE), this.getKeyword(SchemaKeyword.TAG_TYPE_NULL)));
+                anyOfArray.addObject()
+                        .put(this.getKeyword(SchemaKeyword.TAG_TYPE), this.getKeyword(SchemaKeyword.TAG_TYPE_NULL));
             }
             fieldOptions.forEach(option -> anyOfArray.add(this.createFieldSchema(option, false, null)));
         }
@@ -555,18 +577,28 @@ public class SchemaGenerationContextImpl implements SchemaGenerationContext {
      * @param requiredProperties set of properties value required
      */
     private void populateMethod(MethodScope method, Map<String, JsonNode> collectedMethods, Set<String> requiredProperties) {
-        String propertyNameOverride = this.generatorConfig.resolvePropertyNameOverride(method);
-        MethodScope methodWithNameOverride = propertyNameOverride == null ? method : method.withOverriddenName(propertyNameOverride);
-        String propertyName = methodWithNameOverride.getSchemaPropertyName();
-        if (this.generatorConfig.isRequired(method)) {
-            requiredProperties.add(propertyName);
-        }
-        if (collectedMethods.containsKey(propertyName)) {
-            logger.debug("ignoring overridden {}.{}", methodWithNameOverride.getDeclaringType(), methodWithNameOverride.getDeclaredName());
-            return;
+        final MethodScope methodWithNameOverride;
+        final String propertyName;
+        if (method.isFakeContainerItemScope()) {
+            methodWithNameOverride = method;
+            propertyName = method.getSchemaPropertyName();
+        } else {
+            String propertyNameOverride = this.generatorConfig.resolvePropertyNameOverride(method);
+            methodWithNameOverride = propertyNameOverride == null ? method : method.withOverriddenName(propertyNameOverride);
+            propertyName = methodWithNameOverride.getSchemaPropertyName();
+            if (this.generatorConfig.isRequired(method)) {
+                requiredProperties.add(propertyName);
+            }
+            if (collectedMethods.containsKey(propertyName)) {
+                logger.debug("ignoring overridden {}.{}", methodWithNameOverride.getDeclaringType(), methodWithNameOverride.getDeclaredName());
+                return;
+            }
         }
 
         List<ResolvedType> typeOverrides = this.generatorConfig.resolveTargetTypeOverrides(methodWithNameOverride);
+        if (typeOverrides == null && !methodWithNameOverride.isVoid()) {
+            typeOverrides = this.generatorConfig.resolveSubtypes(methodWithNameOverride.getType(), this);
+        }
         List<MethodScope> methodOptions;
         if (typeOverrides == null || typeOverrides.isEmpty()) {
             methodOptions = Collections.singletonList(methodWithNameOverride);
@@ -576,7 +608,8 @@ public class SchemaGenerationContextImpl implements SchemaGenerationContext {
                     .collect(Collectors.toList());
         }
         // consider declared type (instead of overridden one) for determining null-ability
-        boolean isNullable = methodWithNameOverride.isVoid() || this.generatorConfig.isNullable(methodWithNameOverride);
+        boolean isNullable = methodWithNameOverride.isVoid()
+                || !method.isFakeContainerItemScope() && this.generatorConfig.isNullable(methodWithNameOverride);
         if (methodOptions.size() == 1) {
             collectedMethods.put(propertyName, this.createMethodSchema(methodOptions.get(0), isNullable, null));
         } else {
@@ -627,8 +660,16 @@ public class SchemaGenerationContextImpl implements SchemaGenerationContext {
         final CustomDefinition customDefinition = this.generatorConfig.getCustomDefinition(scope, this, ignoredDefinitionProvider);
         if (customDefinition != null && customDefinition.isMeantToBeInline()) {
             targetNode.setAll(customDefinition.getValue());
-            if (customDefinition.shouldIncludeAttributes() && collectedAttributes != null && collectedAttributes.size() > 0) {
-                targetNode.setAll(collectedAttributes);
+            if (customDefinition.shouldIncludeAttributes()) {
+                if (collectedAttributes != null && collectedAttributes.size() > 0) {
+                    targetNode.setAll(collectedAttributes);
+                }
+                Set<String> allowedSchemaTypes = this.collectAllowedSchemaTypes(targetNode);
+                ObjectNode typeAttributes = AttributeCollector.collectTypeAttributes(scope, this, allowedSchemaTypes);
+                // ensure no existing attributes in the 'definition' are replaced, by way of first overriding any conflicts the other way around
+                typeAttributes.setAll(targetNode);
+                // apply merged attributes
+                targetNode.setAll(typeAttributes);
             }
             if (isNullable) {
                 this.makeNullable(targetNode);
@@ -670,14 +711,14 @@ public class SchemaGenerationContextImpl implements SchemaGenerationContext {
             // cannot be sure what is specified in those other schema parts, instead simply create a oneOf wrapper
             ObjectNode nullSchema = this.generatorConfig.createObjectNode()
                     .put(this.getKeyword(SchemaKeyword.TAG_TYPE), this.getKeyword(SchemaKeyword.TAG_TYPE_NULL));
-            ArrayNode oneOf = this.generatorConfig.createArrayNode()
+            ArrayNode anyOf = this.generatorConfig.createArrayNode()
                     // one option in the oneOf should be null
                     .add(nullSchema)
                     // the other option is the given (assumed to be) not-nullable node
                     .add(this.generatorConfig.createObjectNode().setAll(node));
             // replace all existing (and already copied properties with the oneOf wrapper
             node.removeAll();
-            node.set(this.getKeyword(SchemaKeyword.TAG_ONEOF), oneOf);
+            node.set(this.getKeyword(SchemaKeyword.TAG_ANYOF), anyOf);
         } else {
             // given node is a simple schema, we can simply adjust its "type" attribute
             JsonNode fixedJsonSchemaType = node.get(this.getKeyword(SchemaKeyword.TAG_TYPE));
diff --git a/jsonschema-module-jackson/README.md b/jsonschema-module-jackson/README.md
index e2434a2..a8a9362 100644
--- a/jsonschema-module-jackson/README.md
+++ b/jsonschema-module-jackson/README.md
@@ -1,7 +1,7 @@
 # Java JSON Schema Generation – Module jackson
 [![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.github.victools/jsonschema-module-jackson/badge.svg)](https://maven-badges.herokuapp.com/maven-central/com.github.victools/jsonschema-module-jackson)
 
-Module for the `jsonschema-generator` – deriving JSON Schema attributes from `jackson` annotations.
+Module for the [jsonschema-generator](../jsonschema-generator) – deriving JSON Schema attributes from `jackson` annotations.
 
 ## Features
 1. Populate a field/method's "description" as per `@JsonPropertyDescription`
@@ -10,6 +10,9 @@ Module for the `jsonschema-generator` – deriving JSON Schema attributes from `
 4. Ignore fields that are marked with a `@JsonBackReference` annotation.
 5. Ignore fields that are deemed to be ignored according to various other `jackson-annotations` (e.g. `@JsonIgnore`, `@JsonIgnoreType`, `@JsonIgnoreProperties`) or are otherwise supposed to be excluded.
 6. Optionally: treat enum types as plain strings, serialized by `@JsonValue` annotated method
+7. Subtype resolution through `@JsonTypeInfo` and `@JsonSubTypes` on supertype or directly on properties as override of the per-type behaviour
+    - Considering `@JsonTypeInfo.include` with values `As.PROPERTY`, `As.EXISTING_PROPERTY`, `As.WRAPPER_ARRAY`, `As.WRAPPER_OBJECT`
+    - Considering `@JsonTypeInfo.use` with values `Id.CLASS`, `Id.NAME`
 
 Schema attributes derived from validation annotations on getter methods are also applied to their associated fields.
 
@@ -23,6 +26,9 @@ Schema attributes derived from validation annotations on getter methods are also
 </dependency>
 ```
 
+Since version 4.7, the release versions of the main generator library and this module are aligned.
+It is recommended to use identical versions for both dependencies to ensure compatibility.
+
 ### Code
 #### Passing into SchemaGeneratorConfigBuilder.with(Module)
 ```java
diff --git a/jsonschema-module-jackson/src/main/java/com/github/victools/jsonschema/module/jackson/JacksonModule.java b/jsonschema-module-jackson/src/main/java/com/github/victools/jsonschema/module/jackson/JacksonModule.java
index 87edb1b..6842f21 100644
--- a/jsonschema-module-jackson/src/main/java/com/github/victools/jsonschema/module/jackson/JacksonModule.java
+++ b/jsonschema-module-jackson/src/main/java/com/github/victools/jsonschema/module/jackson/JacksonModule.java
@@ -24,6 +24,7 @@ import com.fasterxml.jackson.annotation.JsonPropertyDescription;
 import com.fasterxml.jackson.databind.BeanDescription;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.github.victools.jsonschema.generator.FieldScope;
+import com.github.victools.jsonschema.generator.MethodScope;
 import com.github.victools.jsonschema.generator.Module;
 import com.github.victools.jsonschema.generator.SchemaGeneratorConfigBuilder;
 import com.github.victools.jsonschema.generator.SchemaGeneratorConfigPart;
@@ -82,6 +83,22 @@ public class JacksonModule implements Module {
         if (this.options.contains(JacksonOption.FLATTENED_ENUMS_FROM_JSONVALUE)) {
             generalConfigPart.withCustomDefinitionProvider(new CustomEnumJsonValueDefinitionProvider());
         }
+        boolean lookUpSubtypes = !this.options.contains(JacksonOption.SKIP_SUBTYPE_LOOKUP);
+        boolean includeTypeInfoTransform = !this.options.contains(JacksonOption.IGNORE_TYPE_INFO_TRANSFORM);
+        if (lookUpSubtypes || includeTypeInfoTransform) {
+            JsonSubTypesResolver subtypeResolver = new JsonSubTypesResolver();
+            SchemaGeneratorConfigPart<MethodScope> methodConfigPart = builder.forMethods();
+            if (lookUpSubtypes) {
+                generalConfigPart.withSubtypeResolver(subtypeResolver);
+                fieldConfigPart.withTargetTypeOverridesResolver(subtypeResolver::findTargetTypeOverrides);
+                methodConfigPart.withTargetTypeOverridesResolver(subtypeResolver::findTargetTypeOverrides);
+            }
+            if (includeTypeInfoTransform) {
+                generalConfigPart.withCustomDefinitionProvider(subtypeResolver);
+                fieldConfigPart.withCustomDefinitionProvider(subtypeResolver::provideCustomPropertySchemaDefinition);
+                methodConfigPart.withCustomDefinitionProvider(subtypeResolver::provideCustomPropertySchemaDefinition);
+            }
+        }
     }
 
     /**
@@ -95,6 +112,9 @@ public class JacksonModule implements Module {
      * @return successfully looked-up description (or {@code null})
      */
     protected String resolveDescription(FieldScope field) {
+        if (field.isFakeContainerItemScope()) {
+            return null;
+        }
         // look for property specific description
         JsonPropertyDescription propertyAnnotation = field.getAnnotationConsideringFieldAndGetter(JsonPropertyDescription.class);
         if (propertyAnnotation != null) {
diff --git a/jsonschema-module-jackson/src/main/java/com/github/victools/jsonschema/module/jackson/JacksonOption.java b/jsonschema-module-jackson/src/main/java/com/github/victools/jsonschema/module/jackson/JacksonOption.java
index caeaf20..fbc22c1 100644
--- a/jsonschema-module-jackson/src/main/java/com/github/victools/jsonschema/module/jackson/JacksonOption.java
+++ b/jsonschema-module-jackson/src/main/java/com/github/victools/jsonschema/module/jackson/JacksonOption.java
@@ -28,5 +28,13 @@ public enum JacksonOption {
      * @see com.github.victools.jsonschema.generator.Option#FLATTENED_ENUMS
      * @see com.github.victools.jsonschema.generator.Option#FLATTENED_ENUMS_FROM_TOSTRING
      */
-    FLATTENED_ENUMS_FROM_JSONVALUE;
+    FLATTENED_ENUMS_FROM_JSONVALUE,
+    /**
+     * Use this option to skip the automatic look-up of subtypes according to {@code @JsonSubTypes} annotations.
+     */
+    SKIP_SUBTYPE_LOOKUP,
+    /**
+     * Use this option to skip the transformation according to {@code @JsonTypeInfo} annotations (typically used to identify specific subtypes).
+     */
+    IGNORE_TYPE_INFO_TRANSFORM;
 }
diff --git a/jsonschema-module-jackson/src/main/java/com/github/victools/jsonschema/module/jackson/JsonSubTypesResolver.java b/jsonschema-module-jackson/src/main/java/com/github/victools/jsonschema/module/jackson/JsonSubTypesResolver.java
new file mode 100644
index 0000000..8ca40e2
--- /dev/null
+++ b/jsonschema-module-jackson/src/main/java/com/github/victools/jsonschema/module/jackson/JsonSubTypesResolver.java
@@ -0,0 +1,257 @@
+/*
+ * 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.module.jackson;
+
+import com.fasterxml.classmate.ResolvedType;
+import com.fasterxml.jackson.annotation.JsonSubTypes;
+import com.fasterxml.jackson.annotation.JsonTypeInfo;
+import com.fasterxml.jackson.annotation.JsonTypeName;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.github.victools.jsonschema.generator.CustomDefinition;
+import com.github.victools.jsonschema.generator.CustomDefinitionProviderV2;
+import com.github.victools.jsonschema.generator.CustomPropertyDefinition;
+import com.github.victools.jsonschema.generator.FieldScope;
+import com.github.victools.jsonschema.generator.MemberScope;
+import com.github.victools.jsonschema.generator.MethodScope;
+import com.github.victools.jsonschema.generator.SchemaGenerationContext;
+import com.github.victools.jsonschema.generator.SchemaKeyword;
+import com.github.victools.jsonschema.generator.SubtypeResolver;
+import com.github.victools.jsonschema.generator.TypeContext;
+import com.github.victools.jsonschema.generator.impl.AttributeCollector;
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+/**
+ * Look-up of subtypes from a {@link JsonSubTypes} annotation.
+ */
+public class JsonSubTypesResolver implements SubtypeResolver, CustomDefinitionProviderV2 {
+
+    /*
+     * Looking-up declared subtypes for encountered supertype in general.
+     */
+    @Override
+    public List<ResolvedType> findSubtypes(ResolvedType declaredType, SchemaGenerationContext context) {
+        JsonSubTypes subtypesAnnotation = declaredType.getErasedType().getAnnotation(JsonSubTypes.class);
+        return this.lookUpSubtypesFromAnnotation(declaredType, subtypesAnnotation, context.getTypeContext());
+    }
+
+    /**
+     * Look-up applicable subtypes for the given field/method if there is a {@link JsonSubTypes} annotation.
+     *
+     * @param property targeted field/method
+     * @return list of annotated subtypes (or {@code null} if there is no {@link JsonSubTypes} annotation)
+     */
+    public List<ResolvedType> findTargetTypeOverrides(MemberScope<?, ?> property) {
+        if (property.getType() == null) {
+            return null;
+        }
+        JsonSubTypes subtypesAnnotation = property.getAnnotationConsideringFieldAndGetter(JsonSubTypes.class);
+        return this.lookUpSubtypesFromAnnotation(property.getType(), subtypesAnnotation, property.getContext());
+    }
+
+    /**
+     * Mapping the declared erased types from the annotation to resolved types. Returns {@code null} if annotation is {@code null}/not present.
+     *
+     * @param declaredType supertype encountered while generating a schema
+     * @param subtypesAnnotation annotation to derive applicable subtypes from.
+     * @param context type context for proper type resolution
+     * @return resolved annotated subtypes (or {@code null} if given annotation is {@code null})
+     */
+    public List<ResolvedType> lookUpSubtypesFromAnnotation(ResolvedType declaredType, JsonSubTypes subtypesAnnotation, TypeContext context) {
+        if (subtypesAnnotation == null) {
+            return null;
+        }
+        return Stream.of(subtypesAnnotation.value())
+                .map(entry -> this.resolveSubtype(declaredType, entry, context))
+                .collect(Collectors.toList());
+    }
+
+    /**
+     * Safe way of resolving an erased subtype from its supertype. If the subtype introduces generic parameters not present on the supertype, the
+     * subtype will be resolved without any type parameters – for simplicity's sake not even the ones declared alongside the supertype then.
+     *
+     * @param declaredType supertype encountered while generating a schema
+     * @param annotatedSubtype single subtype declared via {@link JsonSubTypes} on the super class
+     * @param context type context for proper type resolution
+     * @return resolved annotated subtype
+     */
+    private ResolvedType resolveSubtype(ResolvedType declaredType, JsonSubTypes.Type annotatedSubtype, TypeContext context) {
+        try {
+            return context.resolveSubtype(declaredType, annotatedSubtype.value());
+        } catch (IllegalArgumentException ex) {
+            return context.resolve(annotatedSubtype.value());
+        }
+    }
+
+    /*
+     * Providing custom schema definition for subtype.
+     */
+    @Override
+    public CustomDefinition provideCustomSchemaDefinition(ResolvedType javaType, SchemaGenerationContext context) {
+        Class<?> targetSuperType = javaType.getErasedType();
+        JsonTypeInfo typeInfoAnnotation;
+        do {
+            typeInfoAnnotation = targetSuperType.getAnnotation(JsonTypeInfo.class);
+            targetSuperType = targetSuperType.getSuperclass();
+        } while (typeInfoAnnotation == null && targetSuperType != null);
+
+        if (typeInfoAnnotation == null || javaType.getErasedType().getDeclaredAnnotation(JsonSubTypes.class) != null) {
+            return null;
+        }
+        ObjectNode definition = this.createSubtypeDefinition(javaType, typeInfoAnnotation, null, context);
+        if (definition == null) {
+            return null;
+        }
+        return new CustomDefinition(definition, CustomDefinition.DefinitionType.STANDARD, CustomDefinition.AttributeInclusion.NO);
+    }
+
+    /**
+     * Providing custom schema definition for field/method in case of a per-property override of the applicable subtypes or how they are serialized.
+     *
+     * @param scope targeted field or method
+     * @param context generation context
+     * @return applicable custom per-property override schema definition (may be {@code null})
+     */
+    public CustomPropertyDefinition provideCustomPropertySchemaDefinition(MemberScope<?, ?> scope, SchemaGenerationContext context) {
+        JsonTypeInfo typeInfoAnnotation = scope.getAnnotationConsideringFieldAndGetter(JsonTypeInfo.class);
+        if (typeInfoAnnotation == null || scope.getType().getErasedType().getDeclaredAnnotation(JsonSubTypes.class) != null) {
+            return null;
+        }
+        ObjectNode attributes;
+        if (scope instanceof FieldScope) {
+            attributes = AttributeCollector.collectFieldAttributes((FieldScope) scope, context);
+        } else if (scope instanceof MethodScope) {
+            attributes = AttributeCollector.collectMethodAttributes((MethodScope) scope, context);
+        } else {
+            attributes = null;
+        }
+        ObjectNode definition = this.createSubtypeDefinition(scope.getType(), typeInfoAnnotation, attributes, context);
+        if (definition == null) {
+            return null;
+        }
+        return new CustomPropertyDefinition(definition, CustomDefinition.AttributeInclusion.NO);
+    }
+
+    /**
+     * Determine the appropriate type identifier according to {@link JsonTypeInfo#use()}.
+     *
+     * @param javaType specific subtype to identify
+     * @param typeInfoAnnotation annotation for determining what kind of identifier to use
+     * @return type identifier (or {@code null} if no supported value could be found)
+     */
+    private String getTypeIdentifier(ResolvedType javaType, JsonTypeInfo typeInfoAnnotation) {
+        Class<?> erasedTargetType = javaType.getErasedType();
+        final String typeIdentifier;
+        switch (typeInfoAnnotation.use()) {
+        case NAME:
+            typeIdentifier = Optional.ofNullable(erasedTargetType.getAnnotation(JsonTypeName.class))
+                    .map(JsonTypeName::value)
+                    .filter(name -> !name.isEmpty())
+                    .orElseGet(() -> getUnqualifiedClassName(erasedTargetType));
+            break;
+        case CLASS:
+            typeIdentifier = erasedTargetType.getName();
+            break;
+        default:
+            typeIdentifier = null;
+        }
+        return typeIdentifier;
+    }
+
+    /**
+     * In case of a missing {@link JsonTypeName} annotation for a {@code JsonTypeInfo.Id.NAME}, determine the unqualified name of the given class.
+     *
+     * @param erasedTargetType class to produce unqualified class name for
+     * @return simple class name, with declaring class's unqualified name as prefix for member classes
+     */
+    private static String getUnqualifiedClassName(Class<?> erasedTargetType) {
+        Class<?> declaringClass = erasedTargetType.getDeclaringClass();
+        if (declaringClass == null) {
+            return erasedTargetType.getSimpleName();
+        }
+        return getUnqualifiedClassName(declaringClass) + '$' + erasedTargetType.getSimpleName();
+    }
+
+    /**
+     * Create the custom schema definition for the given subtype, considering the {@link JsonTypeInfo#include()} setting.
+     *
+     * @param javaType targeted subtype
+     * @param typeInfoAnnotation annotation for looking up the type identifier and determining the kind of inclusion/serialization
+     * @param attributesToInclude optional: additional attributes to include on the actual/contained schema definition
+     * @param context generation context
+     * @return created custom definition (or {@code null} if no supported subtype resolution scenario could be detected
+     */
+    private ObjectNode createSubtypeDefinition(ResolvedType javaType, JsonTypeInfo typeInfoAnnotation, ObjectNode attributesToInclude,
+            SchemaGenerationContext context) {
+        final String typeIdentifier = this.getTypeIdentifier(javaType, typeInfoAnnotation);
+        if (typeIdentifier == null) {
+            return null;
+        }
+        final ObjectNode definition = context.getGeneratorConfig().createObjectNode();
+        switch (typeInfoAnnotation.include()) {
+        case WRAPPER_ARRAY:
+            definition.put(context.getKeyword(SchemaKeyword.TAG_TYPE), context.getKeyword(SchemaKeyword.TAG_TYPE_ARRAY));
+            ArrayNode itemsArray = definition.withArray(context.getKeyword(SchemaKeyword.TAG_ITEMS));
+            itemsArray.addObject()
+                    .put(context.getKeyword(SchemaKeyword.TAG_TYPE), context.getKeyword(SchemaKeyword.TAG_TYPE_STRING))
+                    .put(context.getKeyword(SchemaKeyword.TAG_CONST), typeIdentifier);
+            if (attributesToInclude == null || attributesToInclude.isEmpty()) {
+                itemsArray.add(context.createStandardDefinitionReference(javaType, this));
+            } else {
+                itemsArray.addObject()
+                        .withArray(context.getKeyword(SchemaKeyword.TAG_ALLOF))
+                        .add(context.createStandardDefinitionReference(javaType, this))
+                        .add(attributesToInclude);
+            }
+            break;
+        case WRAPPER_OBJECT:
+            definition.put(context.getKeyword(SchemaKeyword.TAG_TYPE), context.getKeyword(SchemaKeyword.TAG_TYPE_OBJECT));
+            ObjectNode propertiesNode = definition.with(context.getKeyword(SchemaKeyword.TAG_PROPERTIES));
+            if (attributesToInclude == null || attributesToInclude.isEmpty()) {
+                propertiesNode.set(typeIdentifier, context.createStandardDefinitionReference(javaType, this));
+            } else {
+                propertiesNode.with(typeIdentifier)
+                        .withArray(context.getKeyword(SchemaKeyword.TAG_ALLOF))
+                        .add(context.createStandardDefinitionReference(javaType, this))
+                        .add(attributesToInclude);
+            }
+            break;
+        case PROPERTY:
+        case EXISTING_PROPERTY:
+            final String propertyName = Optional.ofNullable(typeInfoAnnotation.property())
+                    .filter(name -> !name.isEmpty())
+                    .orElseGet(() -> typeInfoAnnotation.use().getDefaultPropertyName());
+            ObjectNode additionalPart = definition.withArray(context.getKeyword(SchemaKeyword.TAG_ALLOF))
+                    .add(context.createStandardDefinitionReference(javaType, this))
+                    .addObject();
+            if (attributesToInclude != null && !attributesToInclude.isEmpty()) {
+                additionalPart.setAll(attributesToInclude);
+            }
+            additionalPart.put(context.getKeyword(SchemaKeyword.TAG_TYPE), context.getKeyword(SchemaKeyword.TAG_TYPE_OBJECT))
+                    .with(context.getKeyword(SchemaKeyword.TAG_PROPERTIES))
+                    .with(propertyName)
+                    .put(context.getKeyword(SchemaKeyword.TAG_CONST), typeIdentifier);
+            break;
+        default:
+            return null;
+        }
+        return definition;
+    }
+}
diff --git a/jsonschema-module-javax-validation/README.md b/jsonschema-module-javax-validation/README.md
index 1cf6eca..1b8f32c 100644
--- a/jsonschema-module-javax-validation/README.md
+++ b/jsonschema-module-javax-validation/README.md
@@ -1,7 +1,7 @@
 # Java JSON Schema Generator – Module javax.validation
 [![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.github.victools/jsonschema-module-javax-validation/badge.svg)](https://maven-badges.herokuapp.com/maven-central/com.github.victools/jsonschema-module-javax-validation)
 
-Module for the `jsonschema-generator` – deriving JSON Schema attributes from `javax.validation.constraints` annotations.
+Module for the [jsonschema-generator](../jsonschema-generator) – deriving JSON Schema attributes from `javax.validation.constraints` annotations.
 
 ## Features
 1. Determine whether a member is not nullable, base assumption being that all fields and method return values are nullable if not annotated. Based on `@NotNull`/`@Null`/`@NotEmpty`/`@NotBlank`
@@ -26,6 +26,9 @@ Schema attributes derived from validation annotations on getter methods are also
 </dependency>
 ```
 
+Since version 4.7, the release versions of the main generator library and this module are aligned.
+It is recommended to use identical versions for both dependencies to ensure compatibility.
+
 ### Code
 #### Passing into SchemaGeneratorConfigBuilder.with(Module)
 ```java
diff --git a/jsonschema-module-javax-validation/src/main/java/com/github/victools/jsonschema/module/javax/validation/JavaxValidationModule.java b/jsonschema-module-javax-validation/src/main/java/com/github/victools/jsonschema/module/javax/validation/JavaxValidationModule.java
index d26d849..9252aa2 100644
--- a/jsonschema-module-javax-validation/src/main/java/com/github/victools/jsonschema/module/javax/validation/JavaxValidationModule.java
+++ b/jsonschema-module-javax-validation/src/main/java/com/github/victools/jsonschema/module/javax/validation/JavaxValidationModule.java
@@ -202,7 +202,7 @@ public class JavaxValidationModule implements Module {
      * @see Size
      */
     protected Integer resolveArrayMinItems(MemberScope<?, ?> member) {
-        if (member.isContainerType()) {
+        if (member.isContainerType() && !member.isFakeContainerItemScope()) {
             Size sizeAnnotation = this.getAnnotationFromFieldOrGetter(member, Size.class, Size::groups);
             if (sizeAnnotation != null && sizeAnnotation.min() > 0) {
                 // minimum length greater than the default 0 was specified
@@ -223,7 +223,7 @@ public class JavaxValidationModule implements Module {
      * @see Size
      */
     protected Integer resolveArrayMaxItems(MemberScope<?, ?> member) {
-        if (member.isContainerType()) {
+        if (member.isContainerType() && !member.isFakeContainerItemScope()) {
             Size sizeAnnotation = this.getAnnotationFromFieldOrGetter(member, Size.class, Size::groups);
             if (sizeAnnotation != null && sizeAnnotation.max() < 2147483647) {
                 // maximum length below the default 2147483647 was specified
@@ -243,7 +243,7 @@ public class JavaxValidationModule implements Module {
      * @see NotBlank
      */
     protected Integer resolveStringMinLength(MemberScope<?, ?> member) {
-        if (member.getType().isInstanceOf(CharSequence.class)) {
+        if (member.getType().isInstanceOf(CharSequence.class) && !member.isFakeContainerItemScope()) {
             Size sizeAnnotation = this.getAnnotationFromFieldOrGetter(member, Size.class, Size::groups);
             if (sizeAnnotation != null && sizeAnnotation.min() > 0) {
                 // minimum length greater than the default 0 was specified
@@ -265,7 +265,7 @@ public class JavaxValidationModule implements Module {
      * @see Size
      */
     protected Integer resolveStringMaxLength(MemberScope<?, ?> member) {
-        if (member.getType().isInstanceOf(CharSequence.class)) {
+        if (member.getType().isInstanceOf(CharSequence.class) && !member.isFakeContainerItemScope()) {
             Size sizeAnnotation = this.getAnnotationFromFieldOrGetter(member, Size.class, Size::groups);
             if (sizeAnnotation != null && sizeAnnotation.max() < 2147483647) {
                 // maximum length below the default 2147483647 was specified
diff --git a/jsonschema-module-swagger-1.5/README.md b/jsonschema-module-swagger-1.5/README.md
index daf303a..ccdc5aa 100644
--- a/jsonschema-module-swagger-1.5/README.md
+++ b/jsonschema-module-swagger-1.5/README.md
@@ -1,7 +1,7 @@
 # Java JSON Schema Generator – Module Swagger (1.5)
 [![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.github.victools/jsonschema-module-swagger-1.5/badge.svg)](https://maven-badges.herokuapp.com/maven-central/com.github.victools/jsonschema-module-swagger-1.5)
 
-Module for the `jsonschema-generator` – deriving JSON Schema attributes from `swagger` (1.5.x) annotations
+Module for the [jsonschema-generator](../jsonschema-generator) – deriving JSON Schema attributes from `swagger` (1.5.x) annotations
 
 ## Features
 1. Optionally override a field's property name with `@ApiModelProperty(name = ...)`
@@ -13,7 +13,7 @@ Module for the `jsonschema-generator` – deriving JSON Schema attributes from `
 7. Indicate a number's (field/method) "exclusiveMinimum" according to `@ApiModelProperty(allowableValues = "range(...")`
 8. Indicate a number's (field/method) "maximum" (inclusive) according to `@ApiModelProperty(allowableValues = "range...]")`
 9. Indicate a number's (field/method) "exclusiveMaximum" according to `@ApiModelProperty(allowableValues = "range...)")`
-10. Indicate a field/method's "const"/"enum" as `@ApiModelProperty(allowableValues = ...)` (if it is not a numeric range declaration)
+10. Indicate a field/method's "const"/"enum" as per `@ApiModelProperty(allowableValues = ...)` (if it is not a numeric range declaration)
 
 Schema attributes derived from `@ApiModelProperty` on fields are also applied to their respective getter methods.
 Schema attributes derived from `@ApiModelProperty` on getter methods are also applied to their associated fields.
@@ -28,6 +28,9 @@ Schema attributes derived from `@ApiModelProperty` on getter methods are also ap
 </dependency>
 ```
 
+Since version 4.7, the release versions of the main generator library and this module are aligned.
+It is recommended to use identical versions for both dependencies to ensure compatibility.
+
 ### Code
 #### Passing into SchemaGeneratorConfigBuilder.with(Module)
 ```java
diff --git a/jsonschema-module-swagger-1.5/src/main/java/com/github/victools/jsonschema/module/swagger15/SwaggerModule.java b/jsonschema-module-swagger-1.5/src/main/java/com/github/victools/jsonschema/module/swagger15/SwaggerModule.java
index 232a7de..260b179 100644
--- a/jsonschema-module-swagger-1.5/src/main/java/com/github/victools/jsonschema/module/swagger15/SwaggerModule.java
+++ b/jsonschema-module-swagger-1.5/src/main/java/com/github/victools/jsonschema/module/swagger15/SwaggerModule.java
@@ -123,6 +123,9 @@ public class SwaggerModule implements Module {
      * @return description (or {@code null})
      */
     protected String resolveDescription(MemberScope<?, ?> member) {
+        if (member.isFakeContainerItemScope()) {
+            return null;
+        }
         return Optional.ofNullable(member.getAnnotationConsideringFieldAndGetter(ApiModelProperty.class))
                 .map(ApiModelProperty::value)
                 .filter(value -> !value.isEmpty())
@@ -164,6 +167,9 @@ public class SwaggerModule implements Module {
      * @return {@link ApiModelProperty} annotation's non-empty {@code allowableValues} (or {@code null})
      */
     private Optional<String> findModelPropertyAllowableValues(MemberScope<?, ?> member) {
+        if (member.isFakeContainerItemScope()) {
+            return null;
+        }
         return Optional.ofNullable(member.getAnnotationConsideringFieldAndGetter(ApiModelProperty.class))
                 .map(ApiModelProperty::allowableValues)
                 .filter(allowableValues -> !allowableValues.isEmpty());
diff --git a/pom.xml b/pom.xml
index 2bd6dd6..a4c075f 100644
--- a/pom.xml
+++ b/pom.xml
@@ -74,6 +74,7 @@
         <version.mockito>2.27.0</version.mockito>
         <version.slf4j>1.7.30</version.slf4j>
         <version.swagger-1.5>1.5.22</version.swagger-1.5>
+        <version.validator>1.0.36</version.validator>
     </properties>
 
     <dependencyManagement>
@@ -113,34 +114,34 @@
                 <artifactId>swagger-annotations</artifactId>
                 <version>${version.swagger-1.5}</version>
             </dependency>
-            <!-- log4j implementation is optional and left up to the library users to determine whether it is desired -->
-            <dependency>
-                <groupId>org.apache.logging.log4j</groupId>
-                <artifactId>log4j-api</artifactId>
-                <version>${version.log4j}</version>
-                <optional>true</optional>
-            </dependency>
-            <dependency>
-                <groupId>org.apache.logging.log4j</groupId>
-                <artifactId>log4j-core</artifactId>
-                <version>${version.log4j}</version>
-                <optional>true</optional>
-            </dependency>
-            <dependency>
-                <groupId>org.apache.logging.log4j</groupId>
-                <artifactId>log4j-slf4j-impl</artifactId>
-                <version>${version.log4j}</version>
-                <optional>true</optional>
-            </dependency>
-            <!-- slf4j api is included to define trace/debug logging -->
-            <dependency>
-                <groupId>org.slf4j</groupId>
-                <artifactId>slf4j-api</artifactId>
-                <version>${version.slf4j}</version>
-            </dependency>
         </dependencies>
     </dependencyManagement>
     <dependencies>
+        <!-- slf4j api is included to define trace/debug logging -->
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-api</artifactId>
+            <version>${version.slf4j}</version>
+        </dependency>
+        <!-- log4j implementation is optional and left up to the library users to determine whether it is desired -->
+        <dependency>
+            <groupId>org.apache.logging.log4j</groupId>
+            <artifactId>log4j-api</artifactId>
+            <version>${version.log4j}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.logging.log4j</groupId>
+            <artifactId>log4j-core</artifactId>
+            <version>${version.log4j}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.logging.log4j</groupId>
+            <artifactId>log4j-slf4j-impl</artifactId>
+            <version>${version.log4j}</version>
+            <scope>test</scope>
+        </dependency>
         <dependency>
             <groupId>junit</groupId>
             <artifactId>junit</artifactId>
@@ -165,6 +166,22 @@
             <version>${version.jsonassert}</version>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>com.networknt</groupId>
+            <artifactId>json-schema-validator</artifactId>
+            <version>${version.validator}</version>
+            <scope>test</scope>
+            <exclusions>
+                <exclusion>
+                    <groupId>com.fasterxml.jackson.core</groupId>
+                    <artifactId>jackson-databind</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>org.slf4j</groupId>
+                    <artifactId>slf4j-api</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
     </dependencies>
 
 	<build>


Test patch:
diff --git a/jsonschema-generator/src/test/java/com/github/victools/jsonschema/generator/SchemaGeneratorComplexTypesTest.java b/jsonschema-generator/src/test/java/com/github/victools/jsonschema/generator/SchemaGeneratorComplexTypesTest.java
index 8c7ed15..ff29cb5 100644
--- a/jsonschema-generator/src/test/java/com/github/victools/jsonschema/generator/SchemaGeneratorComplexTypesTest.java
+++ b/jsonschema-generator/src/test/java/com/github/victools/jsonschema/generator/SchemaGeneratorComplexTypesTest.java
@@ -34,7 +34,6 @@ import java.util.List;
 import java.util.Map;
 import java.util.Optional;
 import java.util.Scanner;
-import java.util.concurrent.atomic.AtomicInteger;
 import junitparams.JUnitParamsRunner;
 import junitparams.Parameters;
 import junitparams.naming.TestCaseName;
@@ -112,12 +111,10 @@ public class SchemaGeneratorComplexTypesTest {
             }
             return null;
         });
-        AtomicInteger idCounter = new AtomicInteger();
-        AtomicInteger anchorCounter = new AtomicInteger();
         Module typeInGeneralModule = configBuilder -> populateTypeConfigPart(
                 configBuilder.with(Option.FORBIDDEN_ADDITIONAL_PROPERTIES_BY_DEFAULT).forTypesInGeneral()
-                        .withIdResolver(scope -> scope.getType().getTypeName().contains("$Test") ? "id-" + idCounter.addAndGet(1) : null)
-                        .withAnchorResolver(scope -> scope.isContainerType() ? null : "#" + anchorCounter.addAndGet(1)),
+                        .withIdResolver(scope -> scope.getType().getTypeName().contains("$Test") ? "id-" + scope.getSimpleTypeDescription() : null)
+                        .withAnchorResolver(scope -> scope.isContainerType() ? null : "#anchor"),
                 "for type in general: ");
         Module methodModule = configBuilder -> populateConfigPart(configBuilder.forMethods(), "looked-up from method: ");
         Module fieldModule = configBuilder -> populateConfigPart(configBuilder.forFields(), "looked-up from field: ");
diff --git a/jsonschema-generator/src/test/java/com/github/victools/jsonschema/generator/SchemaGeneratorCustomDefinitionsTest.java b/jsonschema-generator/src/test/java/com/github/victools/jsonschema/generator/SchemaGeneratorCustomDefinitionsTest.java
index 331a63e..19bceae 100644
--- a/jsonschema-generator/src/test/java/com/github/victools/jsonschema/generator/SchemaGeneratorCustomDefinitionsTest.java
+++ b/jsonschema-generator/src/test/java/com/github/victools/jsonschema/generator/SchemaGeneratorCustomDefinitionsTest.java
@@ -141,7 +141,7 @@ public class SchemaGeneratorCustomDefinitionsTest {
                         .put(context.getKeyword(SchemaKeyword.TAG_TITLE),
                                 "Custom Definition #2 for " + context.getTypeContext().getFullTypeDescription(javaType));
                 // using SchemaGenerationContext.createStandardDefinitionReference() to avoid endless loop with this custom definition
-                customDefinition.withArray(context.getKeyword(SchemaKeyword.TAG_ONEOF))
+                customDefinition.withArray(context.getKeyword(SchemaKeyword.TAG_ANYOF))
                         .add(context.createStandardDefinitionReference(javaType, this))
                         .addObject().put(context.getKeyword(SchemaKeyword.TAG_TYPE), context.getKeyword(SchemaKeyword.TAG_TYPE_NULL));
                 return new CustomDefinition(customDefinition);
diff --git a/jsonschema-generator/src/test/java/com/github/victools/jsonschema/generator/impl/SchemaGenerationContextImplTest.java b/jsonschema-generator/src/test/java/com/github/victools/jsonschema/generator/impl/SchemaGenerationContextImplTest.java
index c12b0de..9110694 100644
--- a/jsonschema-generator/src/test/java/com/github/victools/jsonschema/generator/impl/SchemaGenerationContextImplTest.java
+++ b/jsonschema-generator/src/test/java/com/github/victools/jsonschema/generator/impl/SchemaGenerationContextImplTest.java
@@ -202,7 +202,7 @@ public class SchemaGenerationContextImplTest extends AbstractTypeAwareTest {
                 .getCustomDefinition(Mockito.any(FieldScope.class), Mockito.any(), Mockito.any());
         FieldScope targetField = this.getTestClassField("booleanField");
         ObjectNode result = this.contextImpl.createStandardDefinitionReference(targetField, null);
-        Assert.assertEquals("{\"$comment\":\"custom property\",\"title\":\"Field Title\"}", result.toString());
+        Assert.assertEquals("{\"$comment\":\"custom property\",\"title\":\"Field Title\",\"description\":\"Type Description\"}", result.toString());
     }
 
     @Test
@@ -215,7 +215,7 @@ public class SchemaGenerationContextImplTest extends AbstractTypeAwareTest {
                 .getCustomDefinition(Mockito.any(FieldScope.class), Mockito.any(), Mockito.any());
         FieldScope targetField = this.getTestClassField("booleanField");
         ObjectNode result = this.contextImpl.createStandardDefinitionReference(targetField, null);
-        Assert.assertEquals("{\"$comment\":\"custom property\",\"title\":\"Field Title\"}", result.toString());
+        Assert.assertEquals("{\"$comment\":\"custom property\",\"title\":\"Field Title\",\"description\":\"Type Description\"}", result.toString());
     }
 
     @Test
@@ -243,7 +243,7 @@ public class SchemaGenerationContextImplTest extends AbstractTypeAwareTest {
                 .getCustomDefinition(Mockito.any(MethodScope.class), Mockito.any(), Mockito.any());
         MethodScope targetMethod = this.getTestClassMethod("isBooleanField");
         JsonNode result = this.contextImpl.createStandardDefinitionReference(targetMethod, null);
-        Assert.assertEquals("{\"$comment\":\"custom property\",\"title\":\"Method Title\"}", result.toString());
+        Assert.assertEquals("{\"$comment\":\"custom property\",\"title\":\"Method Title\",\"description\":\"Type Description\"}", result.toString());
     }
 
     @Test
@@ -256,7 +256,7 @@ public class SchemaGenerationContextImplTest extends AbstractTypeAwareTest {
                 .getCustomDefinition(Mockito.any(MethodScope.class), Mockito.any(), Mockito.any());
         MethodScope targetMethod = this.getTestClassMethod("isBooleanField");
         JsonNode result = this.contextImpl.createStandardDefinitionReference(targetMethod, null);
-        Assert.assertEquals("{\"$comment\":\"custom property\",\"title\":\"Method Title\"}", result.toString());
+        Assert.assertEquals("{\"$comment\":\"custom property\",\"title\":\"Method Title\",\"description\":\"Type Description\"}", result.toString());
     }
 
     @Test
diff --git a/jsonschema-generator/src/test/resources/com/github/victools/jsonschema/generator/multiple-definitions-one-type-DRAFT_2019_09.json b/jsonschema-generator/src/test/resources/com/github/victools/jsonschema/generator/multiple-definitions-one-type-DRAFT_2019_09.json
index 3cf55cc..97fbe92 100644
--- a/jsonschema-generator/src/test/resources/com/github/victools/jsonschema/generator/multiple-definitions-one-type-DRAFT_2019_09.json
+++ b/jsonschema-generator/src/test/resources/com/github/victools/jsonschema/generator/multiple-definitions-one-type-DRAFT_2019_09.json
@@ -25,7 +25,7 @@
         },
         "TestDirectCircularClass-2": {
             "title": "Custom Definition #2 for com.github.victools.jsonschema.generator.SchemaGeneratorCustomDefinitionsTest$TestDirectCircularClass",
-            "oneOf": [{
+            "anyOf": [{
                     "$ref": "#/$defs/TestDirectCircularClass-1"
                 }, {
                     "type": "null"
@@ -33,7 +33,7 @@
         },
         "int-1": {
             "title": "Custom Definition #2 for int",
-            "oneOf": [{
+            "anyOf": [{
                     "type": "integer"
                 }, {
                     "type": "null"
diff --git a/jsonschema-generator/src/test/resources/com/github/victools/jsonschema/generator/multiple-definitions-one-type-DRAFT_7.json b/jsonschema-generator/src/test/resources/com/github/victools/jsonschema/generator/multiple-definitions-one-type-DRAFT_7.json
index 1469472..9101af1 100644
--- a/jsonschema-generator/src/test/resources/com/github/victools/jsonschema/generator/multiple-definitions-one-type-DRAFT_7.json
+++ b/jsonschema-generator/src/test/resources/com/github/victools/jsonschema/generator/multiple-definitions-one-type-DRAFT_7.json
@@ -25,7 +25,7 @@
         },
         "TestDirectCircularClass-2": {
             "title": "Custom Definition #2 for com.github.victools.jsonschema.generator.SchemaGeneratorCustomDefinitionsTest$TestDirectCircularClass",
-            "oneOf": [{
+            "anyOf": [{
                     "$ref": "#/definitions/TestDirectCircularClass-1"
                 }, {
                     "type": "null"
@@ -33,7 +33,7 @@
         },
         "int-1": {
             "title": "Custom Definition #2 for int",
-            "oneOf": [{
+            "anyOf": [{
                     "type": "integer"
                 }, {
                     "type": "null"
diff --git a/jsonschema-generator/src/test/resources/com/github/victools/jsonschema/generator/testclass-withsupertypereferences-NO_SUBTYPES.json b/jsonschema-generator/src/test/resources/com/github/victools/jsonschema/generator/testclass-withsupertypereferences-NO_SUBTYPES.json
index 59fb0c1..f24356b 100644
--- a/jsonschema-generator/src/test/resources/com/github/victools/jsonschema/generator/testclass-withsupertypereferences-NO_SUBTYPES.json
+++ b/jsonschema-generator/src/test/resources/com/github/victools/jsonschema/generator/testclass-withsupertypereferences-NO_SUBTYPES.json
@@ -5,7 +5,8 @@
             "type": ["object", "null"],
             "properties": {
                 "genericFieldInSupertype": {
-                    "type": ["boolean", "null"]
+                    "type": ["boolean", "null"],
+                    "title": "Boolean"
                 }
             },
             "title": "TestSuperClass<Boolean>",
@@ -15,7 +16,8 @@
             "type": ["object", "null"],
             "properties": {
                 "genericFieldInSupertype": {
-                    "type": ["string", "null"]
+                    "type": ["string", "null"],
+                    "title": "String"
                 }
             },
             "title": "TestSuperClass<String>",
@@ -27,7 +29,9 @@
         "booleanSupertypeField": {
             "$ref": "#/definitions/TestSuperClass(Boolean)-nullable"
         },
-        "numberOrStringObjectField": {},
+        "numberOrStringObjectField": {
+            "title": "Object"
+        },
         "stringSupertypeField": {
             "$ref": "#/definitions/TestSuperClass(String)-nullable"
         }
diff --git a/jsonschema-generator/src/test/resources/com/github/victools/jsonschema/generator/testclass-withsupertypereferences-ONE_SUBTYPE_INVALID.json b/jsonschema-generator/src/test/resources/com/github/victools/jsonschema/generator/testclass-withsupertypereferences-ONE_SUBTYPE_INVALID.json
index daee406..64f40cf 100644
--- a/jsonschema-generator/src/test/resources/com/github/victools/jsonschema/generator/testclass-withsupertypereferences-ONE_SUBTYPE_INVALID.json
+++ b/jsonschema-generator/src/test/resources/com/github/victools/jsonschema/generator/testclass-withsupertypereferences-ONE_SUBTYPE_INVALID.json
@@ -1,14 +1,16 @@
 {
     "$schema": "http://json-schema.org/draft-07/schema#",
     "definitions": {
-        "TestSubClass3": {
-            "type": "object",
+        "TestSubClass3-nullable": {
+            "type": ["object", "null"],
             "properties": {
                 "fieldInSubtype": {
-                    "type": "integer"
+                    "type": "integer",
+                    "title": "int"
                 },
                 "genericFieldInSupertype": {
-                    "type": ["string", "null"]
+                    "type": ["string", "null"],
+                    "title": "String"
                 }
             },
             "title": "TestSubClass3"
@@ -17,18 +19,12 @@
             "type": ["object", "null"],
             "properties": {
                 "genericFieldInSupertype": {
-                    "type": ["boolean", "null"]
+                    "type": ["boolean", "null"],
+                    "title": "Boolean"
                 }
             },
             "title": "TestSuperClass<Boolean>",
             "description": "supertype-only description"
-        },
-        "TestSuperClass(String)-nullable": {
-            "oneOf": [{
-                    "type": "null"
-                }, {
-                    "$ref": "#/definitions/TestSubClass3"
-                }]
         }
     },
     "type": "object",
@@ -40,13 +36,15 @@
             "anyOf": [{
                     "type": "null"
                 }, {
-                    "type": "number"
+                    "type": "number",
+                    "title": "Number"
                 }, {
-                    "type": "string"
+                    "type": "string",
+                    "title": "String"
                 }]
         },
         "stringSupertypeField": {
-            "$ref": "#/definitions/TestSuperClass(String)-nullable"
+            "$ref": "#/definitions/TestSubClass3-nullable"
         }
     },
     "title": "TestClassWithSuperTypeReferences"
diff --git a/jsonschema-generator/src/test/resources/com/github/victools/jsonschema/generator/testclass-withsupertypereferences-ONE_SUBTYPE_VALID.json b/jsonschema-generator/src/test/resources/com/github/victools/jsonschema/generator/testclass-withsupertypereferences-ONE_SUBTYPE_VALID.json
index fb7d0b8..7daff30 100644
--- a/jsonschema-generator/src/test/resources/com/github/victools/jsonschema/generator/testclass-withsupertypereferences-ONE_SUBTYPE_VALID.json
+++ b/jsonschema-generator/src/test/resources/com/github/victools/jsonschema/generator/testclass-withsupertypereferences-ONE_SUBTYPE_VALID.json
@@ -1,8 +1,8 @@
 {
     "$schema": "http://json-schema.org/draft-07/schema#",
     "definitions": {
-        "TestSubClass1(Boolean)": {
-            "type": "object",
+        "TestSubClass1(Boolean)-nullable": {
+            "type": ["object", "null"],
             "properties": {
                 "dependentGenericFieldInSubtype": {
                     "type": ["array", "null"],
@@ -13,16 +13,18 @@
                     "title": "List<Boolean>"
                 },
                 "genericFieldInSupertype": {
-                    "type": ["boolean", "null"]
+                    "type": ["boolean", "null"],
+                    "title": "Boolean"
                 },
                 "sameGenericFieldInSubtype1": {
-                    "type": ["boolean", "null"]
+                    "type": ["boolean", "null"],
+                    "title": "Boolean"
                 }
             },
             "title": "TestSubClass1<Boolean>"
         },
-        "TestSubClass1(String)": {
-            "type": "object",
+        "TestSubClass1(String)-nullable": {
+            "type": ["object", "null"],
             "properties": {
                 "dependentGenericFieldInSubtype": {
                     "type": ["array", "null"],
@@ -33,45 +35,35 @@
                     "title": "List<String>"
                 },
                 "genericFieldInSupertype": {
-                    "type": ["string", "null"]
+                    "type": ["string", "null"],
+                    "title": "String"
                 },
                 "sameGenericFieldInSubtype1": {
-                    "type": ["string", "null"]
+                    "type": ["string", "null"],
+                    "title": "String"
                 }
             },
             "title": "TestSubClass1<String>"
-        },
-        "TestSuperClass(Boolean)-nullable": {
-            "oneOf": [{
-                    "type": "null"
-                }, {
-                    "$ref": "#/definitions/TestSubClass1(Boolean)"
-                }]
-        },
-        "TestSuperClass(String)-nullable": {
-            "oneOf": [{
-                    "type": "null"
-                }, {
-                    "$ref": "#/definitions/TestSubClass1(String)"
-                }]
         }
     },
     "type": "object",
     "properties": {
         "booleanSupertypeField": {
-            "$ref": "#/definitions/TestSuperClass(Boolean)-nullable"
+            "$ref": "#/definitions/TestSubClass1(Boolean)-nullable"
         },
         "numberOrStringObjectField": {
             "anyOf": [{
                     "type": "null"
                 }, {
-                    "type": "number"
+                    "type": "number",
+                    "title": "Number"
                 }, {
-                    "type": "string"
+                    "type": "string",
+                    "title": "String"
                 }]
         },
         "stringSupertypeField": {
-            "$ref": "#/definitions/TestSuperClass(String)-nullable"
+            "$ref": "#/definitions/TestSubClass1(String)-nullable"
         }
     },
     "title": "TestClassWithSuperTypeReferences"
diff --git a/jsonschema-generator/src/test/resources/com/github/victools/jsonschema/generator/testclass-withsupertypereferences-THREE_SUBTYPES.json b/jsonschema-generator/src/test/resources/com/github/victools/jsonschema/generator/testclass-withsupertypereferences-THREE_SUBTYPES.json
index 9508acc..a7d7219 100644
--- a/jsonschema-generator/src/test/resources/com/github/victools/jsonschema/generator/testclass-withsupertypereferences-THREE_SUBTYPES.json
+++ b/jsonschema-generator/src/test/resources/com/github/victools/jsonschema/generator/testclass-withsupertypereferences-THREE_SUBTYPES.json
@@ -13,10 +13,12 @@
                     "title": "List<Boolean>"
                 },
                 "genericFieldInSupertype": {
-                    "type": ["boolean", "null"]
+                    "type": ["boolean", "null"],
+                    "title": "Boolean"
                 },
                 "sameGenericFieldInSubtype1": {
-                    "type": ["boolean", "null"]
+                    "type": ["boolean", "null"],
+                    "title": "Boolean"
                 }
             },
             "title": "TestSubClass1<Boolean>"
@@ -33,10 +35,12 @@
                     "title": "List<String>"
                 },
                 "genericFieldInSupertype": {
-                    "type": ["string", "null"]
+                    "type": ["string", "null"],
+                    "title": "String"
                 },
                 "sameGenericFieldInSubtype1": {
-                    "type": ["string", "null"]
+                    "type": ["string", "null"],
+                    "title": "String"
                 }
             },
             "title": "TestSubClass1<String>"
@@ -45,10 +49,12 @@
             "type": "object",
             "properties": {
                 "genericFieldInSupertype": {
-                    "type": ["boolean", "null"]
+                    "type": ["boolean", "null"],
+                    "title": "Boolean"
                 },
                 "sameGenericFieldInSubtype2": {
-                    "type": ["boolean", "null"]
+                    "type": ["boolean", "null"],
+                    "title": "Boolean"
                 }
             },
             "title": "TestSubClass2<Boolean>"
@@ -57,10 +63,12 @@
             "type": "object",
             "properties": {
                 "genericFieldInSupertype": {
-                    "type": ["string", "null"]
+                    "type": ["string", "null"],
+                    "title": "String"
                 },
                 "sameGenericFieldInSubtype2": {
-                    "type": ["string", "null"]
+                    "type": ["string", "null"],
+                    "title": "String"
                 }
             },
             "title": "TestSubClass2<String>"
@@ -69,55 +77,49 @@
             "type": "object",
             "properties": {
                 "fieldInSubtype": {
-                    "type": "integer"
+                    "type": "integer",
+                    "title": "int"
                 },
                 "genericFieldInSupertype": {
-                    "type": ["string", "null"]
+                    "type": ["string", "null"],
+                    "title": "String"
                 }
             },
             "title": "TestSubClass3"
-        },
-        "TestSuperClass(Boolean)-nullable": {
-            "oneOf": [{
-                    "type": "null"
-                }, {
-                    "anyOf": [{
-                            "$ref": "#/definitions/TestSubClass1(Boolean)"
-                        }, {
-                            "$ref": "#/definitions/TestSubClass2(Boolean)"
-                        }]
-                }]
-        },
-        "TestSuperClass(String)-nullable": {
-            "oneOf": [{
-                    "type": "null"
-                }, {
-                    "anyOf": [{
-                            "$ref": "#/definitions/TestSubClass1(String)"
-                        }, {
-                            "$ref": "#/definitions/TestSubClass2(String)"
-                        }, {
-                            "$ref": "#/definitions/TestSubClass3"
-                        }]
-                }]
         }
     },
     "type": "object",
     "properties": {
         "booleanSupertypeField": {
-            "$ref": "#/definitions/TestSuperClass(Boolean)-nullable"
+            "anyOf": [{
+                    "type": "null"
+                }, {
+                    "$ref": "#/definitions/TestSubClass1(Boolean)"
+                }, {
+                    "$ref": "#/definitions/TestSubClass2(Boolean)"
+                }]
         },
         "numberOrStringObjectField": {
             "anyOf": [{
                     "type": "null"
                 }, {
-                    "type": "number"
+                    "type": "number",
+                    "title": "Number"
                 }, {
-                    "type": "string"
+                    "type": "string",
+                    "title": "String"
                 }]
         },
         "stringSupertypeField": {
-            "$ref": "#/definitions/TestSuperClass(String)-nullable"
+            "anyOf": [{
+                    "type": "null"
+                }, {
+                    "$ref": "#/definitions/TestSubClass1(String)"
+                }, {
+                    "$ref": "#/definitions/TestSubClass2(String)"
+                }, {
+                    "$ref": "#/definitions/TestSubClass3"
+                }]
         }
     },
     "title": "TestClassWithSuperTypeReferences"
diff --git a/jsonschema-generator/src/test/resources/com/github/victools/jsonschema/generator/testclass-withsupertypereferences-TWO_SUBTYPES.json b/jsonschema-generator/src/test/resources/com/github/victools/jsonschema/generator/testclass-withsupertypereferences-TWO_SUBTYPES.json
index ce5998f..4e43a5c 100644
--- a/jsonschema-generator/src/test/resources/com/github/victools/jsonschema/generator/testclass-withsupertypereferences-TWO_SUBTYPES.json
+++ b/jsonschema-generator/src/test/resources/com/github/victools/jsonschema/generator/testclass-withsupertypereferences-TWO_SUBTYPES.json
@@ -1,14 +1,16 @@
 {
     "$schema": "http://json-schema.org/draft-07/schema#",
     "definitions": {
-        "TestSubClass2(Boolean)": {
-            "type": "object",
+        "TestSubClass2(Boolean)-nullable": {
+            "type": ["object", "null"],
             "properties": {
                 "genericFieldInSupertype": {
-                    "type": ["boolean", "null"]
+                    "type": ["boolean", "null"],
+                    "title": "Boolean"
                 },
                 "sameGenericFieldInSubtype2": {
-                    "type": ["boolean", "null"]
+                    "type": ["boolean", "null"],
+                    "title": "Boolean"
                 }
             },
             "title": "TestSubClass2<Boolean>"
@@ -17,10 +19,12 @@
             "type": "object",
             "properties": {
                 "genericFieldInSupertype": {
-                    "type": ["string", "null"]
+                    "type": ["string", "null"],
+                    "title": "String"
                 },
                 "sameGenericFieldInSubtype2": {
-                    "type": ["string", "null"]
+                    "type": ["string", "null"],
+                    "title": "String"
                 }
             },
             "title": "TestSubClass2<String>"
@@ -29,49 +33,41 @@
             "type": "object",
             "properties": {
                 "fieldInSubtype": {
-                    "type": "integer"
+                    "type": "integer",
+                    "title": "int"
                 },
                 "genericFieldInSupertype": {
-                    "type": ["string", "null"]
+                    "type": ["string", "null"],
+                    "title": "String"
                 }
             },
             "title": "TestSubClass3"
-        },
-        "TestSuperClass(Boolean)-nullable": {
-            "oneOf": [{
-                    "type": "null"
-                }, {
-                    "$ref": "#/definitions/TestSubClass2(Boolean)"
-                }]
-        },
-        "TestSuperClass(String)-nullable": {
-            "oneOf": [{
-                    "type": "null"
-                }, {
-                    "anyOf": [{
-                            "$ref": "#/definitions/TestSubClass2(String)"
-                        }, {
-                            "$ref": "#/definitions/TestSubClass3"
-                        }]
-                }]
         }
     },
     "type": "object",
     "properties": {
         "booleanSupertypeField": {
-            "$ref": "#/definitions/TestSuperClass(Boolean)-nullable"
+            "$ref": "#/definitions/TestSubClass2(Boolean)-nullable"
         },
         "numberOrStringObjectField": {
             "anyOf": [{
                     "type": "null"
                 }, {
-                    "type": "number"
+                    "type": "number",
+                    "title": "Number"
                 }, {
-                    "type": "string"
+                    "type": "string",
+                    "title": "String"
                 }]
         },
         "stringSupertypeField": {
-            "$ref": "#/definitions/TestSuperClass(String)-nullable"
+            "anyOf": [{
+                    "type": "null"
+                }, {
+                    "$ref": "#/definitions/TestSubClass2(String)"
+                }, {
+                    "$ref": "#/definitions/TestSubClass3"
+                }]
         }
     },
     "title": "TestClassWithSuperTypeReferences"
diff --git a/jsonschema-generator/src/test/resources/com/github/victools/jsonschema/generator/testclass1-FULL_DOCUMENTATION-typeattributes.json b/jsonschema-generator/src/test/resources/com/github/victools/jsonschema/generator/testclass1-FULL_DOCUMENTATION-typeattributes.json
index 942f27a..88ebbc2 100644
--- a/jsonschema-generator/src/test/resources/com/github/victools/jsonschema/generator/testclass1-FULL_DOCUMENTATION-typeattributes.json
+++ b/jsonschema-generator/src/test/resources/com/github/victools/jsonschema/generator/testclass1-FULL_DOCUMENTATION-typeattributes.json
@@ -3,13 +3,23 @@
     "properties": {
         "CONSTANT": {
             "type": "integer",
-            "const": 5
+            "const": 5,
+            "$anchor": "#anchor",
+            "title": "Long",
+            "description": "for type in general: Long",
+            "default": 1,
+            "enum": [1, 2, 3, 4, 5],
+            "minimum": 1,
+            "exclusiveMinimum": 0,
+            "maximum": 1E+1,
+            "exclusiveMaximum": 11,
+            "multipleOf": 1
         },
         "genericArray": {
             "type": ["array", "null"],
             "items": {
                 "type": "string",
-                "$anchor": "#1",
+                "$anchor": "#anchor",
                 "title": "String",
                 "description": "for type in general: String",
                 "const": "constant string value",
@@ -25,34 +35,69 @@
             "uniqueItems": false
         },
         "genericValue": {
-            "type": ["string", "null"]
+            "type": ["string", "null"],
+            "$anchor": "#anchor",
+            "title": "String",
+            "description": "for type in general: String",
+            "const": "constant string value",
+            "minLength": 1,
+            "maxLength": 256,
+            "format": "date",
+            "pattern": "^.{1,256}$"
         },
         "ignoredInternalValue": {
-            "type": ["integer", "null"]
+            "type": ["integer", "null"],
+            "$anchor": "#anchor",
+            "title": "Integer",
+            "description": "for type in general: Integer",
+            "default": 1,
+            "enum": [1, 2, 3, 4, 5],
+            "minimum": 1,
+            "exclusiveMinimum": 0,
+            "maximum": 1E+1,
+            "exclusiveMaximum": 11,
+            "multipleOf": 1
         },
         "primitiveValue": {
-            "type": "integer"
+            "type": "integer",
+            "$anchor": "#anchor",
+            "title": "int",
+            "description": "for type in general: int"
         },
         "calculateSomething(Number, Number)": false,
         "getGenericValue()": {
-            "type": ["string", "null"]
+            "type": ["string", "null"],
+            "$anchor": "#anchor",
+            "title": "String",
+            "description": "for type in general: String",
+            "const": "constant string value",
+            "minLength": 1,
+            "maxLength": 256,
+            "format": "date",
+            "pattern": "^.{1,256}$"
         },
         "getPrimitiveValue()": {
-            "type": "integer"
+            "type": "integer",
+            "$anchor": "#anchor",
+            "title": "int",
+            "description": "for type in general: int"
         },
         "isSimpleTestClass()": {
-            "type": "boolean"
+            "type": "boolean",
+            "$anchor": "#anchor",
+            "title": "boolean",
+            "description": "for type in general: boolean"
         }
     },
-    "$id": "id-1",
-    "$anchor": "#2",
+    "$id": "id-TestClass1",
+    "$anchor": "#anchor",
     "title": "TestClass1",
     "description": "for type in general: TestClass1",
     "additionalProperties": false,
     "patternProperties": {
         "^generic.+$": {
             "type": "string",
-            "$anchor": "#3",
+            "$anchor": "#anchor",
             "title": "String",
             "description": "for type in general: String",
             "const": "constant string value",
diff --git a/jsonschema-generator/src/test/resources/com/github/victools/jsonschema/generator/testclass1-PLAIN_JSON-fieldattributes.json b/jsonschema-generator/src/test/resources/com/github/victools/jsonschema/generator/testclass1-PLAIN_JSON-fieldattributes.json
index 3357c4f..8386040 100644
--- a/jsonschema-generator/src/test/resources/com/github/victools/jsonschema/generator/testclass1-PLAIN_JSON-fieldattributes.json
+++ b/jsonschema-generator/src/test/resources/com/github/victools/jsonschema/generator/testclass1-PLAIN_JSON-fieldattributes.json
@@ -10,7 +10,14 @@
             "uniqueItems": false,
             "type": ["array", "null"],
             "items": {
-                "type": "string"
+                "type": "string",
+                "title": "String",
+                "description": "looked-up from field: String",
+                "const": "constant string value",
+                "minLength": 1,
+                "maxLength": 256,
+                "format": "date",
+                "pattern": "^.{1,256}$"
             }
         },
         "genericValue": {
diff --git a/jsonschema-generator/src/test/resources/com/github/victools/jsonschema/generator/testclass3-FULL_DOCUMENTATION-typeattributes.json b/jsonschema-generator/src/test/resources/com/github/victools/jsonschema/generator/testclass3-FULL_DOCUMENTATION-typeattributes.json
index c0702d6..b0ec99d 100644
--- a/jsonschema-generator/src/test/resources/com/github/victools/jsonschema/generator/testclass3-FULL_DOCUMENTATION-typeattributes.json
+++ b/jsonschema-generator/src/test/resources/com/github/victools/jsonschema/generator/testclass3-FULL_DOCUMENTATION-typeattributes.json
@@ -4,16 +4,39 @@
             "type": ["object", "null"],
             "properties": {
                 "get()": {
-                    "type": ["integer", "null"]
+                    "type": ["integer", "null"],
+                    "$anchor": "#anchor",
+                    "title": "Integer",
+                    "description": "for type in general: Integer",
+                    "default": 1,
+                    "enum": [1, 2, 3, 4, 5],
+                    "minimum": 1,
+                    "exclusiveMinimum": 0,
+                    "maximum": 1E+1,
+                    "exclusiveMaximum": 11,
+                    "multipleOf": 1
                 },
                 "isPresent()": {
-                    "type": "boolean"
+                    "type": "boolean",
+                    "$anchor": "#anchor",
+                    "title": "boolean",
+                    "description": "for type in general: boolean"
                 },
                 "orElse(Integer)": {
-                    "type": ["integer", "null"]
+                    "type": ["integer", "null"],
+                    "$anchor": "#anchor",
+                    "title": "Integer",
+                    "description": "for type in general: Integer",
+                    "default": 1,
+                    "enum": [1, 2, 3, 4, 5],
+                    "minimum": 1,
+                    "exclusiveMinimum": 0,
+                    "maximum": 1E+1,
+                    "exclusiveMaximum": 11,
+                    "multipleOf": 1
                 }
             },
-            "$anchor": "#15",
+            "$anchor": "#anchor",
             "title": "Optional<Integer>",
             "description": "for type in general: Optional<Integer>",
             "additionalProperties": false
@@ -22,20 +45,45 @@
             "type": "object",
             "properties": {
                 "name": {
-                    "type": ["string", "null"]
+                    "type": ["string", "null"],
+                    "$anchor": "#anchor",
+                    "title": "String",
+                    "description": "for type in general: String",
+                    "const": "constant string value",
+                    "minLength": 1,
+                    "maxLength": 256,
+                    "format": "date",
+                    "pattern": "^.{1,256}$"
                 },
                 "oldMode": {
-                    "type": "integer"
+                    "type": "integer",
+                    "$anchor": "#anchor",
+                    "title": "int",
+                    "description": "for type in general: int"
                 },
                 "ordinal": {
-                    "type": "integer"
+                    "type": "integer",
+                    "$anchor": "#anchor",
+                    "title": "int",
+                    "description": "for type in general: int"
                 },
                 "compareTo(RoundingMode)": {
-                    "type": "integer"
+                    "type": "integer",
+                    "$anchor": "#anchor",
+                    "title": "int",
+                    "description": "for type in general: int"
                 },
                 "name()": {
                     "type": "string",
-                    "enum": ["UP", "DOWN", "CEILING", "FLOOR", "HALF_UP", "HALF_DOWN", "HALF_EVEN", "UNNECESSARY"]
+                    "enum": ["UP", "DOWN", "CEILING", "FLOOR", "HALF_UP", "HALF_DOWN", "HALF_EVEN", "UNNECESSARY"],
+                    "$anchor": "#anchor",
+                    "title": "String",
+                    "description": "for type in general: String",
+                    "const": "constant string value",
+                    "minLength": 1,
+                    "maxLength": 256,
+                    "format": "date",
+                    "pattern": "^.{1,256}$"
                 },
                 "valueOf(String)": {
                     "$ref": "#/definitions/RoundingMode-nullable"
@@ -55,13 +103,13 @@
                     "uniqueItems": false
                 }
             },
-            "$anchor": "#16",
+            "$anchor": "#anchor",
             "title": "RoundingMode",
             "description": "for type in general: RoundingMode",
             "additionalProperties": false
         },
         "RoundingMode-nullable": {
-            "oneOf": [{
+            "anyOf": [{
                     "type": "null"
                 }, {
                     "$ref": "#/definitions/RoundingMode"
@@ -72,13 +120,23 @@
             "properties": {
                 "CONSTANT": {
                     "type": "integer",
-                    "const": 5
+                    "const": 5,
+                    "$anchor": "#anchor",
+                    "title": "Long",
+                    "description": "for type in general: Long",
+                    "default": 1,
+                    "enum": [1, 2, 3, 4, 5],
+                    "minimum": 1,
+                    "exclusiveMinimum": 0,
+                    "maximum": 1E+1,
+                    "exclusiveMaximum": 11,
+                    "multipleOf": 1
                 },
                 "genericArray": {
                     "type": ["array", "null"],
                     "items": {
                         "type": "string",
-                        "$anchor": "#7",
+                        "$anchor": "#anchor",
                         "title": "String",
                         "description": "for type in general: String",
                         "const": "constant string value",
@@ -94,34 +152,69 @@
                     "uniqueItems": false
                 },
                 "genericValue": {
-                    "type": ["string", "null"]
+                    "type": ["string", "null"],
+                    "$anchor": "#anchor",
+                    "title": "String",
+                    "description": "for type in general: String",
+                    "const": "constant string value",
+                    "minLength": 1,
+                    "maxLength": 256,
+                    "format": "date",
+                    "pattern": "^.{1,256}$"
                 },
                 "ignoredInternalValue": {
-                    "type": ["integer", "null"]
+                    "type": ["integer", "null"],
+                    "$anchor": "#anchor",
+                    "title": "Integer",
+                    "description": "for type in general: Integer",
+                    "default": 1,
+                    "enum": [1, 2, 3, 4, 5],
+                    "minimum": 1,
+                    "exclusiveMinimum": 0,
+                    "maximum": 1E+1,
+                    "exclusiveMaximum": 11,
+                    "multipleOf": 1
                 },
                 "primitiveValue": {
-                    "type": "integer"
+                    "type": "integer",
+                    "$anchor": "#anchor",
+                    "title": "int",
+                    "description": "for type in general: int"
                 },
                 "calculateSomething(Number, Number)": false,
                 "getGenericValue()": {
-                    "type": ["string", "null"]
+                    "type": ["string", "null"],
+                    "$anchor": "#anchor",
+                    "title": "String",
+                    "description": "for type in general: String",
+                    "const": "constant string value",
+                    "minLength": 1,
+                    "maxLength": 256,
+                    "format": "date",
+                    "pattern": "^.{1,256}$"
                 },
                 "getPrimitiveValue()": {
-                    "type": "integer"
+                    "type": "integer",
+                    "$anchor": "#anchor",
+                    "title": "int",
+                    "description": "for type in general: int"
                 },
                 "isSimpleTestClass()": {
-                    "type": "boolean"
+                    "type": "boolean",
+                    "$anchor": "#anchor",
+                    "title": "boolean",
+                    "description": "for type in general: boolean"
                 }
             },
-            "$id": "id-3",
-            "$anchor": "#8",
+            "$id": "id-TestClass1",
+            "$anchor": "#anchor",
             "title": "TestClass1",
             "description": "for type in general: TestClass1",
             "additionalProperties": false,
             "patternProperties": {
                 "^generic.+$": {
                     "type": "string",
-                    "$anchor": "#9",
+                    "$anchor": "#anchor",
                     "title": "String",
                     "description": "for type in general: String",
                     "const": "constant string value",
@@ -139,7 +232,7 @@
                     "type": ["array", "null"],
                     "items": {
                         "type": "integer",
-                        "$anchor": "#4",
+                        "$anchor": "#anchor",
                         "title": "Long",
                         "description": "for type in general: Long",
                         "default": 1,
@@ -157,21 +250,41 @@
                     "uniqueItems": false
                 },
                 "genericValue": {
-                    "type": ["integer", "null"]
+                    "type": ["integer", "null"],
+                    "$anchor": "#anchor",
+                    "title": "Long",
+                    "description": "for type in general: Long",
+                    "default": 1,
+                    "enum": [1, 2, 3, 4, 5],
+                    "minimum": 1,
+                    "exclusiveMinimum": 0,
+                    "maximum": 1E+1,
+                    "exclusiveMaximum": 11,
+                    "multipleOf": 1
                 },
                 "getGenericValue()": {
-                    "type": ["integer", "null"]
+                    "type": ["integer", "null"],
+                    "$anchor": "#anchor",
+                    "title": "Long",
+                    "description": "for type in general: Long",
+                    "default": 1,
+                    "enum": [1, 2, 3, 4, 5],
+                    "minimum": 1,
+                    "exclusiveMinimum": 0,
+                    "maximum": 1E+1,
+                    "exclusiveMaximum": 11,
+                    "multipleOf": 1
                 }
             },
-            "$id": "id-2",
-            "$anchor": "#5",
+            "$id": "id-TestClass2<Long>",
+            "$anchor": "#anchor",
             "title": "TestClass2<Long>",
             "description": "for type in general: TestClass2<Long>",
             "additionalProperties": false,
             "patternProperties": {
                 "^generic.+$": {
                     "type": "integer",
-                    "$anchor": "#6",
+                    "$anchor": "#anchor",
                     "title": "Long",
                     "description": "for type in general: Long",
                     "default": 1,
@@ -185,7 +298,7 @@
             }
         },
         "TestClass2(Long)-nullable": {
-            "oneOf": [{
+            "anyOf": [{
                     "type": "null"
                 }, {
                     "$ref": "#/definitions/TestClass2(Long)"
@@ -198,7 +311,7 @@
                     "type": ["array", "null"],
                     "items": {
                         "type": "string",
-                        "$anchor": "#11",
+                        "$anchor": "#anchor",
                         "title": "String",
                         "description": "for type in general: String",
                         "const": "constant string value",
@@ -214,21 +327,37 @@
                     "uniqueItems": false
                 },
                 "genericValue": {
-                    "type": ["string", "null"]
+                    "type": ["string", "null"],
+                    "$anchor": "#anchor",
+                    "title": "String",
+                    "description": "for type in general: String",
+                    "const": "constant string value",
+                    "minLength": 1,
+                    "maxLength": 256,
+                    "format": "date",
+                    "pattern": "^.{1,256}$"
                 },
                 "getGenericValue()": {
-                    "type": ["string", "null"]
+                    "type": ["string", "null"],
+                    "$anchor": "#anchor",
+                    "title": "String",
+                    "description": "for type in general: String",
+                    "const": "constant string value",
+                    "minLength": 1,
+                    "maxLength": 256,
+                    "format": "date",
+                    "pattern": "^.{1,256}$"
                 }
             },
-            "$id": "id-11",
-            "$anchor": "#12",
+            "$id": "id-TestClass2<String>",
+            "$anchor": "#anchor",
             "title": "TestClass2<String>",
             "description": "for type in general: TestClass2<String>",
             "additionalProperties": false,
             "patternProperties": {
                 "^generic.+$": {
                     "type": "string",
-                    "$anchor": "#13",
+                    "$anchor": "#anchor",
                     "title": "String",
                     "description": "for type in general: String",
                     "const": "constant string value",
@@ -240,7 +369,7 @@
             }
         },
         "TestClass2(String)-nullable": {
-            "oneOf": [{
+            "anyOf": [{
                     "type": "null"
                 }, {
                     "$ref": "#/definitions/TestClass2(String)"
@@ -256,14 +385,14 @@
                         "items": {
                             "$ref": "#/definitions/TestClass1"
                         },
-                        "$id": "id-5",
+                        "$id": "id-TestClass1[]",
                         "title": "TestClass1[]",
                         "description": "for type in general: TestClass1[]",
                         "minItems": 2,
                         "maxItems": 100,
                         "uniqueItems": false
                     },
-                    "$id": "id-6",
+                    "$id": "id-TestClass1[][]<TestClass1[]>",
                     "title": "TestClass1[][]<TestClass1[]>",
                     "description": "for type in general: TestClass1[][]<TestClass1[]>",
                     "minItems": 2,
@@ -275,7 +404,7 @@
                     "items": {
                         "$ref": "#/definitions/TestClass1"
                     },
-                    "$id": "id-4",
+                    "$id": "id-TestClass1[]",
                     "title": "TestClass1[]",
                     "description": "for type in general: TestClass1[]",
                     "minItems": 2,
@@ -287,7 +416,7 @@
                     "items": {
                         "$ref": "#/definitions/TestClass1"
                     },
-                    "$id": "id-7",
+                    "$id": "id-TestClass1[]",
                     "title": "TestClass1[]",
                     "description": "for type in general: TestClass1[]",
                     "minItems": 2,
@@ -295,8 +424,8 @@
                     "uniqueItems": false
                 }
             },
-            "$id": "id-8",
-            "$anchor": "#10",
+            "$id": "id-TestClass2<TestClass1[]>",
+            "$anchor": "#anchor",
             "title": "TestClass2<TestClass1[]>",
             "description": "for type in general: TestClass2<TestClass1[]>",
             "additionalProperties": false,
@@ -306,7 +435,7 @@
                     "items": {
                         "$ref": "#/definitions/TestClass1"
                     },
-                    "$id": "id-9",
+                    "$id": "id-TestClass1[]",
                     "title": "TestClass1[]",
                     "description": "for type in general: TestClass1[]",
                     "minItems": 2,
@@ -323,7 +452,7 @@
                     "items": {
                         "$ref": "#/definitions/TestClass2(String)"
                     },
-                    "$id": "id-12",
+                    "$id": "id-TestClass2[]<TestClass2<String>>",
                     "title": "TestClass2[]<TestClass2<String>>",
                     "description": "for type in general: TestClass2[]<TestClass2<String>>",
                     "minItems": 2,
@@ -337,8 +466,8 @@
                     "$ref": "#/definitions/TestClass2(String)-nullable"
                 }
             },
-            "$id": "id-13",
-            "$anchor": "#14",
+            "$id": "id-TestClass2<TestClass2<String>>",
+            "$anchor": "#anchor",
             "title": "TestClass2<TestClass2<String>>",
             "description": "for type in general: TestClass2<TestClass2<String>>",
             "additionalProperties": false,
@@ -368,13 +497,13 @@
                     "$ref": "#/definitions/TestClass2(TestClass2(String))-nullable"
                 }
             },
-            "$id": "id-14",
-            "$anchor": "#17",
+            "$id": "id-TestClass4<Integer, String>",
+            "$anchor": "#anchor",
             "title": "TestClass4<Integer, String>",
             "description": "for type in general: TestClass4<Integer, String>",
             "additionalProperties": {
                 "type": "string",
-                "$anchor": "#18",
+                "$anchor": "#anchor",
                 "title": "String",
                 "description": "for type in general: String",
                 "const": "constant string value",
@@ -401,7 +530,7 @@
             "items": {
                 "$ref": "#/definitions/TestClass2(Long)"
             },
-            "$id": "id-10",
+            "$id": "id-List<TestClass2<Long>>",
             "title": "List<TestClass2<Long>>",
             "description": "for type in general: List<TestClass2<Long>>",
             "minItems": 2,
@@ -422,7 +551,7 @@
             "items": {
                 "$ref": "#/definitions/TestClass2(Long)"
             },
-            "$id": "id-15",
+            "$id": "id-List<TestClass2<Long>>",
             "title": "List<TestClass2<Long>>",
             "description": "for type in general: List<TestClass2<Long>>",
             "minItems": 2,
@@ -430,8 +559,8 @@
             "uniqueItems": false
         }
     },
-    "$id": "id-16",
-    "$anchor": "#19",
+    "$id": "id-TestClass3",
+    "$anchor": "#anchor",
     "title": "TestClass3",
     "description": "for type in general: TestClass3",
     "additionalProperties": false
diff --git a/jsonschema-generator/src/test/resources/com/github/victools/jsonschema/generator/testclass3-FULL_DOCUMENTATION.json b/jsonschema-generator/src/test/resources/com/github/victools/jsonschema/generator/testclass3-FULL_DOCUMENTATION.json
index c023387..99a3dc1 100644
--- a/jsonschema-generator/src/test/resources/com/github/victools/jsonschema/generator/testclass3-FULL_DOCUMENTATION.json
+++ b/jsonschema-generator/src/test/resources/com/github/victools/jsonschema/generator/testclass3-FULL_DOCUMENTATION.json
@@ -51,7 +51,7 @@
             }
         },
         "RoundingMode-nullable": {
-            "oneOf": [{
+            "anyOf": [{
                     "type": "null"
                 }, {
                     "$ref": "#/definitions/RoundingMode"
@@ -109,7 +109,7 @@
             }
         },
         "TestClass2(Long)-nullable": {
-            "oneOf": [{
+            "anyOf": [{
                     "type": "null"
                 }, {
                     "$ref": "#/definitions/TestClass2(Long)"
@@ -133,7 +133,7 @@
             }
         },
         "TestClass2(String)-nullable": {
-            "oneOf": [{
+            "anyOf": [{
                     "type": "null"
                 }, {
                     "$ref": "#/definitions/TestClass2(String)"
diff --git a/jsonschema-generator/src/test/resources/com/github/victools/jsonschema/generator/testclass3-JAVA_OBJECT-methodattributes.json b/jsonschema-generator/src/test/resources/com/github/victools/jsonschema/generator/testclass3-JAVA_OBJECT-methodattributes.json
index be49c6f..e01e728 100644
--- a/jsonschema-generator/src/test/resources/com/github/victools/jsonschema/generator/testclass3-JAVA_OBJECT-methodattributes.json
+++ b/jsonschema-generator/src/test/resources/com/github/victools/jsonschema/generator/testclass3-JAVA_OBJECT-methodattributes.json
@@ -42,13 +42,18 @@
                     "uniqueItems": false,
                     "type": ["array", "null"],
                     "items": {
-                        "$ref": "#/definitions/RoundingMode"
+                        "allOf": [{
+                                "$ref": "#/definitions/RoundingMode"
+                            }, {
+                                "title": "RoundingMode",
+                                "description": "looked-up from method: RoundingMode"
+                            }]
                     }
                 }
             }
         },
         "RoundingMode-nullable": {
-            "oneOf": [{
+            "anyOf": [{
                     "type": "null"
                 }, {
                     "$ref": "#/definitions/RoundingMode"
@@ -191,7 +196,7 @@
                             }
                         },
                         "getGenericValue()": {
-                            "oneOf": [{
+                            "anyOf": [{
                                     "type": "null"
                                 }, {
                                     "$ref": "#/definitions/TestClass2(String)"
@@ -242,7 +247,18 @@
                     "uniqueItems": false,
                     "type": ["array", "null"],
                     "items": {
-                        "$ref": "#/definitions/TestClass1"
+                        "allOf": [{
+                                "$ref": "#/definitions/TestClass1"
+                            }, {
+                                "title": "TestClass1",
+                                "description": "looked-up from method: TestClass1",
+                                "additionalProperties": false,
+                                "patternProperties": {
+                                    "^generic.+$": {
+                                        "type": "string"
+                                    }
+                                }
+                            }]
                     }
                 }
             },
@@ -259,7 +275,7 @@
             }
         },
         "getNestedLong()": {
-            "oneOf": [{
+            "anyOf": [{
                     "type": "null"
                 }, {
                     "$ref": "#/definitions/TestClass2(Long)"
@@ -281,7 +297,18 @@
             "uniqueItems": false,
             "type": ["array", "null"],
             "items": {
-                "$ref": "#/definitions/TestClass2(Long)"
+                "allOf": [{
+                        "$ref": "#/definitions/TestClass2(Long)"
+                    }, {
+                        "title": "TestClass2<Long>",
+                        "description": "looked-up from method: TestClass2<Long>",
+                        "additionalProperties": false,
+                        "patternProperties": {
+                            "^generic.+$": {
+                                "type": "integer"
+                            }
+                        }
+                    }]
             }
         }
     }
diff --git a/jsonschema-generator/src/test/resources/com/github/victools/jsonschema/generator/testclass3-PLAIN_JSON-fieldattributes.json b/jsonschema-generator/src/test/resources/com/github/victools/jsonschema/generator/testclass3-PLAIN_JSON-fieldattributes.json
index 6128d18..bc04e0b 100644
--- a/jsonschema-generator/src/test/resources/com/github/victools/jsonschema/generator/testclass3-PLAIN_JSON-fieldattributes.json
+++ b/jsonschema-generator/src/test/resources/com/github/victools/jsonschema/generator/testclass3-PLAIN_JSON-fieldattributes.json
@@ -12,7 +12,14 @@
                     "uniqueItems": false,
                     "type": ["array", "null"],
                     "items": {
-                        "type": "string"
+                        "type": "string",
+                        "title": "String",
+                        "description": "looked-up from field: String",
+                        "const": "constant string value",
+                        "minLength": 1,
+                        "maxLength": 256,
+                        "format": "date",
+                        "pattern": "^.{1,256}$"
                     }
                 },
                 "genericValue": {
@@ -55,7 +62,16 @@
                     "uniqueItems": false,
                     "type": ["array", "null"],
                     "items": {
-                        "type": "integer"
+                        "type": "integer",
+                        "title": "Long",
+                        "description": "looked-up from field: Long",
+                        "default": 1,
+                        "enum": [1, 2, 3, 4, 5],
+                        "minimum": 1,
+                        "exclusiveMinimum": 0,
+                        "maximum": 1E+1,
+                        "exclusiveMaximum": 11,
+                        "multipleOf": 1
                     }
                 },
                 "genericValue": {
@@ -83,7 +99,14 @@
                     "uniqueItems": false,
                     "type": ["array", "null"],
                     "items": {
-                        "type": "string"
+                        "type": "string",
+                        "title": "String",
+                        "description": "looked-up from field: String",
+                        "const": "constant string value",
+                        "minLength": 1,
+                        "maxLength": 256,
+                        "format": "date",
+                        "pattern": "^.{1,256}$"
                     }
                 },
                 "genericValue": {
@@ -115,11 +138,22 @@
                             "uniqueItems": false,
                             "type": ["array", "null"],
                             "items": {
-                                "$ref": "#/definitions/TestClass2(String)"
+                                "allOf": [{
+                                        "$ref": "#/definitions/TestClass2(String)"
+                                    }, {
+                                        "title": "TestClass2<String>",
+                                        "description": "looked-up from field: TestClass2<String>",
+                                        "additionalProperties": false,
+                                        "patternProperties": {
+                                            "^generic.+$": {
+                                                "type": "string"
+                                            }
+                                        }
+                                    }]
                             }
                         },
                         "genericValue": {
-                            "oneOf": [{
+                            "anyOf": [{
                                     "type": "null"
                                 }, {
                                     "$ref": "#/definitions/TestClass2(String)"
@@ -173,6 +207,11 @@
                     "uniqueItems": false,
                     "type": ["array", "null"],
                     "items": {
+                        "title": "TestClass1[]",
+                        "description": "looked-up from field: TestClass1[]",
+                        "minItems": 2,
+                        "maxItems": 100,
+                        "uniqueItems": false,
                         "type": "array",
                         "items": {
                             "$ref": "#/definitions/TestClass1"
@@ -187,7 +226,18 @@
                     "uniqueItems": false,
                     "type": ["array", "null"],
                     "items": {
-                        "$ref": "#/definitions/TestClass1"
+                        "allOf": [{
+                                "$ref": "#/definitions/TestClass1"
+                            }, {
+                                "title": "TestClass1",
+                                "description": "looked-up from field: TestClass1",
+                                "additionalProperties": false,
+                                "patternProperties": {
+                                    "^generic.+$": {
+                                        "type": "string"
+                                    }
+                                }
+                            }]
                     }
                 }
             },
@@ -225,7 +275,18 @@
             "uniqueItems": false,
             "type": "array",
             "items": {
-                "$ref": "#/definitions/TestClass2(Long)"
+                "allOf": [{
+                        "$ref": "#/definitions/TestClass2(Long)"
+                    }, {
+                        "title": "TestClass2<Long>",
+                        "description": "looked-up from field: TestClass2<Long>",
+                        "additionalProperties": false,
+                        "patternProperties": {
+                            "^generic.+$": {
+                                "type": "integer"
+                            }
+                        }
+                    }]
             }
         }
     },
diff --git a/jsonschema-generator/src/test/resources/com/github/victools/jsonschema/generator/testenum-FULL_DOCUMENTATION-default.json b/jsonschema-generator/src/test/resources/com/github/victools/jsonschema/generator/testenum-FULL_DOCUMENTATION-default.json
index 5f701fe..d24e5a4 100644
--- a/jsonschema-generator/src/test/resources/com/github/victools/jsonschema/generator/testenum-FULL_DOCUMENTATION-default.json
+++ b/jsonschema-generator/src/test/resources/com/github/victools/jsonschema/generator/testenum-FULL_DOCUMENTATION-default.json
@@ -1,7 +1,7 @@
 {
     "definitions": {
         "TestEnum-nullable": {
-            "oneOf": [{
+            "anyOf": [{
                     "type": "null"
                 }, {
                     "$ref": "#"
diff --git a/jsonschema-module-jackson/src/test/java/com/github/victools/jsonschema/module/jackson/AbstractTypeAwareTest.java b/jsonschema-module-jackson/src/test/java/com/github/victools/jsonschema/module/jackson/AbstractTypeAwareTest.java
new file mode 100644
index 0000000..50ce687
--- /dev/null
+++ b/jsonschema-module-jackson/src/test/java/com/github/victools/jsonschema/module/jackson/AbstractTypeAwareTest.java
@@ -0,0 +1,119 @@
+/*
+ * 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.module.jackson;
+
+import com.fasterxml.classmate.ResolvedType;
+import com.fasterxml.classmate.ResolvedTypeWithMembers;
+import com.fasterxml.classmate.members.ResolvedField;
+import com.fasterxml.classmate.members.ResolvedMethod;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.github.victools.jsonschema.generator.FieldScope;
+import com.github.victools.jsonschema.generator.MethodScope;
+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.TypeContext;
+import com.github.victools.jsonschema.generator.impl.TypeContextFactory;
+import java.util.stream.Stream;
+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.
+ */
+public class AbstractTypeAwareTest {
+
+    private final Class<?> testClass;
+    private SchemaGenerationContext context;
+    private ResolvedTypeWithMembers testClassMembers;
+
+    protected AbstractTypeAwareTest(Class<?> testClass) {
+        this.testClass = testClass;
+    }
+
+    /**
+     * 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, Mockito.RETURNS_DEEP_STUBS);
+        Mockito.when(this.context.getTypeContext()).thenReturn(typeContext);
+
+        SchemaGeneratorConfig config = Mockito.mock(SchemaGeneratorConfig.class);
+        Mockito.when(this.context.getGeneratorConfig()).thenReturn(config);
+        Mockito.when(config.resolveArrayMaxItems(Mockito.any(FieldScope.class))).thenReturn(null);
+        Mockito.when(config.resolveArrayMaxItems(Mockito.any(MethodScope.class))).thenReturn(null);
+        Mockito.when(config.resolveArrayMaxItemsForType(Mockito.any())).thenReturn(null);
+        Mockito.when(config.resolveArrayMinItems(Mockito.any(FieldScope.class))).thenReturn(null);
+        Mockito.when(config.resolveArrayMinItems(Mockito.any(MethodScope.class))).thenReturn(null);
+        Mockito.when(config.resolveArrayMinItemsForType(Mockito.any())).thenReturn(null);
+        Mockito.when(config.resolveArrayUniqueItems(Mockito.any(FieldScope.class))).thenReturn(null);
+        Mockito.when(config.resolveArrayUniqueItems(Mockito.any(MethodScope.class))).thenReturn(null);
+        Mockito.when(config.resolveArrayUniqueItemsForType(Mockito.any())).thenReturn(null);
+        Mockito.when(config.resolveStringMaxLength(Mockito.any(FieldScope.class))).thenReturn(null);
+        Mockito.when(config.resolveStringMaxLength(Mockito.any(MethodScope.class))).thenReturn(null);
+        Mockito.when(config.resolveStringMaxLengthForType(Mockito.any())).thenReturn(null);
+        Mockito.when(config.resolveStringMinLength(Mockito.any(FieldScope.class))).thenReturn(null);
+        Mockito.when(config.resolveStringMinLength(Mockito.any(MethodScope.class))).thenReturn(null);
+        Mockito.when(config.resolveStringMinLengthForType(Mockito.any())).thenReturn(null);
+
+        Mockito.when(config.getSchemaVersion()).thenReturn(schemaVersion);
+        Answer<String> keywordLookup = invocation -> ((SchemaKeyword) invocation.getArgument(0)).forVersion(schemaVersion);
+        Mockito.when(config.getKeyword(Mockito.any())).thenAnswer(keywordLookup);
+        Mockito.when(this.context.getKeyword(Mockito.any())).thenAnswer(keywordLookup);
+
+        ObjectMapper objectMapper = new ObjectMapper();
+        Mockito.when(config.getObjectMapper()).thenReturn(objectMapper);
+        Mockito.when(config.createArrayNode()).thenAnswer((_invocation) -> objectMapper.createArrayNode());
+        Mockito.when(config.createObjectNode()).thenAnswer((_invocation) -> objectMapper.createObjectNode());
+        Mockito.when(this.context.createStandardDefinitionReference(Mockito.any(ResolvedType.class), Mockito.any()))
+                .thenAnswer((_invocation) -> objectMapper.createObjectNode());
+    }
+
+    protected SchemaGenerationContext getContext() {
+        return this.context;
+    }
+
+    protected FieldScope getTestClassField(String fieldName) {
+        ResolvedField resolvedField = Stream.of(this.testClassMembers.getMemberFields())
+                .filter(field -> fieldName.equals(field.getName()))
+                .findAny()
+                .orElseGet(
+                        () -> Stream.of(this.testClassMembers.getStaticFields())
+                                .filter(field -> fieldName.equals(field.getName()))
+                                .findAny()
+                                .get());
+        return this.context.getTypeContext().createFieldScope(resolvedField, this.testClassMembers);
+    }
+
+    protected MethodScope getTestClassMethod(String methodName) {
+        ResolvedMethod resolvedMethod = Stream.of(this.testClassMembers.getMemberMethods())
+                .filter(method -> methodName.equals(method.getName()))
+                .findAny()
+                .orElseGet(
+                        () -> Stream.of(this.testClassMembers.getStaticMethods())
+                                .filter(method -> methodName.equals(method.getName()))
+                                .findAny()
+                                .get());
+        return this.context.getTypeContext().createMethodScope(resolvedMethod, this.testClassMembers);
+    }
+}
diff --git a/jsonschema-module-jackson/src/test/java/com/github/victools/jsonschema/module/jackson/JacksonModuleTest.java b/jsonschema-module-jackson/src/test/java/com/github/victools/jsonschema/module/jackson/JacksonModuleTest.java
index 937b507..7b6e98c 100644
--- a/jsonschema-module-jackson/src/test/java/com/github/victools/jsonschema/module/jackson/JacksonModuleTest.java
+++ b/jsonschema-module-jackson/src/test/java/com/github/victools/jsonschema/module/jackson/JacksonModuleTest.java
@@ -21,6 +21,7 @@ import com.fasterxml.jackson.annotation.JsonProperty;
 import com.fasterxml.jackson.annotation.JsonPropertyDescription;
 import com.github.victools.jsonschema.generator.ConfigFunction;
 import com.github.victools.jsonschema.generator.FieldScope;
+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.SchemaGeneratorGeneralConfigPart;
@@ -42,6 +43,7 @@ public class JacksonModuleTest {
 
     private SchemaGeneratorConfigBuilder configBuilder;
     private SchemaGeneratorConfigPart<FieldScope> fieldConfigPart;
+    private SchemaGeneratorConfigPart<MethodScope> methodConfigPart;
     private SchemaGeneratorGeneralConfigPart typesInGeneralConfigPart;
 
     @Before
@@ -49,6 +51,8 @@ public class JacksonModuleTest {
         this.configBuilder = Mockito.mock(SchemaGeneratorConfigBuilder.class);
         this.fieldConfigPart = Mockito.spy(new SchemaGeneratorConfigPart<>());
         Mockito.when(this.configBuilder.forFields()).thenReturn(this.fieldConfigPart);
+        this.methodConfigPart = Mockito.spy(new SchemaGeneratorConfigPart<>());
+        Mockito.when(this.configBuilder.forMethods()).thenReturn(this.methodConfigPart);
         this.typesInGeneralConfigPart = Mockito.spy(new SchemaGeneratorGeneralConfigPart());
         Mockito.when(this.configBuilder.forTypesInGeneral()).thenReturn(this.typesInGeneralConfigPart);
     }
@@ -59,7 +63,56 @@ public class JacksonModuleTest {
 
         this.verifyCommonConfigurations();
 
-        Mockito.verifyNoMoreInteractions(this.configBuilder, this.fieldConfigPart, this.typesInGeneralConfigPart);
+        Mockito.verify(this.configBuilder).forMethods();
+        Mockito.verify(this.typesInGeneralConfigPart).withSubtypeResolver(Mockito.any());
+        Mockito.verify(this.fieldConfigPart).withTargetTypeOverridesResolver(Mockito.any());
+        Mockito.verify(this.methodConfigPart).withTargetTypeOverridesResolver(Mockito.any());
+        Mockito.verify(this.typesInGeneralConfigPart).withCustomDefinitionProvider(Mockito.any());
+        Mockito.verify(this.fieldConfigPart).withCustomDefinitionProvider(Mockito.any());
+        Mockito.verify(this.methodConfigPart).withCustomDefinitionProvider(Mockito.any());
+
+        Mockito.verifyNoMoreInteractions(this.configBuilder, this.fieldConfigPart, this.methodConfigPart, this.typesInGeneralConfigPart);
+    }
+
+
+    @Test
+    public void testApplyToConfigBuilderWithSkipSubtypeLookupAndIgnoreTypeInfoTranformOptions() {
+        new JacksonModule(JacksonOption.SKIP_SUBTYPE_LOOKUP, JacksonOption.IGNORE_TYPE_INFO_TRANSFORM)
+                .applyToConfigBuilder(this.configBuilder);
+
+        this.verifyCommonConfigurations();
+
+        Mockito.verifyNoMoreInteractions(this.configBuilder, this.fieldConfigPart, this.methodConfigPart, this.typesInGeneralConfigPart);
+    }
+
+    @Test
+    public void testApplyToConfigBuilderWithSkipSubtypeLookupOption() {
+        new JacksonModule(JacksonOption.SKIP_SUBTYPE_LOOKUP)
+                .applyToConfigBuilder(this.configBuilder);
+
+        this.verifyCommonConfigurations();
+
+        Mockito.verify(this.configBuilder).forMethods();
+        Mockito.verify(this.typesInGeneralConfigPart).withCustomDefinitionProvider(Mockito.any());
+        Mockito.verify(this.fieldConfigPart).withCustomDefinitionProvider(Mockito.any());
+        Mockito.verify(this.methodConfigPart).withCustomDefinitionProvider(Mockito.any());
+
+        Mockito.verifyNoMoreInteractions(this.configBuilder, this.fieldConfigPart, this.methodConfigPart, this.typesInGeneralConfigPart);
+    }
+
+    @Test
+    public void testApplyToConfigBuilderWithIgnoreTypeInfoTranformOption() {
+        new JacksonModule(JacksonOption.IGNORE_TYPE_INFO_TRANSFORM)
+                .applyToConfigBuilder(this.configBuilder);
+
+        this.verifyCommonConfigurations();
+
+        Mockito.verify(this.configBuilder).forMethods();
+        Mockito.verify(this.typesInGeneralConfigPart).withSubtypeResolver(Mockito.any());
+        Mockito.verify(this.fieldConfigPart).withTargetTypeOverridesResolver(Mockito.any());
+        Mockito.verify(this.methodConfigPart).withTargetTypeOverridesResolver(Mockito.any());
+
+        Mockito.verifyNoMoreInteractions(this.configBuilder, this.fieldConfigPart, this.methodConfigPart, this.typesInGeneralConfigPart);
     }
 
     @Test
@@ -69,9 +122,15 @@ public class JacksonModuleTest {
 
         this.verifyCommonConfigurations();
 
-        Mockito.verify(this.typesInGeneralConfigPart).withCustomDefinitionProvider(Mockito.any());
+        Mockito.verify(this.configBuilder).forMethods();
+        Mockito.verify(this.typesInGeneralConfigPart).withSubtypeResolver(Mockito.any());
+        Mockito.verify(this.typesInGeneralConfigPart, Mockito.times(2)).withCustomDefinitionProvider(Mockito.any());
+        Mockito.verify(this.fieldConfigPart).withTargetTypeOverridesResolver(Mockito.any());
+        Mockito.verify(this.fieldConfigPart).withCustomDefinitionProvider(Mockito.any());
+        Mockito.verify(this.methodConfigPart).withTargetTypeOverridesResolver(Mockito.any());
+        Mockito.verify(this.methodConfigPart).withCustomDefinitionProvider(Mockito.any());
 
-        Mockito.verifyNoMoreInteractions(this.configBuilder, this.fieldConfigPart, this.typesInGeneralConfigPart);
+        Mockito.verifyNoMoreInteractions(this.configBuilder, this.fieldConfigPart, this.methodConfigPart, this.typesInGeneralConfigPart);
     }
 
     private void verifyCommonConfigurations() {
diff --git a/jsonschema-module-jackson/src/test/java/com/github/victools/jsonschema/module/jackson/JsonSubTypesResolverCustomDefinitionsTest.java b/jsonschema-module-jackson/src/test/java/com/github/victools/jsonschema/module/jackson/JsonSubTypesResolverCustomDefinitionsTest.java
new file mode 100644
index 0000000..70a4faa
--- /dev/null
+++ b/jsonschema-module-jackson/src/test/java/com/github/victools/jsonschema/module/jackson/JsonSubTypesResolverCustomDefinitionsTest.java
@@ -0,0 +1,212 @@
+/*
+ * 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.module.jackson;
+
+import com.fasterxml.classmate.ResolvedType;
+import com.fasterxml.jackson.annotation.JsonSubTypes;
+import com.fasterxml.jackson.annotation.JsonTypeInfo;
+import com.fasterxml.jackson.annotation.JsonTypeName;
+import com.github.victools.jsonschema.generator.CustomDefinition;
+import com.github.victools.jsonschema.generator.FieldScope;
+import com.github.victools.jsonschema.generator.MemberScope;
+import com.github.victools.jsonschema.generator.MethodScope;
+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.Mockito;
+import org.mockito.stubbing.Answer;
+import org.skyscreamer.jsonassert.JSONAssert;
+import org.skyscreamer.jsonassert.JSONCompareMode;
+
+/**
+ * Test for the {@link JsonSubTypesResolver}.
+ */
+@RunWith(JUnitParamsRunner.class)
+public class JsonSubTypesResolverCustomDefinitionsTest extends AbstractTypeAwareTest {
+
+    private final JsonSubTypesResolver instance = new JsonSubTypesResolver();
+
+    public JsonSubTypesResolverCustomDefinitionsTest() {
+        super(TestClassWithSuperTypeReferences.class);
+    }
+
+    @Before
+    public void setUp() {
+        this.prepareContextForVersion(SchemaVersion.DRAFT_2019_09);
+        Answer<String> memberTitleLookUp = invocation -> ((MemberScope<?, ?>) invocation.getArgument(0))
+                .getType().getErasedType() == TestSubClass1.class ? "property attribute" : null;
+        Mockito.when(this.getContext().getGeneratorConfig().resolveTitle(Mockito.any(FieldScope.class))).thenAnswer(memberTitleLookUp);
+        Mockito.when(this.getContext().getGeneratorConfig().resolveTitle(Mockito.any(MethodScope.class))).thenAnswer(memberTitleLookUp);
+    }
+
+    private void assertCustomDefinitionsAreEqual(String customDefinition, CustomDefinition result, CustomDefinition.DefinitionType definitionType)
+            throws Exception {
+        if (customDefinition == null) {
+            Assert.assertNull(result);
+        } else {
+            Assert.assertNotNull(result);
+            Assert.assertNotNull(result.getValue());
+            JSONAssert.assertEquals('\n' + result.getValue().toString() + '\n',
+                    customDefinition, result.getValue().toString(), JSONCompareMode.STRICT);
+            Assert.assertEquals(definitionType, result.getDefinitionType());
+            Assert.assertEquals(CustomDefinition.AttributeInclusion.NO, result.getAttributeInclusion());
+        }
+    }
+
+    public Object[] parametersForTestProvideCustomSchemaDefinition() {
+        return new Object[][]{
+            {TestClassWithSuperTypeReferences.class, null},
+            {TestSuperClassWithNameProperty.class, null},
+            {TestSubClass1.class, "{\"allOf\":[{},{\"type\":\"object\",\"properties\":{\"@type\":{\"const\":\"SUB_CLASS_1\"}}}]}"}
+        };
+    }
+
+    @Test
+    @Parameters
+    public void testProvideCustomSchemaDefinition(Class<?> erasedTargetType, String customDefinition) throws Exception {
+        ResolvedType javaType = this.getContext().getTypeContext().resolve(erasedTargetType);
+        CustomDefinition result = this.instance.provideCustomSchemaDefinition(javaType, this.getContext());
+        this.assertCustomDefinitionsAreEqual(customDefinition, result, CustomDefinition.DefinitionType.STANDARD);
+    }
+
+    public Object[] parametersForTestProvideCustomPropertySchemaDefinitionForField() {
+        return new Object[][]{
+            {"supertypeNoAnnotation", null, null},
+            {"supertypeNoAnnotation", TestSubClass1.class, null},
+            {"supertypeNoAnnotation", TestSubClass2.class, null},
+            {"supertypeWithAnnotationOnField", null, null},
+            {"supertypeWithAnnotationOnField", TestSubClass1.class,
+                "{\"allOf\":[{},{\"title\":\"property attribute\",\"type\":\"object\",\"properties\":{\"fullClass\":{\"const\":"
+                + "\"com.github.victools.jsonschema.module.jackson.JsonSubTypesResolverCustomDefinitionsTest$TestSubClass1\"}}}]}"},
+            {"supertypeWithAnnotationOnField", TestSubClass2.class,
+                "{\"allOf\":[{},{\"type\":\"object\",\"properties\":{\"fullClass\":{\"const\":"
+                + "\"com.github.victools.jsonschema.module.jackson.JsonSubTypesResolverCustomDefinitionsTest$TestSubClass2\"}}}]}"},
+            {"supertypeWithAnnotationOnGetter", null, null},
+            {"supertypeWithAnnotationOnGetter", TestSubClass1.class,
+                "{\"allOf\":[{},{\"title\":\"property attribute\",\"type\":\"object\",\"properties\":{\"@type\":{\"const\":\"SUB_CLASS_1\"}}}]}"},
+            {"supertypeWithAnnotationOnGetter", TestSubClass2.class,
+                "{\"allOf\":[{},{\"type\":\"object\",\"properties\":{\"@type\":{\"const\":\"SUB_CLASS_2\"}}}]}"},
+            {"supertypeWithAnnotationOnFieldAndGetter", null, null},
+            {"supertypeWithAnnotationOnFieldAndGetter", TestSubClass1.class,
+                "{\"type\":\"array\",\"items\":[{\"type\":\"string\",\"const\":"
+                + "\"com.github.victools.jsonschema.module.jackson.JsonSubTypesResolverCustomDefinitionsTest$TestSubClass1\"},"
+                + "{\"allOf\":[{},{\"title\":\"property attribute\"}]}]}"},
+            {"supertypeWithAnnotationOnFieldAndGetter", TestSubClass2.class,
+                "{\"type\":\"array\",\"items\":[{\"type\":\"string\",\"const\":"
+                + "\"com.github.victools.jsonschema.module.jackson.JsonSubTypesResolverCustomDefinitionsTest$TestSubClass2\"},{}]}"}
+        };
+    }
+
+    @Test
+    @Parameters
+    public void testProvideCustomPropertySchemaDefinitionForField(String fieldName, Class<?> typeOverride, String customDefinition)
+            throws Exception {
+        FieldScope field = this.getTestClassField(fieldName);
+        if (typeOverride != null) {
+            field = field.withOverriddenType(this.getContext().getTypeContext().resolve(typeOverride));
+        }
+        CustomDefinition result = this.instance.provideCustomPropertySchemaDefinition(field, this.getContext());
+        this.assertCustomDefinitionsAreEqual(customDefinition, result, CustomDefinition.DefinitionType.INLINE);
+    }
+
+    public Object[] parametersForTestProvideCustomPropertySchemaDefinitionForMethod() {
+        return new Object[][]{
+            {"getSupertypeNoAnnotation", null, null},
+            {"getSupertypeNoAnnotation", TestSubClass1.class, null},
+            {"getSupertypeNoAnnotation", TestSubClass2.class, null},
+            {"getSupertypeWithAnnotationOnField", null, null},
+            {"getSupertypeWithAnnotationOnField", TestSubClass1.class,
+                "{\"allOf\":[{},{\"title\":\"property attribute\",\"type\":\"object\",\"properties\":{\"fullClass\":{\"const\":"
+                + "\"com.github.victools.jsonschema.module.jackson.JsonSubTypesResolverCustomDefinitionsTest$TestSubClass1\"}}}]}"},
+            {"getSupertypeWithAnnotationOnField", TestSubClass2.class,
+                "{\"allOf\":[{},{\"type\":\"object\",\"properties\":{\"fullClass\":{\"const\":"
+                + "\"com.github.victools.jsonschema.module.jackson.JsonSubTypesResolverCustomDefinitionsTest$TestSubClass2\"}}}]}"},
+            {"getSupertypeWithAnnotationOnGetter", null, null},
+            {"getSupertypeWithAnnotationOnGetter", TestSubClass1.class,
+                "{\"allOf\":[{},{\"title\":\"property attribute\",\"type\":\"object\",\"properties\":{\"@type\":{\"const\":\"SUB_CLASS_1\"}}}]}"},
+            {"getSupertypeWithAnnotationOnGetter", TestSubClass2.class,
+                "{\"allOf\":[{},{\"type\":\"object\",\"properties\":{\"@type\":{\"const\":\"SUB_CLASS_2\"}}}]}"},
+            {"getSupertypeWithAnnotationOnFieldAndGetter", null, null},
+            {"getSupertypeWithAnnotationOnFieldAndGetter", TestSubClass1.class,
+                "{\"type\":\"object\",\"properties\":{\"SUB_CLASS_1\":{\"allOf\":[{},{\"title\":\"property attribute\"}]}}}"},
+            {"getSupertypeWithAnnotationOnFieldAndGetter", TestSubClass2.class,
+                "{\"type\":\"object\",\"properties\":{\"SUB_CLASS_2\":{}}}"}
+        };
+    }
+
+    @Test
+    @Parameters
+    public void testProvideCustomPropertySchemaDefinitionForMethod(String methodName, Class<?> typeOverride, String customDefinition)
+            throws Exception {
+        MethodScope method = this.getTestClassMethod(methodName);
+        if (typeOverride != null) {
+            method = method.withOverriddenType(this.getContext().getTypeContext().resolve(typeOverride));
+        }
+        CustomDefinition result = this.instance.provideCustomPropertySchemaDefinition(method, this.getContext());
+        this.assertCustomDefinitionsAreEqual(customDefinition, result, CustomDefinition.DefinitionType.INLINE);
+    }
+
+    private static class TestClassWithSuperTypeReferences {
+
+        public TestSuperClassWithNameProperty supertypeNoAnnotation;
+        @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, property = "fullClass", include = JsonTypeInfo.As.EXISTING_PROPERTY)
+        public TestSuperClassWithNameProperty supertypeWithAnnotationOnField;
+        public TestSuperClassWithNameProperty supertypeWithAnnotationOnGetter;
+        @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.WRAPPER_ARRAY)
+        public TestSuperClassWithNameProperty supertypeWithAnnotationOnFieldAndGetter;
+
+        public TestSuperClassWithNameProperty getSupertypeNoAnnotation() {
+            return this.supertypeNoAnnotation;
+        }
+
+        public TestSuperClassWithNameProperty getSupertypeWithAnnotationOnField() {
+            return this.supertypeWithAnnotationOnField;
+        }
+
+        @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY)
+        public TestSuperClassWithNameProperty getSupertypeWithAnnotationOnGetter() {
+            return this.supertypeWithAnnotationOnGetter;
+        }
+
+        @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.WRAPPER_OBJECT)
+        public TestSuperClassWithNameProperty getSupertypeWithAnnotationOnFieldAndGetter() {
+            return this.supertypeWithAnnotationOnFieldAndGetter;
+        }
+    }
+
+    @JsonTypeInfo(use = JsonTypeInfo.Id.NAME)
+    @JsonSubTypes({
+        @JsonSubTypes.Type(TestSubClass1.class),
+        @JsonSubTypes.Type(TestSubClass2.class)
+    })
+    private static class TestSuperClassWithNameProperty {
+
+        public String fullClass;
+    }
+
+    @JsonTypeName("SUB_CLASS_1")
+    private static class TestSubClass1 extends TestSuperClassWithNameProperty {
+    }
+
+    @JsonTypeName("SUB_CLASS_2")
+    private static class TestSubClass2 extends TestSuperClassWithNameProperty {
+    }
+}
diff --git a/jsonschema-module-jackson/src/test/java/com/github/victools/jsonschema/module/jackson/JsonSubTypesResolverLookUpTest.java b/jsonschema-module-jackson/src/test/java/com/github/victools/jsonschema/module/jackson/JsonSubTypesResolverLookUpTest.java
new file mode 100644
index 0000000..ae30e84
--- /dev/null
+++ b/jsonschema-module-jackson/src/test/java/com/github/victools/jsonschema/module/jackson/JsonSubTypesResolverLookUpTest.java
@@ -0,0 +1,164 @@
+/*
+ * 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.module.jackson;
+
+import com.fasterxml.classmate.ResolvedType;
+import com.fasterxml.jackson.annotation.JsonSubTypes;
+import com.fasterxml.jackson.annotation.JsonTypeInfo;
+import com.fasterxml.jackson.annotation.JsonTypeName;
+import com.github.victools.jsonschema.generator.SchemaVersion;
+import com.github.victools.jsonschema.generator.TypeContext;
+import java.util.Arrays;
+import java.util.List;
+import junitparams.JUnitParamsRunner;
+import junitparams.Parameters;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Test for the {@link JsonSubTypesResolver}.
+ */
+@RunWith(JUnitParamsRunner.class)
+public class JsonSubTypesResolverLookUpTest extends AbstractTypeAwareTest {
+
+    private final JsonSubTypesResolver instance = new JsonSubTypesResolver();
+
+    public JsonSubTypesResolverLookUpTest() {
+        super(TestSuperClassWithNameProperty.class);
+    }
+
+    private void assertErasedSubtypesAreEquals(List<Class<?>> erasedSubtypes, List<ResolvedType> subtypes) {
+        if (erasedSubtypes == null) {
+            Assert.assertNull(subtypes);
+        } else {
+            Assert.assertNotNull(subtypes);
+            Assert.assertEquals(erasedSubtypes.size(), subtypes.size());
+            for (int index = 0; index < erasedSubtypes.size(); index++) {
+                Assert.assertSame(erasedSubtypes.get(index), subtypes.get(index).getErasedType());
+            }
+        }
+    }
+
+    public Object[] parametersForTestFindSubtypes() {
+        return new Object[][]{
+            {JsonSubTypesResolverLookUpTest.class, null},
+            {TestSuperClassWithNameProperty.class, Arrays.asList(TestSubClass1.class, TestSubClass2.class, TestSubClass3.class)}
+        };
+    }
+
+    @Test
+    @Parameters
+    public void testFindSubtypes(Class<?> targetType, List<Class<?>> erasedSubtypes) {
+        this.prepareContextForVersion(SchemaVersion.DRAFT_2019_09);
+        TypeContext typeContext = this.getContext().getTypeContext();
+        List<ResolvedType> subtypes = this.instance.findSubtypes(typeContext.resolve(targetType), this.getContext());
+        this.assertErasedSubtypesAreEquals(erasedSubtypes, subtypes);
+    }
+
+    public Object[] parametersForTestFindTargetTypeOverridesForField() {
+        return new Object[][]{
+            {"supertypeNoAnnotation", null},
+            {"supertypeWithAnnotationOnField", Arrays.asList(TestSubClass1.class, TestSubClass2.class)},
+            {"supertypeWithAnnotationOnGetter", Arrays.asList(TestSubClass2.class, TestSubClass3.class)},
+            {"supertypeWithAnnotationOnFieldAndGetter", Arrays.asList(TestSubClass1.class, TestSubClass2.class)}
+        };
+    }
+
+    @Test
+    @Parameters
+    public void testFindTargetTypeOverridesForField(String fieldName, List<Class<?>> erasedSubtypes) {
+        this.prepareContextForVersion(SchemaVersion.DRAFT_2019_09);
+        List<ResolvedType> subtypes = this.instance.findTargetTypeOverrides(this.getTestClassField(fieldName));
+        this.assertErasedSubtypesAreEquals(erasedSubtypes, subtypes);
+    }
+
+    public Object[] parametersForTestFindTargetTypeOverridesForMethod() {
+        return new Object[][]{
+            {"getSupertypeNoAnnotation", null},
+            {"getSupertypeWithAnnotationOnField", Arrays.asList(TestSubClass1.class, TestSubClass2.class)},
+            {"getSupertypeWithAnnotationOnGetter", Arrays.asList(TestSubClass2.class, TestSubClass3.class)},
+            {"getSupertypeWithAnnotationOnFieldAndGetter", Arrays.asList(TestSubClass2.class, TestSubClass3.class)}
+        };
+    }
+
+    @Test
+    @Parameters
+    public void testFindTargetTypeOverridesForMethod(String methodName, List<Class<?>> erasedSubtypes) {
+        this.prepareContextForVersion(SchemaVersion.DRAFT_2019_09);
+        List<ResolvedType> subtypes = this.instance.findTargetTypeOverrides(this.getTestClassMethod(methodName));
+        this.assertErasedSubtypesAreEquals(erasedSubtypes, subtypes);
+    }
+
+    @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type", include = JsonTypeInfo.As.PROPERTY)
+    @JsonSubTypes({
+        @JsonSubTypes.Type(TestSubClass1.class),
+        @JsonSubTypes.Type(TestSubClass2.class),
+        @JsonSubTypes.Type(TestSubClass3.class)
+    })
+    private static class TestSuperClassWithNameProperty {
+
+        public TestSuperClassWithNameProperty supertypeNoAnnotation;
+        @JsonSubTypes({
+            @JsonSubTypes.Type(TestSubClass1.class),
+            @JsonSubTypes.Type(TestSubClass2.class)
+        })
+        public TestSuperClassWithNameProperty supertypeWithAnnotationOnField;
+        public TestSuperClassWithNameProperty supertypeWithAnnotationOnGetter;
+        @JsonSubTypes({
+            @JsonSubTypes.Type(TestSubClass1.class),
+            @JsonSubTypes.Type(TestSubClass2.class)
+        })
+        public TestSuperClassWithNameProperty supertypeWithAnnotationOnFieldAndGetter;
+
+        public TestSuperClassWithNameProperty getSupertypeNoAnnotation() {
+            return this.supertypeNoAnnotation;
+        }
+
+        public TestSuperClassWithNameProperty getSupertypeWithAnnotationOnField() {
+            return this.supertypeWithAnnotationOnField;
+        }
+
+        @JsonSubTypes({
+            @JsonSubTypes.Type(TestSubClass2.class),
+            @JsonSubTypes.Type(TestSubClass3.class)
+        })
+        public TestSuperClassWithNameProperty getSupertypeWithAnnotationOnGetter() {
+            return this.supertypeWithAnnotationOnGetter;
+        }
+
+        @JsonSubTypes({
+            @JsonSubTypes.Type(TestSubClass2.class),
+            @JsonSubTypes.Type(TestSubClass3.class)
+        })
+        public TestSuperClassWithNameProperty getSupertypeWithAnnotationOnFieldAndGetter() {
+            return this.supertypeWithAnnotationOnFieldAndGetter;
+        }
+    }
+
+    @JsonTypeName("SUB_CLASS_1")
+    private static class TestSubClass1 extends TestSuperClassWithNameProperty {
+    }
+
+    @JsonTypeName("SUB_CLASS_2")
+    private static class TestSubClass2 extends TestSuperClassWithNameProperty {
+    }
+
+    @JsonTypeName("SUB_CLASS_3")
+    private static class TestSubClass3 extends TestSuperClassWithNameProperty {
+    }
+}
diff --git a/jsonschema-module-jackson/src/test/java/com/github/victools/jsonschema/module/jackson/SubtypeResolutionIntegrationTest.java b/jsonschema-module-jackson/src/test/java/com/github/victools/jsonschema/module/jackson/SubtypeResolutionIntegrationTest.java
new file mode 100644
index 0000000..8452304
--- /dev/null
+++ b/jsonschema-module-jackson/src/test/java/com/github/victools/jsonschema/module/jackson/SubtypeResolutionIntegrationTest.java
@@ -0,0 +1,172 @@
+/*
+ * 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.module.jackson;
+
+import com.fasterxml.jackson.annotation.JsonSubTypes;
+import com.fasterxml.jackson.annotation.JsonTypeInfo;
+import com.fasterxml.jackson.annotation.JsonTypeName;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.github.victools.jsonschema.generator.Option;
+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;
+import com.networknt.schema.JsonSchema;
+import com.networknt.schema.JsonSchemaFactory;
+import com.networknt.schema.SpecVersion;
+import com.networknt.schema.ValidationMessage;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Scanner;
+import java.util.Set;
+import java.util.stream.Collectors;
+import org.junit.Assert;
+import org.junit.Test;
+import org.skyscreamer.jsonassert.JSONAssert;
+import org.skyscreamer.jsonassert.JSONCompareMode;
+
+/**
+ * Integration test of this module being used in a real SchemaGenerator instance, focusing on the subtype resolution.
+ */
+public class SubtypeResolutionIntegrationTest {
+
+    @Test
+    public void testIntegration() throws Exception {
+        JacksonModule module = new JacksonModule(JacksonOption.FLATTENED_ENUMS_FROM_JSONVALUE);
+        SchemaGeneratorConfig config = new SchemaGeneratorConfigBuilder(new ObjectMapper(), SchemaVersion.DRAFT_2019_09, OptionPreset.PLAIN_JSON)
+                .with(Option.DEFINITIONS_FOR_ALL_OBJECTS, Option.NULLABLE_FIELDS_BY_DEFAULT)
+                .with(module)
+                .build();
+        SchemaGenerator generator = new SchemaGenerator(config);
+        JsonNode result = generator.generateSchema(TestClassForSubtypeResolution.class);
+
+        String rawJsonSchema = result.toString();
+        JSONAssert.assertEquals('\n' + rawJsonSchema + '\n',
+                loadResource("subtype-integration-test-result.json"), rawJsonSchema, JSONCompareMode.STRICT);
+
+        JsonSchema schemaForValidation = JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V201909).getSchema(result);
+        String jsonInstance = config.getObjectMapper().writeValueAsString(new TestClassForSubtypeResolution());
+
+        Set<ValidationMessage> validationResult = schemaForValidation.validate(config.getObjectMapper().readTree(jsonInstance));
+        if (!validationResult.isEmpty()) {
+            Assert.fail("\n" + jsonInstance + "\n  " + validationResult.stream()
+                    .map(ValidationMessage::getMessage)
+                    .collect(Collectors.joining("\n  ")));
+        }
+    }
+
+    private static String loadResource(String resourcePath) throws IOException {
+        StringBuilder stringBuilder = new StringBuilder();
+        try (InputStream inputStream = SubtypeResolutionIntegrationTest.class
+                .getResourceAsStream(resourcePath);
+                Scanner scanner = new Scanner(inputStream, StandardCharsets.UTF_8.name())) {
+            while (scanner.hasNext()) {
+                stringBuilder.append(scanner.nextLine()).append('\n');
+            }
+        }
+        String fileAsString = stringBuilder.toString();
+        return fileAsString;
+
+    }
+
+    private static class TestClassForSubtypeResolution {
+
+        public TestSuperClass supertypeWithoutAnnotation;
+        @JsonSubTypes({
+            @JsonSubTypes.Type(value = TestSubClassWithTypeNameAnnotation.class, name = "SpecificSubClass1"),
+            @JsonSubTypes.Type(value = TestSubClass2.class, name = "SpecificSubClass2")
+        })
+        public TestSuperClass supertypeWithJsonSubTypesAnnotation;
+        @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.WRAPPER_ARRAY)
+        public TestSuperClass supertypeAsWrapperArray;
+
+        TestClassForSubtypeResolution() {
+            this.supertypeWithoutAnnotation = new TestSubClassWithTypeNameAnnotation(
+                    new TestSubClass2(new TestSubClass3(null))
+            );
+            this.supertypeWithJsonSubTypesAnnotation = new TestSubClass2(
+                    new TestSubClassWithTypeNameAnnotation(
+                            new TestSubClass2(new TestSubClassWithTypeNameAnnotation()),
+                            new TestSubClass2()
+                    )
+            );
+            this.supertypeAsWrapperArray = new TestSubClass3(
+                    new TestSubClass3(null)
+            );
+        }
+    }
+
+    @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.WRAPPER_OBJECT)
+    @JsonSubTypes({
+        @JsonSubTypes.Type(value = TestSubClassWithTypeNameAnnotation.class, name = "SubClass1"),
+        @JsonSubTypes.Type(value = TestSubClass2.class, name = "SubClass2"),
+        @JsonSubTypes.Type(value = TestSubClass3.class, name = "SubClass3")
+    })
+    private static class TestSuperClass {
+
+        public String typeString;
+    }
+
+    @JsonTypeName("AnnotatedSubTypeName")
+    private static class TestSubClassWithTypeNameAnnotation extends TestSuperClass {
+
+        public List<TestSubClass2> directSubClass2;
+
+        TestSubClassWithTypeNameAnnotation(TestSubClass2... directSubClass2) {
+            this.directSubClass2 = Arrays.asList(directSubClass2);
+        }
+    }
+
+    private static class TestSubClass2 extends TestSuperClass {
+
+        @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.EXISTING_PROPERTY, property = "typeString")
+        @JsonSubTypes({
+            @JsonSubTypes.Type(value = TestSubClassWithTypeNameAnnotation.class, name = "Sub1"),
+            @JsonSubTypes.Type(value = TestSubClass3.class, name = "Sub3")
+        })
+        public TestSuperClass superClassViaExistingProperty;
+
+        public TestSubClass2() {
+            this.superClassViaExistingProperty = null;
+        }
+
+        public TestSubClass2(TestSubClassWithTypeNameAnnotation superClassViaExistingProperty) {
+            this.superClassViaExistingProperty = superClassViaExistingProperty;
+            this.superClassViaExistingProperty.typeString = "Sub1";
+        }
+
+        public TestSubClass2(TestSubClass3 superClassViaExistingProperty) {
+            this.superClassViaExistingProperty = superClassViaExistingProperty;
+            this.superClassViaExistingProperty.typeString = "Sub3";
+        }
+    }
+
+    private static class TestSubClass3 extends TestSuperClass {
+
+        @JsonTypeInfo(use = JsonTypeInfo.Id.NAME)
+        public TestSubClass3 recursiveSubClass3;
+
+        TestSubClass3(TestSubClass3 recursiveSubClass3) {
+            this.recursiveSubClass3 = recursiveSubClass3;
+        }
+    }
+}
diff --git a/jsonschema-module-jackson/src/test/resources/com/github/victools/jsonschema/module/jackson/subtype-integration-test-result.json b/jsonschema-module-jackson/src/test/resources/com/github/victools/jsonschema/module/jackson/subtype-integration-test-result.json
new file mode 100644
index 0000000..1a47510
--- /dev/null
+++ b/jsonschema-module-jackson/src/test/resources/com/github/victools/jsonschema/module/jackson/subtype-integration-test-result.json
@@ -0,0 +1,145 @@
+{
+    "$schema": "https://json-schema.org/draft/2019-09/schema",
+    "$defs": {
+        "TestSubClass2-1": {
+            "type": "object",
+            "properties": {
+                "superClassViaExistingProperty": {
+                    "anyOf": [{
+                            "type": "null"
+                        }, {
+                            "$ref": "#/$defs/TestSubClassWithTypeNameAnnotation-1",
+                            "type": "object",
+                            "properties": {
+                                "typeString": {
+                                    "const": "com.github.victools.jsonschema.module.jackson.SubtypeResolutionIntegrationTest$TestSubClassWithTypeNameAnnotation"
+                                }
+                            }
+                        }, {
+                            "$ref": "#/$defs/TestSubClass3-1",
+                            "type": "object",
+                            "properties": {
+                                "typeString": {
+                                    "const": "com.github.victools.jsonschema.module.jackson.SubtypeResolutionIntegrationTest$TestSubClass3"
+                                }
+                            }
+                        }]
+                },
+                "typeString": {
+                    "type": ["string", "null"]
+                }
+            }
+        },
+        "TestSubClass2-2": {
+            "type": "object",
+            "properties": {
+                "SubtypeResolutionIntegrationTest$TestSubClass2": {
+                    "$ref": "#/$defs/TestSubClass2-1"
+                }
+            }
+        },
+        "TestSubClass3-1": {
+            "type": "object",
+            "properties": {
+                "recursiveSubClass3": {
+                    "anyOf": [{
+                            "type": "null"
+                        }, {
+                            "$ref": "#/$defs/TestSubClass3-1",
+                            "type": "object",
+                            "properties": {
+                                "@type": {
+                                    "const": "SubtypeResolutionIntegrationTest$TestSubClass3"
+                                }
+                            }
+                        }]
+                },
+                "typeString": {
+                    "type": ["string", "null"]
+                }
+            }
+        },
+        "TestSubClass3-2": {
+            "type": "object",
+            "properties": {
+                "SubtypeResolutionIntegrationTest$TestSubClass3": {
+                    "$ref": "#/$defs/TestSubClass3-1"
+                }
+            }
+        },
+        "TestSubClassWithTypeNameAnnotation-1": {
+            "type": "object",
+            "properties": {
+                "directSubClass2": {
+                    "type": ["array", "null"],
+                    "items": {
+                        "$ref": "#/$defs/TestSubClass2-2"
+                    }
+                },
+                "typeString": {
+                    "type": ["string", "null"]
+                }
+            }
+        },
+        "TestSubClassWithTypeNameAnnotation-2": {
+            "type": "object",
+            "properties": {
+                "AnnotatedSubTypeName": {
+                    "$ref": "#/$defs/TestSubClassWithTypeNameAnnotation-1"
+                }
+            }
+        }
+    },
+    "type": "object",
+    "properties": {
+        "supertypeAsWrapperArray": {
+            "anyOf": [{
+                    "type": "null"
+                }, {
+                    "type": "array",
+                    "items": [{
+                            "type": "string",
+                            "const": "com.github.victools.jsonschema.module.jackson.SubtypeResolutionIntegrationTest$TestSubClassWithTypeNameAnnotation"
+                        }, {
+                            "$ref": "#/$defs/TestSubClassWithTypeNameAnnotation-1"
+                        }]
+                }, {
+                    "type": "array",
+                    "items": [{
+                            "type": "string",
+                            "const": "com.github.victools.jsonschema.module.jackson.SubtypeResolutionIntegrationTest$TestSubClass2"
+                        }, {
+                            "$ref": "#/$defs/TestSubClass2-1"
+                        }]
+                }, {
+                    "type": "array",
+                    "items": [{
+                            "type": "string",
+                            "const": "com.github.victools.jsonschema.module.jackson.SubtypeResolutionIntegrationTest$TestSubClass3"
+                        }, {
+                            "$ref": "#/$defs/TestSubClass3-1"
+                        }]
+                }]
+        },
+        "supertypeWithJsonSubTypesAnnotation": {
+            "anyOf": [{
+                    "type": "null"
+                }, {
+                    "$ref": "#/$defs/TestSubClassWithTypeNameAnnotation-2"
+                }, {
+                    "$ref": "#/$defs/TestSubClass2-2"
+                }]
+        },
+        "supertypeWithoutAnnotation": {
+            "anyOf": [{
+                    "type": "null"
+                }, {
+                    "$ref": "#/$defs/TestSubClassWithTypeNameAnnotation-2"
+                }, {
+                    "$ref": "#/$defs/TestSubClass2-2"
+                }, {
+                    "$ref": "#/$defs/TestSubClass3-2"
+                }]
+        }
+    }
+}