formatTypeMapping) {
this.formatTypeMapping = formatTypeMapping;
}
@@ -1338,4 +1361,14 @@ public boolean isIncludeGeneratedAnnotation() {
public boolean isUseJakartaValidation() {
return useJakartaValidation;
}
+
+ @Override
+ public boolean isGenerateDefinitions() {
+ return generateDefinitions;
+ }
+
+ @Override
+ public String getDefinitionsPath() {
+ return definitionsPath;
+ }
}
diff --git a/jsonschema2pojo-ant/src/site/Jsonschema2PojoTask.html b/jsonschema2pojo-ant/src/site/Jsonschema2PojoTask.html
index d2df628fc..b6b9492a5 100644
--- a/jsonschema2pojo-ant/src/site/Jsonschema2PojoTask.html
+++ b/jsonschema2pojo-ant/src/site/Jsonschema2PojoTask.html
@@ -632,6 +632,21 @@ Parameters
No (default false ) |
+
+ generateDefinitions |
+ Whether to generate POJO's from subschemas path defined by
+ definitionsPath configuration option
+ |
+ No (default false ) |
+
+
+ definitionsPath |
+ Defines path to subschemas that should be processed by jsonschema2pojo.
+ This property works in collaboration with the generateDefinitions configuration option and
+ will have no effect if latter is set to false
+ |
+ No (default /$defs ) |
+
Examples
diff --git a/jsonschema2pojo-cli/src/main/java/org/jsonschema2pojo/cli/Arguments.java b/jsonschema2pojo-cli/src/main/java/org/jsonschema2pojo/cli/Arguments.java
index 88ab72ec4..13aef38c3 100644
--- a/jsonschema2pojo-cli/src/main/java/org/jsonschema2pojo/cli/Arguments.java
+++ b/jsonschema2pojo-cli/src/main/java/org/jsonschema2pojo/cli/Arguments.java
@@ -251,6 +251,12 @@ public class Arguments implements GenerationConfig {
@Parameter(names = { "--useJakartaValidation" }, description = "Whether to use annotations from jakarta.validation package instead of javax.validation package when adding JSR-303/349 annotations to generated Java types")
private boolean useJakartaValidation = false;
+ @Parameter(names = { "--generateDefinitions" }, description = "Whether to generate POJO's from subschemas path defined by '--definitionsPath' configuration option")
+ private boolean generateDefinitions = false;
+
+ @Parameter(names = { "--definitionsPath" }, description = "Defines path to subschemas that should be processed by jsonschema2pojo when '--generateDefinitions' is enabled")
+ private String definitionsPath = "/$defs";
+
@Parameter(names = { "-v", "--version"}, description = "Print version information", help = true)
private boolean printVersion = false;
@@ -624,4 +630,14 @@ public boolean isIncludeGeneratedAnnotation() {
public boolean isUseJakartaValidation() {
return useJakartaValidation;
}
+
+ @Override
+ public boolean isGenerateDefinitions() {
+ return generateDefinitions;
+ }
+
+ @Override
+ public String getDefinitionsPath() {
+ return definitionsPath;
+ }
}
diff --git a/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/GenerationConfig.java b/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/GenerationConfig.java
index 963266c12..de185a54f 100644
--- a/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/GenerationConfig.java
+++ b/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/GenerationConfig.java
@@ -635,4 +635,24 @@ default boolean isUseInnerClassBuilders() {
*/
boolean isUseJakartaValidation();
+ /**
+ * Gets the 'generateDefinitions' configuration option.
+ *
+ * @return whether to generate POJO's from subschemas path defined by {@link #getDefinitionsPath} configuration option
+ */
+ default boolean isGenerateDefinitions() {
+ return false;
+ }
+
+ /**
+ * Gets the 'definitionsPath' configuration option.
+ * This property works in collaboration with the {@link #isGenerateDefinitions()} configuration option.
+ * If the {@link #isGenerateDefinitions()} returns {@code false}, then this configuration option will not affect anything.
+ *
+ * @return path to subschemas that should be processed by jsonschema2pojo
+ */
+ default String getDefinitionsPath() {
+ return "/$defs";
+ }
+
}
diff --git a/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/rules/SchemaRule.java b/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/rules/SchemaRule.java
index 23548c45a..3d11df01e 100644
--- a/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/rules/SchemaRule.java
+++ b/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/rules/SchemaRule.java
@@ -20,7 +20,10 @@
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
+import java.util.Iterator;
+import java.util.Map;
+import org.apache.commons.lang3.StringUtils;
import org.jsonschema2pojo.Jsonschema2Pojo;
import org.jsonschema2pojo.Schema;
import org.jsonschema2pojo.exception.GenerationException;
@@ -84,9 +87,39 @@ public JType apply(String nodeName, JsonNode schemaNode, JsonNode parent, JClass
}
schema.setJavaTypeIfEmpty(javaType);
+ processDefinitions(schemaNode, generatableType, schema);
+
return javaType;
}
+ private void processDefinitions(JsonNode schemaNode, JClassContainer generatableType, Schema parent) {
+ if (!ruleFactory.getGenerationConfig().isGenerateDefinitions()) {
+ return;
+ }
+
+ final String definitionsNodePath = getDefinitionsNodePath(schemaNode);
+ if (StringUtils.isNotBlank(definitionsNodePath)) {
+ final Iterator> definitions = schemaNode.at(definitionsNodePath).fields();
+ while (definitions.hasNext()) {
+ final Map.Entry definition = definitions.next();
+
+ final Schema schema = ruleFactory.getSchemaStore().create(
+ parent,
+ "#" + definitionsNodePath + "/" + definition.getKey(),
+ ruleFactory.getGenerationConfig().getRefFragmentPathDelimiters());
+ if (schema.isGenerated()) {
+ continue;
+ }
+
+ apply(definition.getKey(), definition.getValue(), schemaNode, generatableType, schema);
+ }
+ }
+ }
+
+ protected String getDefinitionsNodePath(JsonNode schemaNode) {
+ return ruleFactory.getGenerationConfig().getDefinitionsPath();
+ }
+
private String nameFromRef(String ref) {
if ("#".equals(ref)) {
diff --git a/jsonschema2pojo-gradle-plugin/README.md b/jsonschema2pojo-gradle-plugin/README.md
index 29ded90c4..b4f965c00 100644
--- a/jsonschema2pojo-gradle-plugin/README.md
+++ b/jsonschema2pojo-gradle-plugin/README.md
@@ -315,6 +315,12 @@ jsonSchema2Pojo {
// Whether to use annotations from jakarta.validation package instead of javax.validation package
// when adding JSR-303 annotations to generated Java types
useJakartaValidation = false
+
+ // Whether to generate POJO's from subschemas path defined by 'definitionsPath' configuration option
+ generateDefinitions = false
+
+ // Defines path to subschemas that should be processed by jsonschema2pojo when 'generateDefinitions' is enabled
+ definitionsPath = '/$defs'
}
```
diff --git a/jsonschema2pojo-gradle-plugin/src/main/groovy/org/jsonschema2pojo/gradle/JsonSchemaExtension.groovy b/jsonschema2pojo-gradle-plugin/src/main/groovy/org/jsonschema2pojo/gradle/JsonSchemaExtension.groovy
index eb06e24b8..846e064c7 100644
--- a/jsonschema2pojo-gradle-plugin/src/main/groovy/org/jsonschema2pojo/gradle/JsonSchemaExtension.groovy
+++ b/jsonschema2pojo-gradle-plugin/src/main/groovy/org/jsonschema2pojo/gradle/JsonSchemaExtension.groovy
@@ -98,6 +98,8 @@ public class JsonSchemaExtension implements GenerationConfig {
Map formatTypeMapping
boolean includeGeneratedAnnotation
boolean useJakartaValidation
+ boolean generateDefinitions
+ String definitionsPath
public JsonSchemaExtension() {
// See DefaultGenerationConfig
@@ -158,6 +160,8 @@ public class JsonSchemaExtension implements GenerationConfig {
formatTypeMapping = Collections.emptyMap()
includeGeneratedAnnotation = true
useJakartaValidation = false
+ generateDefinitions = false
+ definitionsPath = '/$defs'
}
@Override
@@ -292,6 +296,8 @@ public class JsonSchemaExtension implements GenerationConfig {
|includeConstructorPropertiesAnnotation = ${includeConstructorPropertiesAnnotation}
|includeGeneratedAnnotation = ${includeGeneratedAnnotation}
|useJakartaValidation = ${useJakartaValidation}
+ |generateDefinitions = ${generateDefinitions}
+ |definitionsPath = ${definitionsPath}
""".stripMargin()
}
diff --git a/jsonschema2pojo-integration-tests/src/test/java/org/jsonschema2pojo/integration/SchemaIT.java b/jsonschema2pojo-integration-tests/src/test/java/org/jsonschema2pojo/integration/SchemaIT.java
new file mode 100644
index 000000000..6ad1eb836
--- /dev/null
+++ b/jsonschema2pojo-integration-tests/src/test/java/org/jsonschema2pojo/integration/SchemaIT.java
@@ -0,0 +1,105 @@
+/**
+ * Copyright © 2010-2020 Nokia
+ *
+ * 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 org.jsonschema2pojo.integration;
+
+import org.jsonschema2pojo.integration.util.Jsonschema2PojoRule;
+import org.junit.Rule;
+import org.junit.Test;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.empty;
+import static org.hamcrest.Matchers.emptyArray;
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.not;
+import static org.hamcrest.Matchers.notNullValue;
+import static org.jsonschema2pojo.integration.util.CodeGenerationHelper.config;
+import static org.junit.Assert.assertThrows;
+
+public class SchemaIT {
+
+ @Rule
+ public Jsonschema2PojoRule schemaRule = new Jsonschema2PojoRule();
+
+ @Test
+ public void subschemasDefinedInDefsAreGenerated_when_generateDefinitions_isTrue() throws ReflectiveOperationException {
+ ClassLoader resultsClassLoader = schemaRule.generateAndCompile(
+ "/schema/definitions/schemaDefsStorage.json",
+ "com.example",
+ config("generateDefinitions", true));
+
+ Class> schemaDefsStorageType = resultsClassLoader.loadClass("com.example.SchemaDefsStorage");
+ Class> referencedDefinitionsStorageType = resultsClassLoader.loadClass("com.example.ReferencedDefsStorage");
+
+ assertThat(schemaDefsStorageType.getDeclaredFields(), is(emptyArray()));
+ assertThat(referencedDefinitionsStorageType.getDeclaredField("name"), is(notNullValue()));
+ assertInlinePropertyTypes(resultsClassLoader);
+ }
+
+ @Test
+ public void subschemasNotProcessed_when_schemaDoesNotContainOverridenDefinitionsPath() throws ReflectiveOperationException {
+ ClassLoader resultsClassLoader = schemaRule.generateAndCompile(
+ "/schema/definitions/schemaDefsStorage.json",
+ "com.example",
+ config("generateDefinitions", true, "definitionsPath", "/components/schemas"));
+
+ Class> schemaDefsStorageType = resultsClassLoader.loadClass("com.example.SchemaDefsStorage");
+ assertThat(schemaDefsStorageType.getDeclaredFields(), is(emptyArray()));
+ assertThrows(ClassNotFoundException.class, () -> resultsClassLoader.loadClass("com.example.ReferencedDefsStorage"));
+ }
+
+ @Test
+ public void definitionsFromCustomPathProcessed_when_definitionsNodePathIsOverridden() throws ReflectiveOperationException {
+ ClassLoader resultsClassLoader = schemaRule.generateAndCompile(
+ "/schema/definitions/schemaDefinitionsStorageWithCustomSection.json",
+ "com.example",
+ config("generateDefinitions", true, "definitionsPath", "/components/schemas"));
+
+ Class> statusTypeRaw = resultsClassLoader.loadClass("com.example.Status");
+ assertThat(statusTypeRaw.isEnum(), is(true));
+ @SuppressWarnings("unchecked")
+ Class> statusType = (Class>) statusTypeRaw;
+ assertThat(statusType.getEnumConstants()[0].name(), is("ACTIVE"));
+ assertThat(statusType.getEnumConstants()[1].name(), is("INACTIVE"));
+
+ Class> userType = resultsClassLoader.loadClass("com.example.User");
+ assertThat(userType.getDeclaredField("id"), is(notNullValue()));
+ assertThat(userType.getDeclaredField("id").getType(), is(equalTo(Integer.class)));
+ assertThat(userType.getDeclaredField("name"), is(notNullValue()));
+ assertThat(userType.getDeclaredField("name").getType(), is(equalTo(String.class)));
+
+ assertThrows(ClassNotFoundException.class, () -> resultsClassLoader.loadClass("com.example.Unexpected"));
+ }
+
+ private void assertInlinePropertyTypes(ClassLoader resultsClassLoader) throws ReflectiveOperationException {
+ Class> referencedInlineType = resultsClassLoader.loadClass("com.example.Inline");
+ Class> inlineType = resultsClassLoader.loadClass("com.example.Inline__1");
+
+ assertThat(inlineType, is(not(equalTo(referencedInlineType))));
+
+ assertThat(referencedInlineType.getDeclaredField("inlineProperty"), is(notNullValue()));
+ assertThat(referencedInlineType.getDeclaredField("inlineProperty").getType(), is(equalTo(Boolean.class)));
+
+ assertThat(inlineType.getDeclaredField("inlineProperty"), is(notNullValue()));
+ assertThat(inlineType.getDeclaredField("inlineProperty").getType(), is(equalTo(String.class)));
+
+ Class> selfReferenceType = resultsClassLoader.loadClass("com.example.SelfReference");
+ assertThat(selfReferenceType.getDeclaredField("selfRefProperty"), is(notNullValue()));
+ assertThat(selfReferenceType.getDeclaredField("selfRefProperty").getType(), is(equalTo(inlineType)));
+ }
+
+}
diff --git a/jsonschema2pojo-integration-tests/src/test/resources/schema/definitions/referencedDefsStorage.json b/jsonschema2pojo-integration-tests/src/test/resources/schema/definitions/referencedDefsStorage.json
new file mode 100644
index 000000000..2c0bd7305
--- /dev/null
+++ b/jsonschema2pojo-integration-tests/src/test/resources/schema/definitions/referencedDefsStorage.json
@@ -0,0 +1,20 @@
+{
+ "type": "object",
+ "additionalProperties": false,
+ "properties": {
+ "name": {
+ "type": "string"
+ }
+ },
+ "$defs": {
+ "inline": {
+ "type": "object",
+ "additionalProperties": false,
+ "properties": {
+ "inlineProperty": {
+ "type": "boolean"
+ }
+ }
+ }
+ }
+}
diff --git a/jsonschema2pojo-integration-tests/src/test/resources/schema/definitions/schemaDefinitionsStorageWithCustomSection.json b/jsonschema2pojo-integration-tests/src/test/resources/schema/definitions/schemaDefinitionsStorageWithCustomSection.json
new file mode 100644
index 000000000..9e7aadf79
--- /dev/null
+++ b/jsonschema2pojo-integration-tests/src/test/resources/schema/definitions/schemaDefinitionsStorageWithCustomSection.json
@@ -0,0 +1,29 @@
+{
+ "definitions": {
+ "unexpected": {
+ "type": "object"
+ }
+ },
+ "components": {
+ "schemas": {
+ "user": {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "integer"
+ },
+ "name": {
+ "type": "string"
+ }
+ }
+ },
+ "status": {
+ "type": "string",
+ "enum": [
+ "active",
+ "inactive"
+ ]
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/jsonschema2pojo-integration-tests/src/test/resources/schema/definitions/schemaDefsStorage.json b/jsonschema2pojo-integration-tests/src/test/resources/schema/definitions/schemaDefsStorage.json
new file mode 100644
index 000000000..d47d2c74e
--- /dev/null
+++ b/jsonschema2pojo-integration-tests/src/test/resources/schema/definitions/schemaDefsStorage.json
@@ -0,0 +1,24 @@
+{
+ "type": "object",
+ "additionalProperties": false,
+ "$defs": {
+ "reference": {
+ "$ref": "referencedDefsStorage.json"
+ },
+ "self-reference": {
+ "properties": {
+ "selfRefProperty": {
+ "$ref": "#/$defs/inline"
+ }
+ }
+ },
+ "inline": {
+ "type": "object",
+ "properties": {
+ "inlineProperty": {
+ "type": "string"
+ }
+ }
+ }
+ }
+}
diff --git a/jsonschema2pojo-maven-plugin/src/main/java/org/jsonschema2pojo/maven/Jsonschema2PojoMojo.java b/jsonschema2pojo-maven-plugin/src/main/java/org/jsonschema2pojo/maven/Jsonschema2PojoMojo.java
index 5c3d4d279..69def2279 100644
--- a/jsonschema2pojo-maven-plugin/src/main/java/org/jsonschema2pojo/maven/Jsonschema2PojoMojo.java
+++ b/jsonschema2pojo-maven-plugin/src/main/java/org/jsonschema2pojo/maven/Jsonschema2PojoMojo.java
@@ -778,6 +778,20 @@ public class Jsonschema2PojoMojo extends AbstractMojo implements GenerationConfi
@Parameter(property = "jsonschema2pojo.useJakartaValidation", defaultValue = "false")
private boolean useJakartaValidation = false;
+ /**
+ * Whether to generate POJO's from subschemas path defined by {@link #getDefinitionsPath} configuration option
+ */
+ @Parameter(property = "jsonschema2pojo.generateDefinitions", defaultValue = "false")
+ private boolean generateDefinitions;
+
+ /**
+ * Defines path to subschemas that should be processed by jsonschema2pojo.
+ * This property works in collaboration with the {@link #isGenerateDefinitions()} configuration option.
+ * If the {@link #isGenerateDefinitions()} returns {@code false}, then this configuration option will not affect anything.
+ */
+ @Parameter(property = "jsonschema2pojo.definitionsPath", defaultValue = "/$defs")
+ private String definitionsPath = "/$defs";
+
/**
* Executes the plugin, to read the given source and behavioural properties
* and generate POJOs. The current implementation acts as a wrapper around
@@ -1275,4 +1289,15 @@ public boolean isIncludeGeneratedAnnotation() {
public boolean isUseJakartaValidation() {
return useJakartaValidation;
}
+
+ @Override
+ public boolean isGenerateDefinitions() {
+ return generateDefinitions;
+ }
+
+ @Override
+ public String getDefinitionsPath() {
+ return definitionsPath;
+ }
+
}
From ea456c6ec9d04df705f1664a20e15f82a0955fa2 Mon Sep 17 00:00:00 2001
From: unkish <3533269+unkish@users.noreply.github.com>
Date: Sat, 4 Nov 2023 20:13:30 +0200
Subject: [PATCH 2/2] Fix SchemaRuleTest::enumAsRootIsGeneratedCorrectly test
---
.../src/test/java/org/jsonschema2pojo/rules/SchemaRuleTest.java | 2 ++
1 file changed, 2 insertions(+)
diff --git a/jsonschema2pojo-core/src/test/java/org/jsonschema2pojo/rules/SchemaRuleTest.java b/jsonschema2pojo-core/src/test/java/org/jsonschema2pojo/rules/SchemaRuleTest.java
index 7d6b9a636..f3f685b31 100644
--- a/jsonschema2pojo-core/src/test/java/org/jsonschema2pojo/rules/SchemaRuleTest.java
+++ b/jsonschema2pojo-core/src/test/java/org/jsonschema2pojo/rules/SchemaRuleTest.java
@@ -23,6 +23,7 @@
import java.net.URI;
import java.net.URISyntaxException;
+import org.jsonschema2pojo.DefaultGenerationConfig;
import org.jsonschema2pojo.GenerationConfig;
import org.jsonschema2pojo.Schema;
import org.jsonschema2pojo.SchemaStore;
@@ -92,6 +93,7 @@ public void enumAsRootIsGeneratedCorrectly() throws JClassAlreadyExistsException
EnumRule enumRule = mock(EnumRule.class);
when(mockRuleFactory.getEnumRule()).thenReturn(enumRule);
+ when(mockRuleFactory.getGenerationConfig()).thenReturn(new DefaultGenerationConfig());
when(enumRule.apply(NODE_NAME, enumNode, null, jclass, schema)).thenReturn(jclass);