Repository: victools/jsonschema-generator
Pull request #412 (2023-11-18 09:55:32)

feat: option to inline nullable schemas
Created in response to https://github.com/victools/jsonschema-generator/discussions/410.


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 85318f3..c8c57ab 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 ### `jsonschema-generator`
 #### Added
 - new `Option.STANDARD_FORMATS` includes standard `"format"` values to some types considered by `Option.ADDITIONAL_FIXED_TYPES`
+- new `Option.INLINE_NULLABLE_SCHEMAS` avoids `"<type>-nullable"` entries in the `"definitions"`/`"$defs"`
 
 #### Changed
 - include new `Option.STANDARD_FORMATS` in `OptionPreset.PLAIN_JSON` by default
diff --git a/jsonschema-generator/src/main/java/com/github/victools/jsonschema/generator/Option.java b/jsonschema-generator/src/main/java/com/github/victools/jsonschema/generator/Option.java
index cb39802..d154891 100644
--- a/jsonschema-generator/src/main/java/com/github/victools/jsonschema/generator/Option.java
+++ b/jsonschema-generator/src/main/java/com/github/victools/jsonschema/generator/Option.java
@@ -268,6 +268,14 @@ public enum Option {
      * @since 4.27.0
      */
     DEFINITIONS_FOR_MEMBER_SUPERTYPES(null, null),
+    /**
+     * Whether the "nullable" variants of a sub-schema should be defined in-line, i.e., avoiding a second "MyType-nullable" entry in the
+     * "definitions"/"$defs". This takes precedence over {@link #DEFINITIONS_FOR_ALL_OBJECTS} in this specific case. The non-nullable sub-schema is
+     * unaffected by this setting.
+     *
+     * @since 4.33.0
+     */
+    INLINE_NULLABLE_SCHEMAS(null, null),
     /**
      * Whether all sub-schemas should be defined in-line, i.e. including no "definitions"/"$defs". This takes precedence over
      * {@link #DEFINITIONS_FOR_ALL_OBJECTS} and {@link #DEFINITION_FOR_MAIN_SCHEMA}.
@@ -277,7 +285,8 @@ public enum Option {
      *
      * @since 4.10.0
      */
-    INLINE_ALL_SCHEMAS(InlineSchemaModule::new, null, Option.DEFINITIONS_FOR_ALL_OBJECTS, Option.DEFINITION_FOR_MAIN_SCHEMA),
+    INLINE_ALL_SCHEMAS(InlineSchemaModule::new, null,
+            Option.DEFINITIONS_FOR_ALL_OBJECTS, Option.DEFINITION_FOR_MAIN_SCHEMA, Option.INLINE_NULLABLE_SCHEMAS),
     /**
      * Generally, keys in the collected "definitions"/"$defs" are ensured to be URI compatible but may include parentheses and commas for listing type
      * parameters. By enabling this option, these parentheses and commas will be removed to conform with a reduced set of characters, e.g. as expected
diff --git a/jsonschema-generator/src/main/java/com/github/victools/jsonschema/generator/SchemaBuilder.java b/jsonschema-generator/src/main/java/com/github/victools/jsonschema/generator/SchemaBuilder.java
index 2574309..c4d568c 100644
--- a/jsonschema-generator/src/main/java/com/github/victools/jsonschema/generator/SchemaBuilder.java
+++ b/jsonschema-generator/src/main/java/com/github/victools/jsonschema/generator/SchemaBuilder.java
@@ -205,17 +205,14 @@ public class SchemaBuilder {
     private ObjectNode buildDefinitionsAndResolveReferences(String designatedDefinitionPath, DefinitionKey mainSchemaKey,
             SchemaGenerationContextImpl generationContext) {
         final ObjectNode definitionsNode = this.config.createObjectNode();
-        final boolean createDefinitionsForAll = this.config.shouldCreateDefinitionsForAllObjects();
-        final boolean inlineAllSchemas = this.config.shouldInlineAllSchemas();
 
         final AtomicBoolean considerOnlyDirectReferences = new AtomicBoolean(false);
-        Predicate<DefinitionKey> shouldProduceDefinition = this.getShouldProduceDefinitionCheck(mainSchemaKey, considerOnlyDirectReferences,
-                createDefinitionsForAll, inlineAllSchemas);
+        Predicate<DefinitionKey> shouldProduceDefinition = this.getShouldProduceDefinitionCheck(mainSchemaKey, considerOnlyDirectReferences);
         Map<DefinitionKey, String> baseReferenceKeys = this.getReferenceKeys(mainSchemaKey, shouldProduceDefinition, generationContext);
         considerOnlyDirectReferences.set(true);
-        for (Map.Entry<DefinitionKey, String> entry : baseReferenceKeys.entrySet()) {
-            String definitionName = entry.getValue();
-            DefinitionKey definitionKey = entry.getKey();
+        for (Map.Entry<DefinitionKey, String> baseReferenceKey : baseReferenceKeys.entrySet()) {
+            String definitionName = baseReferenceKey.getValue();
+            DefinitionKey definitionKey = baseReferenceKey.getKey();
             List<ObjectNode> references = generationContext.getReferences(definitionKey);
             List<ObjectNode> nullableReferences = generationContext.getNullableReferences(definitionKey);
             final String referenceKey;
@@ -242,8 +239,7 @@ public class SchemaBuilder {
                     definition = this.config.createObjectNode().put(this.config.getKeyword(SchemaKeyword.TAG_REF), referenceKey);
                 }
                 generationContext.makeNullable(definition);
-                if (generationContext.shouldNeverInlineDefinition(definitionKey)
-                        || (!inlineAllSchemas && (createDefinitionsForAll || nullableReferences.size() > 1))) {
+                if (this.shouldCreateNullableDefinition(generationContext, definitionKey, nullableReferences)) {
                     String nullableDefinitionName = this.definitionNamingStrategy
                             .adjustNullableName(definitionKey, definitionName, generationContext);
                     definitionsNode.set(nullableDefinitionName, definition);
@@ -258,6 +254,20 @@ public class SchemaBuilder {
         return definitionsNode;
     }
 
+    private boolean shouldCreateNullableDefinition(SchemaGenerationContextImpl generationContext, DefinitionKey definitionKey,
+            List<ObjectNode> nullableReferences) {
+        if (this.config.shouldInlineNullableSchemas()) {
+            return false;
+        }
+        if (generationContext.shouldNeverInlineDefinition(definitionKey)) {
+            return true;
+        }
+        if (this.config.shouldInlineAllSchemas()) {
+            return false;
+        }
+        return this.config.shouldCreateDefinitionsForAllObjects() || nullableReferences.size() > 1;
+    }
+
     private String getReferenceKey(DefinitionKey mainSchemaKey, DefinitionKey definitionKey, Supplier<String> addDefinitionAndReturnReferenceKey) {
         final String referenceKey;
         if (definitionKey.equals(mainSchemaKey) && !this.config.shouldCreateDefinitionForMainSchema()) {
@@ -275,12 +285,11 @@ public class SchemaBuilder {
      *
      * @param mainSchemaKey main type to consider
      * @param considerOnlyDirectReferences whether to ignore nullable references when determing about definition vs. inlining
-     * @param createDefinitionsForAll whether to produce definitions for all schemas by default (unless explicitly stated otherwise)
-     * @param inlineAllSchemas whether to inline all schemas by default (unless explicitly stated otherwise)
      * @return reusable predicate
      */
-    private Predicate<DefinitionKey> getShouldProduceDefinitionCheck(DefinitionKey mainSchemaKey, AtomicBoolean considerOnlyDirectReferences,
-            boolean createDefinitionsForAll, boolean inlineAllSchemas) {
+    private Predicate<DefinitionKey> getShouldProduceDefinitionCheck(DefinitionKey mainSchemaKey, AtomicBoolean considerOnlyDirectReferences) {
+        final boolean createDefinitionsForAll = this.config.shouldCreateDefinitionsForAllObjects();
+        final boolean inlineAllSchemas = this.config.shouldInlineAllSchemas();
         return definitionKey -> {
             if (generationContext.shouldNeverInlineDefinition(definitionKey)) {
                 // e.g. custom definition explicitly marked to always produce a definition
diff --git a/jsonschema-generator/src/main/java/com/github/victools/jsonschema/generator/SchemaGeneratorConfig.java b/jsonschema-generator/src/main/java/com/github/victools/jsonschema/generator/SchemaGeneratorConfig.java
index d2753fb..842a5c2 100644
--- a/jsonschema-generator/src/main/java/com/github/victools/jsonschema/generator/SchemaGeneratorConfig.java
+++ b/jsonschema-generator/src/main/java/com/github/victools/jsonschema/generator/SchemaGeneratorConfig.java
@@ -85,6 +85,14 @@ public interface SchemaGeneratorConfig extends StatefulConfig {
      */
     boolean shouldInlineAllSchemas();
 
+    /**
+     * Determine whether nullable sub-schemas should be included in-line, even if they occur multiple times, and not in the schema's
+     * "definitions"/"$defs".
+     *
+     * @return whether to include nullable sub-schemas in-line
+     */
+    boolean shouldInlineNullableSchemas();
+
     /**
      * Determine whether the {@link SchemaKeyword#TAG_SCHEMA} attribute with {@link SchemaKeyword#TAG_SCHEMA_VALUE} should be added.
      *
diff --git a/jsonschema-generator/src/main/java/com/github/victools/jsonschema/generator/impl/SchemaGeneratorConfigImpl.java b/jsonschema-generator/src/main/java/com/github/victools/jsonschema/generator/impl/SchemaGeneratorConfigImpl.java
index 02e82e8..5a835f3 100644
--- a/jsonschema-generator/src/main/java/com/github/victools/jsonschema/generator/impl/SchemaGeneratorConfigImpl.java
+++ b/jsonschema-generator/src/main/java/com/github/victools/jsonschema/generator/impl/SchemaGeneratorConfigImpl.java
@@ -133,6 +133,11 @@ public class SchemaGeneratorConfigImpl implements SchemaGeneratorConfig {
         return this.isOptionEnabled(Option.INLINE_ALL_SCHEMAS);
     }
 
+    @Override
+    public boolean shouldInlineNullableSchemas() {
+        return this.isOptionEnabled(Option.INLINE_NULLABLE_SCHEMAS);
+    }
+
     @Override
     public boolean shouldUsePlainDefinitionKeys() {
         return this.isOptionEnabled(Option.PLAIN_DEFINITION_KEYS);
diff --git a/slate-docs/source/includes/_main-generator-options.md b/slate-docs/source/includes/_main-generator-options.md
index 37c3489..1c44b67 100644
--- a/slate-docs/source/includes/_main-generator-options.md
+++ b/slate-docs/source/includes/_main-generator-options.md
@@ -310,6 +310,14 @@ configBuilder.without(
     <tr><th>#</th><th>Behavior if included</th><th>Behavior if excluded</th></tr>
     <tr>
       <td rowspan="2" style="text-align: right">33</td>
+      <td colspan="2"><code>Option.INLINE_NULLABLE_SCHEMAS</code></td>
+    </tr>
+    <tr>
+      <td>Do not include <code>$defs</code>/<code>definitions</code> for a nullable version of a type, but rather define it "inline". The non-nullable type may still be referenced.</td>
+      <td>Depending on whether <code>DEFINITIONS_FOR_ALL_OBJECTS</code> is included or excluded.</td>
+    </tr>
+    <tr>
+      <td rowspan="2" style="text-align: right">34</td>
       <td colspan="2"><code>Option.PLAIN_DEFINITION_KEYS</code></td>
     </tr>
     <tr>
@@ -317,7 +325,7 @@ configBuilder.without(
       <td>Ensure that the keys for any <code>$defs</code>/<code>definitions</code> are URI compatible (as expected by the JSON Schema specification).</td>
     </tr>
     <tr>
-      <td rowspan="2" style="text-align: right">34</td>
+      <td rowspan="2" style="text-align: right">35</td>
       <td colspan="2"><code>Option.ALLOF_CLEANUP_AT_THE_END</code></td>
     </tr>
     <tr>
@@ -325,7 +333,7 @@ configBuilder.without(
       <td>Do not attempt to reduce <code>allOf</code> wrappers but preserve them as they were generated regardless of them being necessary or not.</td>
     </tr>
     <tr>
-      <td rowspan="2" style="text-align: right">35</td>
+      <td rowspan="2" style="text-align: right">36</td>
       <td colspan="2"><code>Option.STRICT_TYPE_INFO</code></td>
     </tr>
     <tr>
@@ -375,6 +383,7 @@ Below, you can find the lists of <code>Option</code>s included/excluded in the r
 | 30 | `DEFINITION_FOR_MAIN_SCHEMA`                 | ⬜️ | ⬜️ | ⬜️ |
 | 31 | `DEFINITIONS_FOR_MEMBER_SUPERTYPES`          | ⬜️ | ⬜️ | ⬜️ |
 | 32 | `INLINE_ALL_SCHEMAS`                         | ⬜️ | ⬜️ | ⬜️ |
-| 33 | `PLAIN_DEFINITION_KEYS`                      | ⬜️ | ⬜️ | ⬜️ |
-| 34 | `ALLOF_CLEANUP_AT_THE_END`                   | ✅ | ✅ | ✅ |
-| 35 | `STRICT_TYPE_INFO`                           | ⬜️ | ⬜️ | ⬜️ |
\ No newline at end of file
+| 33 | `INLINE_NULLABLE_SCHEMAS`                    | ⬜️ | ⬜️ | ⬜️ |
+| 34 | `PLAIN_DEFINITION_KEYS`                      | ⬜️ | ⬜️ | ⬜️ |
+| 35 | `ALLOF_CLEANUP_AT_THE_END`                   | ✅ | ✅ | ✅ |
+| 36 | `STRICT_TYPE_INFO`                           | ⬜️ | ⬜️ | ⬜️ |


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 ed4464d..d84ca6a 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
@@ -110,7 +110,7 @@ public class SchemaGeneratorComplexTypesTest {
             return null;
         });
         Module alternativeDefinitionModule = configBuilder -> configBuilder.with(Option.DEFINITION_FOR_MAIN_SCHEMA,
-                Option.PLAIN_DEFINITION_KEYS, Option.FIELDS_DERIVED_FROM_ARGUMENTFREE_METHODS);
+                Option.PLAIN_DEFINITION_KEYS, Option.FIELDS_DERIVED_FROM_ARGUMENTFREE_METHODS, Option.INLINE_NULLABLE_SCHEMAS);
         Module typeInGeneralModule = configBuilder -> populateTypeConfigPart(
                 configBuilder.with(Option.FORBIDDEN_ADDITIONAL_PROPERTIES_BY_DEFAULT).forTypesInGeneral()
                         .withIdResolver(scope -> scope.getType().getTypeName().contains("$Test") ? "id-" + scope.getSimpleTypeDescription() : null)
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 05e5bf1..39f814a 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
@@ -185,6 +185,7 @@ public class SchemaGeneratorCustomDefinitionsTest {
                             .set(accessProperty, context.createDefinitionReference(generic))));
         };
         SchemaGeneratorConfigBuilder configBuilder = new SchemaGeneratorConfigBuilder(schemaVersion);
+        configBuilder.with(Option.INLINE_NULLABLE_SCHEMAS);
         configBuilder.forTypesInGeneral()
                 .withCustomDefinitionProvider(customDefinitionProvider);
         SchemaGenerator generator = new SchemaGenerator(configBuilder.build());
diff --git a/jsonschema-generator/src/test/resources/com/github/victools/jsonschema/generator/circular-custom-definition-DRAFT_2019_09.json b/jsonschema-generator/src/test/resources/com/github/victools/jsonschema/generator/circular-custom-definition-DRAFT_2019_09.json
index f803957..757d917 100644
--- a/jsonschema-generator/src/test/resources/com/github/victools/jsonschema/generator/circular-custom-definition-DRAFT_2019_09.json
+++ b/jsonschema-generator/src/test/resources/com/github/victools/jsonschema/generator/circular-custom-definition-DRAFT_2019_09.json
@@ -1,26 +1,15 @@
 {
     "$defs": {
-        "List(TestCircularClass1)-nullable": {
-            "type": ["object", "null"],
-            "properties": {
-                "get(0)": {
-                    "$ref": "#"
-                }
-            }
-        },
-        "List(TestCircularClass2)-nullable": {
-            "type": ["object", "null"],
-            "properties": {
-                "get(0)": {
-                    "$ref": "#/$defs/TestCircularClass2"
-                }
-            }
-        },
         "TestCircularClass2": {
             "type": "object",
             "properties": {
                 "list1": {
-                    "$ref": "#/$defs/List(TestCircularClass1)-nullable"
+                    "type": ["object", "null"],
+                    "properties": {
+                        "get(0)": {
+                            "$ref": "#"
+                        }
+                    }
                 }
             }
         }
@@ -28,7 +17,12 @@
     "type": "object",
     "properties": {
         "list2": {
-            "$ref": "#/$defs/List(TestCircularClass2)-nullable"
+            "type": ["object", "null"],
+            "properties": {
+                "get(0)": {
+                    "$ref": "#/$defs/TestCircularClass2"
+                }
+            }
         }
     }
 }
diff --git a/jsonschema-generator/src/test/resources/com/github/victools/jsonschema/generator/circular-custom-definition-DRAFT_2020_12.json b/jsonschema-generator/src/test/resources/com/github/victools/jsonschema/generator/circular-custom-definition-DRAFT_2020_12.json
index f803957..757d917 100644
--- a/jsonschema-generator/src/test/resources/com/github/victools/jsonschema/generator/circular-custom-definition-DRAFT_2020_12.json
+++ b/jsonschema-generator/src/test/resources/com/github/victools/jsonschema/generator/circular-custom-definition-DRAFT_2020_12.json
@@ -1,26 +1,15 @@
 {
     "$defs": {
-        "List(TestCircularClass1)-nullable": {
-            "type": ["object", "null"],
-            "properties": {
-                "get(0)": {
-                    "$ref": "#"
-                }
-            }
-        },
-        "List(TestCircularClass2)-nullable": {
-            "type": ["object", "null"],
-            "properties": {
-                "get(0)": {
-                    "$ref": "#/$defs/TestCircularClass2"
-                }
-            }
-        },
         "TestCircularClass2": {
             "type": "object",
             "properties": {
                 "list1": {
-                    "$ref": "#/$defs/List(TestCircularClass1)-nullable"
+                    "type": ["object", "null"],
+                    "properties": {
+                        "get(0)": {
+                            "$ref": "#"
+                        }
+                    }
                 }
             }
         }
@@ -28,7 +17,12 @@
     "type": "object",
     "properties": {
         "list2": {
-            "$ref": "#/$defs/List(TestCircularClass2)-nullable"
+            "type": ["object", "null"],
+            "properties": {
+                "get(0)": {
+                    "$ref": "#/$defs/TestCircularClass2"
+                }
+            }
         }
     }
 }
diff --git a/jsonschema-generator/src/test/resources/com/github/victools/jsonschema/generator/circular-custom-definition-DRAFT_6.json b/jsonschema-generator/src/test/resources/com/github/victools/jsonschema/generator/circular-custom-definition-DRAFT_6.json
index a76e9cd..d2cf65b 100644
--- a/jsonschema-generator/src/test/resources/com/github/victools/jsonschema/generator/circular-custom-definition-DRAFT_6.json
+++ b/jsonschema-generator/src/test/resources/com/github/victools/jsonschema/generator/circular-custom-definition-DRAFT_6.json
@@ -1,26 +1,15 @@
 {
     "definitions": {
-        "List(TestCircularClass1)-nullable": {
-            "type": ["object", "null"],
-            "properties": {
-                "get(0)": {
-                    "$ref": "#"
-                }
-            }
-        },
-        "List(TestCircularClass2)-nullable": {
-            "type": ["object", "null"],
-            "properties": {
-                "get(0)": {
-                    "$ref": "#/definitions/TestCircularClass2"
-                }
-            }
-        },
         "TestCircularClass2": {
             "type": "object",
             "properties": {
                 "list1": {
-                    "$ref": "#/definitions/List(TestCircularClass1)-nullable"
+                    "type": ["object", "null"],
+                    "properties": {
+                        "get(0)": {
+                            "$ref": "#"
+                        }
+                    }
                 }
             }
         }
@@ -28,7 +17,12 @@
     "type": "object",
     "properties": {
         "list2": {
-            "$ref": "#/definitions/List(TestCircularClass2)-nullable"
+            "type": ["object", "null"],
+            "properties": {
+                "get(0)": {
+                    "$ref": "#/definitions/TestCircularClass2"
+                }
+            }
         }
     }
 }
diff --git a/jsonschema-generator/src/test/resources/com/github/victools/jsonschema/generator/circular-custom-definition-DRAFT_7.json b/jsonschema-generator/src/test/resources/com/github/victools/jsonschema/generator/circular-custom-definition-DRAFT_7.json
index a76e9cd..d2cf65b 100644
--- a/jsonschema-generator/src/test/resources/com/github/victools/jsonschema/generator/circular-custom-definition-DRAFT_7.json
+++ b/jsonschema-generator/src/test/resources/com/github/victools/jsonschema/generator/circular-custom-definition-DRAFT_7.json
@@ -1,26 +1,15 @@
 {
     "definitions": {
-        "List(TestCircularClass1)-nullable": {
-            "type": ["object", "null"],
-            "properties": {
-                "get(0)": {
-                    "$ref": "#"
-                }
-            }
-        },
-        "List(TestCircularClass2)-nullable": {
-            "type": ["object", "null"],
-            "properties": {
-                "get(0)": {
-                    "$ref": "#/definitions/TestCircularClass2"
-                }
-            }
-        },
         "TestCircularClass2": {
             "type": "object",
             "properties": {
                 "list1": {
-                    "$ref": "#/definitions/List(TestCircularClass1)-nullable"
+                    "type": ["object", "null"],
+                    "properties": {
+                        "get(0)": {
+                            "$ref": "#"
+                        }
+                    }
                 }
             }
         }
@@ -28,7 +17,12 @@
     "type": "object",
     "properties": {
         "list2": {
-            "$ref": "#/definitions/List(TestCircularClass2)-nullable"
+            "type": ["object", "null"],
+            "properties": {
+                "get(0)": {
+                    "$ref": "#/definitions/TestCircularClass2"
+                }
+            }
         }
     }
 }
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 d0d1137..ab6b37c 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
@@ -23,13 +23,6 @@
                 }
             }
         },
-        "Optional_Integer_-nullable": {
-            "anyOf": [{
-                    "type": "null"
-                }, {
-                    "$ref": "#/definitions/Optional_Integer_"
-                }]
-        },
         "RoundingMode": {
             "type": "object",
             "properties": {
@@ -50,10 +43,22 @@
                     "enum": ["UP", "DOWN", "CEILING", "FLOOR", "HALF_UP", "HALF_DOWN", "HALF_EVEN", "UNNECESSARY"]
                 },
                 "valueOf(String)": {
-                    "$ref": "#/definitions/RoundingMode-nullable"
+                    "anyOf": [
+                        {
+                            "type": "null"
+                        }, {
+                            "$ref": "#/definitions/RoundingMode"
+                        }
+                    ]
                 },
                 "valueOf(int)": {
-                    "$ref": "#/definitions/RoundingMode-nullable"
+                    "anyOf": [
+                        {
+                            "type": "null"
+                        }, {
+                            "$ref": "#/definitions/RoundingMode"
+                        }
+                    ]
                 },
                 "values()": {
                     "type": ["array", "null"],
@@ -63,21 +68,6 @@
                 }
             }
         },
-        "RoundingMode-nullable": {
-            "anyOf": [{
-                    "type": "null"
-                }, {
-                    "$ref": "#/definitions/RoundingMode"
-                }]
-        },
-        "Supplier_Integer_-nullable": {
-            "type": ["object", "null"],
-            "properties": {
-                "get()": {
-                    "type": ["integer", "null"]
-                }
-            }
-        },
         "TestClass1": {
             "type": "object",
             "properties": {
@@ -120,13 +110,6 @@
                 }
             }
         },
-        "TestClass2_Long_-nullable": {
-            "anyOf": [{
-                    "type": "null"
-                }, {
-                    "$ref": "#/definitions/TestClass2_Long_"
-                }]
-        },
         "TestClass2_String_": {
             "type": "object",
             "properties": {
@@ -141,58 +124,100 @@
                 }
             }
         },
-        "TestClass2_String_-nullable": {
-            "anyOf": [{
-                    "type": "null"
-                }, {
-                    "$ref": "#/definitions/TestClass2_String_"
-                }]
-        },
-        "TestClass2_TestClass1..._-nullable": {
-            "type": ["object", "null"],
-            "properties": {
-                "genericArray": {
-                    "type": ["array", "null"],
-                    "items": {
-                        "type": "array",
-                        "items": {
-                            "$ref": "#/definitions/TestClass1"
-                        }
-                    }
-                },
-                "genericValue": {
-                    "type": ["array", "null"],
-                    "items": {
-                        "$ref": "#/definitions/TestClass1"
-                    }
-                }
-            }
-        },
-        "TestClass2_TestClass2_String__-nullable": {
-            "type": ["object", "null"],
-            "properties": {
-                "genericArray": {
-                    "type": ["array", "null"],
-                    "items": {
-                        "$ref": "#/definitions/TestClass2_String_"
-                    }
-                },
-                "genericValue": {
-                    "$ref": "#/definitions/TestClass2_String_-nullable"
-                }
-            }
-        },
         "TestClass3": {
             "type": "object",
             "properties": {
                 "class4": {
-                    "$ref": "#/definitions/TestClass4_Integer.String_-nullable"
+                    "type": ["object", "null"],
+                    "properties": {
+                        "DEFAULT_ROUNDING_MODE": {
+                            "allOf": [
+                                {
+                                    "$ref": "#/definitions/RoundingMode"
+                                }, {
+                                    "const": "HALF_UP"
+                                }
+                            ]
+                        },
+                        "class2OfClass2OfT": {
+                            "type": ["object", "null"],
+                            "properties": {
+                                "genericArray": {
+                                    "type": ["array", "null"],
+                                    "items": {
+                                        "$ref": "#/definitions/TestClass2_String_"
+                                    }
+                                },
+                                "genericValue": {
+                                    "anyOf": [
+                                        {
+                                            "type": "null"
+                                        }, {
+                                            "$ref": "#/definitions/TestClass2_String_"
+                                        }
+                                    ]
+                                }
+                            }
+                        },
+                        "listOfOptionalS": {
+                            "type": ["array", "null"],
+                            "items": {
+                                "$ref": "#/definitions/Optional_Integer_"
+                            }
+                        },
+                        "optionalS": {
+                            "anyOf": [
+                                {
+                                    "type": "null"
+                                }, {
+                                    "$ref": "#/definitions/Optional_Integer_"
+                                }
+                            ]
+                        },
+                        "setOfStringSupplier": {
+                            "type": ["array", "null"],
+                            "items": {
+                                "$ref": "#/definitions/LazyStringSupplier"
+                            }
+                        },
+                        "supplierS": {
+                            "type": ["object", "null"],
+                            "properties": {
+                                "get()": {
+                                    "type": ["integer", "null"]
+                                }
+                            }
+                        }
+                    }
                 },
                 "nestedClass1Array": {
-                    "$ref": "#/definitions/TestClass2_TestClass1..._-nullable"
+                    "type": ["object", "null"],
+                    "properties": {
+                        "genericArray": {
+                            "type": ["array", "null"],
+                            "items": {
+                                "type": "array",
+                                "items": {
+                                    "$ref": "#/definitions/TestClass1"
+                                }
+                            }
+                        },
+                        "genericValue": {
+                            "type": ["array", "null"],
+                            "items": {
+                                "$ref": "#/definitions/TestClass1"
+                            }
+                        }
+                    }
                 },
                 "nestedLong": {
-                    "$ref": "#/definitions/TestClass2_Long_-nullable"
+                    "anyOf": [
+                        {
+                            "type": "null"
+                        }, {
+                            "$ref": "#/definitions/TestClass2_Long_"
+                        }
+                    ]
                 },
                 "nestedLongList": {
                     "type": ["array", "null"],
@@ -201,39 +226,6 @@
                     }
                 }
             }
-        },
-        "TestClass4_Integer.String_-nullable": {
-            "type": ["object", "null"],
-            "properties": {
-                "DEFAULT_ROUNDING_MODE": {
-                    "allOf": [{
-                            "$ref": "#/definitions/RoundingMode"
-                        }, {
-                            "const": "HALF_UP"
-                        }]
-                },
-                "class2OfClass2OfT": {
-                    "$ref": "#/definitions/TestClass2_TestClass2_String__-nullable"
-                },
-                "listOfOptionalS": {
-                    "type": ["array", "null"],
-                    "items": {
-                        "$ref": "#/definitions/Optional_Integer_"
-                    }
-                },
-                "optionalS": {
-                    "$ref": "#/definitions/Optional_Integer_-nullable"
-                },
-                "setOfStringSupplier": {
-                    "type": ["array", "null"],
-                    "items": {
-                        "$ref": "#/definitions/LazyStringSupplier"
-                    }
-                },
-                "supplierS": {
-                    "$ref": "#/definitions/Supplier_Integer_-nullable"
-                }
-            }
         }
     }
 }
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 a4bd989..6a8783d 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
@@ -21,7 +21,13 @@
                     "type": ["string", "null"]
                 },
                 "valueOf(String)": {
-                    "$ref": "#/definitions/TestEnum-nullable"
+                    "anyOf": [
+                        {
+                            "type": "null"
+                        }, {
+                            "$ref": "#/definitions/TestEnum"
+                        }
+                    ]
                 },
                 "values()": {
                     "type": ["array", "null"],
@@ -30,13 +36,6 @@
                     }
                 }
             }
-        },
-        "TestEnum-nullable": {
-            "anyOf": [{
-                    "type": "null"
-                }, {
-                    "$ref": "#/definitions/TestEnum"
-                }]
         }
     }
 }