From 9c42ab1f66608c02a466ed280b4859c8f3f3adda Mon Sep 17 00:00:00 2001 From: unkish <3533269+unkish@users.noreply.github.com> Date: Sat, 14 Feb 2026 18:16:37 +0200 Subject: [PATCH 1/5] Apply '@Valid' to properties with "existingJavaType" --- .../org/jsonschema2pojo/rules/ValidRule.java | 33 ++++++++++--- .../com/example/ClassWithSizeAnnotation.java | 35 ++++++++++++++ .../config/IncludeJsr303AnnotationsIT.java | 37 ++++++++++++++- .../jsr303/existingJavaTypeProperties.json | 46 +++++++++++++++++++ .../jsr303/minAndMaxItemsExistingType.json | 2 +- 5 files changed, 144 insertions(+), 9 deletions(-) create mode 100644 jsonschema2pojo-integration-tests/src/test/java/com/example/ClassWithSizeAnnotation.java create mode 100644 jsonschema2pojo-integration-tests/src/test/resources/schema/jsr303/existingJavaTypeProperties.json diff --git a/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/rules/ValidRule.java b/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/rules/ValidRule.java index be09f8ad0..64bd4f566 100644 --- a/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/rules/ValidRule.java +++ b/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/rules/ValidRule.java @@ -46,15 +46,34 @@ public ValidRule(RuleFactory ruleFactory) { @Override public JType apply(String nodeName, JsonNode node, JsonNode parent, JType type, Schema currentSchema) { + if (ruleFactory.getGenerationConfig().isIncludeJsr303Annotations() && type instanceof JClass jclass) { + if (!isContainer(jclass) && !isScalar(jclass)) { + return JAnnotatedClass.of(jclass).annotated(getValidClass()); + } + if (null != node && node.has("existingJavaType")) { + return applyToExistingJavaType(jclass); + } + } + return type; + } - if (ruleFactory.getGenerationConfig().isIncludeJsr303Annotations() - && type instanceof JClass jclass - && !isContainer(jclass) - && !isScalar(jclass)) { - return JAnnotatedClass.of(jclass).annotated(getValidClass()); - } else { - return type; + private JClass applyToExistingJavaType(JClass jClass) { + if (jClass.isReference() && isContainer(jClass.erasure())) { + final var typeParameters = jClass.getTypeParameters(); + if ((jClass.owner().ref(Iterable.class).isAssignableFrom(jClass.erasure()) + || jClass.owner().ref(Optional.class).isAssignableFrom(jClass.erasure())) + && typeParameters.size() == 1 + && !isScalar(typeParameters.get(0))) { + return jClass.erasure().narrow(applyToExistingJavaType(typeParameters.get(0))); + } else if (jClass.owner().ref(Map.class).isAssignableFrom(jClass.erasure()) + && typeParameters.size() == 2 && !isScalar(typeParameters.get(1))) { + return jClass.erasure().narrow(typeParameters.get(0), applyToExistingJavaType(typeParameters.get(1))); + } + } + if (!isContainer(jClass) && !isScalar(jClass)) { + return JAnnotatedClass.of(jClass).annotated(getValidClass()); } + return jClass; } private boolean isContainer(JClass jclass) { diff --git a/jsonschema2pojo-integration-tests/src/test/java/com/example/ClassWithSizeAnnotation.java b/jsonschema2pojo-integration-tests/src/test/java/com/example/ClassWithSizeAnnotation.java new file mode 100644 index 000000000..1e746aaee --- /dev/null +++ b/jsonschema2pojo-integration-tests/src/test/java/com/example/ClassWithSizeAnnotation.java @@ -0,0 +1,35 @@ +/** + * 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 com.example; + +import jakarta.validation.constraints.Size; + +public class ClassWithSizeAnnotation { + + @Size(max = 2) + @javax.validation.constraints.Size(max = 2) + private final String text; + + public ClassWithSizeAnnotation() { + text = "should fail"; + } + + public ClassWithSizeAnnotation(String text) { + this.text = text; + } + +} diff --git a/jsonschema2pojo-integration-tests/src/test/java/org/jsonschema2pojo/integration/config/IncludeJsr303AnnotationsIT.java b/jsonschema2pojo-integration-tests/src/test/java/org/jsonschema2pojo/integration/config/IncludeJsr303AnnotationsIT.java index 984df4981..6e8434ea0 100644 --- a/jsonschema2pojo-integration-tests/src/test/java/org/jsonschema2pojo/integration/config/IncludeJsr303AnnotationsIT.java +++ b/jsonschema2pojo-integration-tests/src/test/java/org/jsonschema2pojo/integration/config/IncludeJsr303AnnotationsIT.java @@ -38,6 +38,8 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Map; +import java.util.Optional; import java.util.Set; import java.util.UUID; import org.apache.bval.jsr.ApacheValidationProvider; @@ -47,6 +49,8 @@ import org.junit.jupiter.params.ParameterizedClass; import org.junit.jupiter.params.provider.ValueSource; +import com.example.ClassWithSizeAnnotation; + @SuppressWarnings("rawtypes") @ParameterizedClass @ValueSource(booleans = { true, false }) @@ -631,7 +635,7 @@ public void jsr303ValidAnnotationOnAdditionalPropertiesWithItemTypeArray() throw } @Test - public void jsr303ValidAnnotationsOnExistingJavaType() throws Exception { + public void jsr303ValidAnnotationsOnExistingJavaTypeWithScalar() throws Exception { ClassLoader resultsClassLoader = schemaRule.generateAndCompile( "/schema/jsr303/validExistingJavaType.json", "com.example", config("includeJsr303Annotations", true, "useJakartaValidation", useJakartaValidation)); @@ -700,6 +704,37 @@ public void jsr303ValidAnnotationNotAppliedToScalarTypes() throws Exception { source, not(containsString("@Valid"))); } + @Test + void jsr303AnnotationsValidatedForExistingJavaType() throws ReflectiveOperationException { + ClassLoader classLoader = schemaRule.generateAndCompile( + "/schema/jsr303/existingJavaTypeProperties.json", + "com.example", + config("includeJsr303Annotations", true, "useJakartaValidation", useJakartaValidation)); + + Class clazz = classLoader.loadClass("com.example.ExistingJavaTypeProperties"); + Object instance = clazz.getDeclaredConstructor().newInstance(); + + assertNumberOfConstraintViolationsOn(instance, is(0)); + + clazz.getDeclaredMethod("setMapOfStringOptionalListOfExistingClasses", Map.class) + .invoke(instance, Map.of("a", Optional.of(List.of(new ClassWithSizeAnnotation())))); + assertNumberOfConstraintViolationsOn(instance, is(1)); + + clazz.getDeclaredMethod("setListOfOptionalStringExistingClassMaps", List.class) + .invoke(instance, List.of(Optional.of(Map.of("a", new ClassWithSizeAnnotation())))); + assertNumberOfConstraintViolationsOn(instance, is(2)); + + clazz.getDeclaredMethod("setListOfOptionalStringObjectMaps", List.class) + .invoke(instance, List.of(Optional.of(Map.of("a", new ClassWithSizeAnnotation())))); + assertNumberOfConstraintViolationsOn(instance, is(3)); + + clazz.getDeclaredMethod("setOptionalOfExistingClass", Optional.class).invoke(instance, Optional.of(new ClassWithSizeAnnotation())); + assertNumberOfConstraintViolationsOn(instance, is(4)); + + clazz.getDeclaredMethod("setMapOfStringExistingJavaClasses", Map.class).invoke(instance, Map.of("a", new ClassWithSizeAnnotation())); + assertNumberOfConstraintViolationsOn(instance, is(5)); + } + private void assertNumberOfConstraintViolationsOn(Object instance, Matcher matcher) { final Set violationsForValidInstance = useJakartaValidation ? validator.validate(instance) : javaxValidator.validate(instance); final String validatorName = useJakartaValidation ? "jakarta/hibernate validator" : "javax/bval validator"; diff --git a/jsonschema2pojo-integration-tests/src/test/resources/schema/jsr303/existingJavaTypeProperties.json b/jsonschema2pojo-integration-tests/src/test/resources/schema/jsr303/existingJavaTypeProperties.json new file mode 100644 index 000000000..0692cc990 --- /dev/null +++ b/jsonschema2pojo-integration-tests/src/test/resources/schema/jsr303/existingJavaTypeProperties.json @@ -0,0 +1,46 @@ +{ + "type": "object", + "additionalProperties": false, + "properties": { + "mapOfStringOptionalListOfExistingClasses": { + "type": "object", + "existingJavaType": "java.util.Map>>" + }, + "listOfOptionalStringExistingClassMaps": { + "type": "object", + "existingJavaType": "java.util.List>>" + }, + "listOfOptionalStringObjectMaps": { + "type": "object", + "existingJavaType": "java.util.List>>" + }, + "listOfOptionalStringStringMaps": { + "type": "object", + "existingJavaType": "java.util.List>>" + }, + "optionalOfExistingClass": { + "type": "object", + "existingJavaType": "java.util.Optional" + }, + "mapOfStringExistingJavaClasses": { + "type": "object", + "existingJavaType": "java.util.Map" + }, + "listOfObjects": { + "type": "object", + "existingJavaType": "java.util.List" + }, + "mapOfStringObject": { + "type": "object", + "existingJavaType": "java.util.Map" + }, + "rawOptional": { + "type": "object", + "existingJavaType": "java.util.Optional" + }, + "rawMap": { + "type": "object", + "existingJavaType": "java.util.Map" + } + } +} diff --git a/jsonschema2pojo-integration-tests/src/test/resources/schema/jsr303/minAndMaxItemsExistingType.json b/jsonschema2pojo-integration-tests/src/test/resources/schema/jsr303/minAndMaxItemsExistingType.json index 835b42ff9..fca223283 100644 --- a/jsonschema2pojo-integration-tests/src/test/resources/schema/jsr303/minAndMaxItemsExistingType.json +++ b/jsonschema2pojo-integration-tests/src/test/resources/schema/jsr303/minAndMaxItemsExistingType.json @@ -5,7 +5,7 @@ "type" : "array", "minItems" : 2, "maxItems" : 4, - "existingJavaType" : "java.util.LinkedList" + "existingJavaType" : "java.util.LinkedList" } } } From 02f8ebc0e0adbef4bacefd569156131e953a6477 Mon Sep 17 00:00:00 2001 From: unkish <3533269+unkish@users.noreply.github.com> Date: Sun, 15 Feb 2026 10:30:46 +0200 Subject: [PATCH 2/5] Added more test cases --- .../config/IncludeJsr303AnnotationsIT.java | 53 ++++++++++++++----- .../jsr303/existingJavaTypeProperties.json | 10 ++++ .../jsr303/minAndMaxItemsExistingType.json | 2 +- 3 files changed, 51 insertions(+), 14 deletions(-) diff --git a/jsonschema2pojo-integration-tests/src/test/java/org/jsonschema2pojo/integration/config/IncludeJsr303AnnotationsIT.java b/jsonschema2pojo-integration-tests/src/test/java/org/jsonschema2pojo/integration/config/IncludeJsr303AnnotationsIT.java index 6e8434ea0..ed3c1af85 100644 --- a/jsonschema2pojo-integration-tests/src/test/java/org/jsonschema2pojo/integration/config/IncludeJsr303AnnotationsIT.java +++ b/jsonschema2pojo-integration-tests/src/test/java/org/jsonschema2pojo/integration/config/IncludeJsr303AnnotationsIT.java @@ -705,7 +705,7 @@ public void jsr303ValidAnnotationNotAppliedToScalarTypes() throws Exception { } @Test - void jsr303AnnotationsValidatedForExistingJavaType() throws ReflectiveOperationException { + public void jsr303AnnotationsValidatedForExistingJavaType() throws ReflectiveOperationException { ClassLoader classLoader = schemaRule.generateAndCompile( "/schema/jsr303/existingJavaTypeProperties.json", "com.example", @@ -716,23 +716,50 @@ void jsr303AnnotationsValidatedForExistingJavaType() throws ReflectiveOperationE assertNumberOfConstraintViolationsOn(instance, is(0)); - clazz.getDeclaredMethod("setMapOfStringOptionalListOfExistingClasses", Map.class) - .invoke(instance, Map.of("a", Optional.of(List.of(new ClassWithSizeAnnotation())))); + instance = createInstanceWithPropertyValue( + clazz, + "mapOfStringOptionalListOfExistingClasses", + Map.of("a", Optional.of(List.of(new ClassWithSizeAnnotation())))); assertNumberOfConstraintViolationsOn(instance, is(1)); - clazz.getDeclaredMethod("setListOfOptionalStringExistingClassMaps", List.class) - .invoke(instance, List.of(Optional.of(Map.of("a", new ClassWithSizeAnnotation())))); - assertNumberOfConstraintViolationsOn(instance, is(2)); + instance = createInstanceWithPropertyValue( + clazz, + "listOfOptionalStringExistingClassMaps", + List.of(Optional.of(Map.of("a", new ClassWithSizeAnnotation())))); + assertNumberOfConstraintViolationsOn(instance, is(1)); + + instance = createInstanceWithPropertyValue( + clazz, + "listOfOptionalStringObjectMaps", + List.of(Optional.of(Map.of("a", new ClassWithSizeAnnotation())))); + assertNumberOfConstraintViolationsOn(instance, is(1)); + + instance = createInstanceWithPropertyValue(clazz, "optionalOfExistingClass", Optional.of(new ClassWithSizeAnnotation())); + assertNumberOfConstraintViolationsOn(instance, is(1)); + + instance = createInstanceWithPropertyValue(clazz, "mapOfStringExistingJavaClasses", Map.of("a", new ClassWithSizeAnnotation())); + assertNumberOfConstraintViolationsOn(instance, is(1)); + + instance = createInstanceWithPropertyValue(clazz, "listOfObjects", List.of(new ClassWithSizeAnnotation())); + assertNumberOfConstraintViolationsOn(instance, is(1)); + + instance = createInstanceWithPropertyValue(clazz, "mapOfStringObject", Map.of("a", new ClassWithSizeAnnotation())); + assertNumberOfConstraintViolationsOn(instance, is(1)); + + instance = createInstanceWithPropertyValue(clazz, "primitiveLong", 1L); + assertNumberOfConstraintViolationsOn(instance, is(1)); - clazz.getDeclaredMethod("setListOfOptionalStringObjectMaps", List.class) - .invoke(instance, List.of(Optional.of(Map.of("a", new ClassWithSizeAnnotation())))); - assertNumberOfConstraintViolationsOn(instance, is(3)); + instance = createInstanceWithPropertyValue(clazz, "rawOptional", Optional.of(new ClassWithSizeAnnotation())); + assertNumberOfConstraintViolationsOn(instance, is(0)); - clazz.getDeclaredMethod("setOptionalOfExistingClass", Optional.class).invoke(instance, Optional.of(new ClassWithSizeAnnotation())); - assertNumberOfConstraintViolationsOn(instance, is(4)); + instance = createInstanceWithPropertyValue(clazz, "rawMap", Map.of("a", new ClassWithSizeAnnotation())); + assertNumberOfConstraintViolationsOn(instance, is(0)); - clazz.getDeclaredMethod("setMapOfStringExistingJavaClasses", Map.class).invoke(instance, Map.of("a", new ClassWithSizeAnnotation())); - assertNumberOfConstraintViolationsOn(instance, is(5)); + instance = createInstanceWithPropertyValue( + clazz, + "existingTypeAsArrayItem", + List.of(Map.of("a", new ClassWithSizeAnnotation()))); + assertNumberOfConstraintViolationsOn(instance, is(1)); } private void assertNumberOfConstraintViolationsOn(Object instance, Matcher matcher) { diff --git a/jsonschema2pojo-integration-tests/src/test/resources/schema/jsr303/existingJavaTypeProperties.json b/jsonschema2pojo-integration-tests/src/test/resources/schema/jsr303/existingJavaTypeProperties.json index 0692cc990..781caebf9 100644 --- a/jsonschema2pojo-integration-tests/src/test/resources/schema/jsr303/existingJavaTypeProperties.json +++ b/jsonschema2pojo-integration-tests/src/test/resources/schema/jsr303/existingJavaTypeProperties.json @@ -34,6 +34,10 @@ "type": "object", "existingJavaType": "java.util.Map" }, + "primitiveLong": { + "maximum": 0, + "existingJavaType": "long" + }, "rawOptional": { "type": "object", "existingJavaType": "java.util.Optional" @@ -41,6 +45,12 @@ "rawMap": { "type": "object", "existingJavaType": "java.util.Map" + }, + "existingTypeAsArrayItem": { + "type": "array", + "items": { + "existingJavaType": "java.util.Map" + } } } } diff --git a/jsonschema2pojo-integration-tests/src/test/resources/schema/jsr303/minAndMaxItemsExistingType.json b/jsonschema2pojo-integration-tests/src/test/resources/schema/jsr303/minAndMaxItemsExistingType.json index fca223283..835b42ff9 100644 --- a/jsonschema2pojo-integration-tests/src/test/resources/schema/jsr303/minAndMaxItemsExistingType.json +++ b/jsonschema2pojo-integration-tests/src/test/resources/schema/jsr303/minAndMaxItemsExistingType.json @@ -5,7 +5,7 @@ "type" : "array", "minItems" : 2, "maxItems" : 4, - "existingJavaType" : "java.util.LinkedList" + "existingJavaType" : "java.util.LinkedList" } } } From 39ec7a357a917d941c207d856b3a4ec9364f4c60 Mon Sep 17 00:00:00 2001 From: unkish <3533269+unkish@users.noreply.github.com> Date: Sun, 15 Feb 2026 19:05:29 +0200 Subject: [PATCH 3/5] Update default target java version for CLI to be '8' --- .../src/main/java/org/jsonschema2pojo/cli/Arguments.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 a31a35dc2..63cf4e6ab 100644 --- a/jsonschema2pojo-cli/src/main/java/org/jsonschema2pojo/cli/Arguments.java +++ b/jsonschema2pojo-cli/src/main/java/org/jsonschema2pojo/cli/Arguments.java @@ -195,7 +195,7 @@ public class Arguments implements GenerationConfig { private boolean disableSetters = false; @Parameter(names = { "-tv", "--target-version" }, description = "The target version for generated source files.") - private String targetVersion = "1.6"; + private String targetVersion = "8"; @Parameter(names = { "-ida", "--include-dynamic-accessors" }, description = "Include dynamic getter, setter, and builder support on generated types.") private boolean includeDynamicAccessors = false; From d0e4d57413d14d1d92b9e553008ab564972fda50 Mon Sep 17 00:00:00 2001 From: unkish <3533269+unkish@users.noreply.github.com> Date: Mon, 16 Feb 2026 09:38:07 +0200 Subject: [PATCH 4/5] Added test cases for arrays * removed unused imports --- .../rules/MinItemsMaxItemsRule.java | 1 - .../rules/MinLengthMaxLengthRule.java | 1 - .../config/IncludeJsr303AnnotationsIT.java | 11 +++++++++++ .../integration/config/ParcelableIT.java | 1 - .../schema/jsr303/existingJavaTypeProperties.json | 14 +++++++++++++- 5 files changed, 24 insertions(+), 4 deletions(-) diff --git a/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/rules/MinItemsMaxItemsRule.java b/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/rules/MinItemsMaxItemsRule.java index b3e1f2765..a4914edeb 100644 --- a/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/rules/MinItemsMaxItemsRule.java +++ b/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/rules/MinItemsMaxItemsRule.java @@ -17,7 +17,6 @@ package org.jsonschema2pojo.rules; import java.lang.annotation.Annotation; -import java.lang.reflect.Array; import java.util.Collection; import java.util.Map; diff --git a/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/rules/MinLengthMaxLengthRule.java b/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/rules/MinLengthMaxLengthRule.java index d3b144583..9e893b389 100644 --- a/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/rules/MinLengthMaxLengthRule.java +++ b/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/rules/MinLengthMaxLengthRule.java @@ -17,7 +17,6 @@ package org.jsonschema2pojo.rules; import java.lang.annotation.Annotation; -import java.lang.reflect.Array; import java.util.Collection; import java.util.Map; diff --git a/jsonschema2pojo-integration-tests/src/test/java/org/jsonschema2pojo/integration/config/IncludeJsr303AnnotationsIT.java b/jsonschema2pojo-integration-tests/src/test/java/org/jsonschema2pojo/integration/config/IncludeJsr303AnnotationsIT.java index ed3c1af85..9f135a087 100644 --- a/jsonschema2pojo-integration-tests/src/test/java/org/jsonschema2pojo/integration/config/IncludeJsr303AnnotationsIT.java +++ b/jsonschema2pojo-integration-tests/src/test/java/org/jsonschema2pojo/integration/config/IncludeJsr303AnnotationsIT.java @@ -760,6 +760,17 @@ public void jsr303AnnotationsValidatedForExistingJavaType() throws ReflectiveOpe "existingTypeAsArrayItem", List.of(Map.of("a", new ClassWithSizeAnnotation()))); assertNumberOfConstraintViolationsOn(instance, is(1)); + + instance = createInstanceWithPropertyValue(clazz, "itemOfExistingClass", new ClassWithSizeAnnotation()); + assertNumberOfConstraintViolationsOn(instance, is(1)); + + // Note: at present arrays are generated as single item, once arrays are generated correctly below checks should be adjusted + instance = createInstanceWithPropertyValue(clazz, "arrayOfExistingClasses", new ClassWithSizeAnnotation()); + assertNumberOfConstraintViolationsOn(instance, is(1)); + + // Validation does not cascade through multidimensional arrays at present + instance = createInstanceWithPropertyValue(clazz, "multiDimensionalArrayOfExistingClasses", new ClassWithSizeAnnotation()); + assertNumberOfConstraintViolationsOn(instance, is(1)); } private void assertNumberOfConstraintViolationsOn(Object instance, Matcher matcher) { diff --git a/jsonschema2pojo-integration-tests/src/test/java/org/jsonschema2pojo/integration/config/ParcelableIT.java b/jsonschema2pojo-integration-tests/src/test/java/org/jsonschema2pojo/integration/config/ParcelableIT.java index 31b6a7208..b462bc783 100644 --- a/jsonschema2pojo-integration-tests/src/test/java/org/jsonschema2pojo/integration/config/ParcelableIT.java +++ b/jsonschema2pojo-integration-tests/src/test/java/org/jsonschema2pojo/integration/config/ParcelableIT.java @@ -43,7 +43,6 @@ import org.robolectric.internal.ShadowProvider; import org.robolectric.internal.bytecode.InstrumentationConfiguration; import org.robolectric.internal.bytecode.Interceptors; -import org.robolectric.internal.bytecode.MutableClass; import org.robolectric.internal.bytecode.ClassDetails; import org.robolectric.internal.bytecode.ClassInstrumentor; import org.robolectric.internal.bytecode.Sandbox; diff --git a/jsonschema2pojo-integration-tests/src/test/resources/schema/jsr303/existingJavaTypeProperties.json b/jsonschema2pojo-integration-tests/src/test/resources/schema/jsr303/existingJavaTypeProperties.json index 781caebf9..634f7cc4d 100644 --- a/jsonschema2pojo-integration-tests/src/test/resources/schema/jsr303/existingJavaTypeProperties.json +++ b/jsonschema2pojo-integration-tests/src/test/resources/schema/jsr303/existingJavaTypeProperties.json @@ -8,7 +8,7 @@ }, "listOfOptionalStringExistingClassMaps": { "type": "object", - "existingJavaType": "java.util.List>>" + "existingJavaType": "java.util.List>>" }, "listOfOptionalStringObjectMaps": { "type": "object", @@ -51,6 +51,18 @@ "items": { "existingJavaType": "java.util.Map" } + }, + "itemOfExistingClass": { + "type": "object", + "existingJavaType": "com.example.ClassWithSizeAnnotation" + }, + "arrayOfExistingClasses": { + "type": "object", + "existingJavaType": "com.example.ClassWithSizeAnnotation[]" + }, + "multiDimensionalArrayOfExistingClasses": { + "type": "object", + "existingJavaType": "com.example.ClassWithSizeAnnotation[][]" } } } From 4b7e9acfd0827a4c2369ed8b6cda992070dedfe4 Mon Sep 17 00:00:00 2001 From: unkish <3533269+unkish@users.noreply.github.com> Date: Tue, 17 Feb 2026 19:50:58 +0200 Subject: [PATCH 5/5] Partially revert changes from 'Fix JLS 9.7.4 type-use annotation placement': apply ValidRule on JFieldVar instead of JType --- .../rules/AdditionalPropertiesRule.java | 2 +- .../org/jsonschema2pojo/rules/ObjectRule.java | 3 +-- .../jsonschema2pojo/rules/PropertyRule.java | 2 ++ .../jsonschema2pojo/rules/RuleFactory.java | 2 +- .../org/jsonschema2pojo/rules/TypeRule.java | 2 -- .../org/jsonschema2pojo/rules/ValidRule.java | 25 +++++++++---------- .../config/IncludeJsr303AnnotationsIT.java | 2 ++ .../jsr303/existingJavaTypeProperties.json | 2 +- 8 files changed, 20 insertions(+), 20 deletions(-) diff --git a/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/rules/AdditionalPropertiesRule.java b/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/rules/AdditionalPropertiesRule.java index 4730dadbb..4d8f40711 100644 --- a/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/rules/AdditionalPropertiesRule.java +++ b/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/rules/AdditionalPropertiesRule.java @@ -115,10 +115,10 @@ public JDefinedClass apply(String nodeName, JsonNode node, JsonNode parent, JDef additionalPropertiesSchema.setJavaTypeIfEmpty(propertyType); } else { propertyType = jclass.owner().ref(Object.class); - propertyType = ruleFactory.getValidRule().apply(nodeName, node, parent, propertyType, schema); } JFieldVar field = addAdditionalPropertiesField(jclass, propertyType); + ruleFactory.getValidRule().apply(nodeName, node, parent, field, schema); addGetter(jclass, field); diff --git a/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/rules/ObjectRule.java b/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/rules/ObjectRule.java index af3f42906..f47757a2a 100644 --- a/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/rules/ObjectRule.java +++ b/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/rules/ObjectRule.java @@ -98,8 +98,7 @@ public JType apply(String nodeName, JsonNode node, JsonNode parent, JPackage _pa jclass._extends((JClass) superType); - // storing this type for future self refs - schema.setJavaTypeIfEmpty(ruleFactory.getValidRule().apply(nodeName, node, parent, jclass, schema)); + schema.setJavaTypeIfEmpty(jclass); if (node.has("title")) { ruleFactory.getTitleRule().apply(nodeName, node.get("title"), node, jclass, schema); diff --git a/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/rules/PropertyRule.java b/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/rules/PropertyRule.java index 1452f0188..839f31e8c 100644 --- a/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/rules/PropertyRule.java +++ b/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/rules/PropertyRule.java @@ -129,6 +129,8 @@ public JDefinedClass apply(String nodeName, JsonNode node, JsonNode parent, JDef ruleFactory.getDigitsRule().apply(nodeName, node, parent, field, schema); + ruleFactory.getValidRule().apply(nodeName, node, parent, field, schema); + return jclass; } diff --git a/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/rules/RuleFactory.java b/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/rules/RuleFactory.java index cddcd1430..3339984fd 100644 --- a/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/rules/RuleFactory.java +++ b/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/rules/RuleFactory.java @@ -313,7 +313,7 @@ public Rule getPatternRule() { * * @return a schema rule that applies the {@code @Valid} annotation to types requiring cascading validation. */ - public Rule getValidRule() { + public Rule getValidRule() { return new ValidRule(this); } diff --git a/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/rules/TypeRule.java b/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/rules/TypeRule.java index 626e6ed9f..36690f87f 100644 --- a/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/rules/TypeRule.java +++ b/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/rules/TypeRule.java @@ -118,8 +118,6 @@ public JType apply(String nodeName, JsonNode node, JsonNode parent, JClassContai type = ruleFactory.getMediaRule().apply(nodeName, node.get("media"), node, type, schema); } - type = ruleFactory.getValidRule().apply(nodeName, node, parent, type, schema); - return type; } diff --git a/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/rules/ValidRule.java b/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/rules/ValidRule.java index 64bd4f566..e95596fda 100644 --- a/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/rules/ValidRule.java +++ b/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/rules/ValidRule.java @@ -26,7 +26,7 @@ import com.fasterxml.jackson.databind.JsonNode; import com.sun.codemodel.JClass; -import com.sun.codemodel.JType; +import com.sun.codemodel.JFieldVar; import jakarta.validation.Valid; @@ -36,7 +36,7 @@ * Container types (Collections, Maps) are not annotated — only their element/value types are, * via the recursive rule pipeline. */ -public class ValidRule implements Rule { +public class ValidRule implements Rule { private final RuleFactory ruleFactory; @@ -45,29 +45,28 @@ public ValidRule(RuleFactory ruleFactory) { } @Override - public JType apply(String nodeName, JsonNode node, JsonNode parent, JType type, Schema currentSchema) { - if (ruleFactory.getGenerationConfig().isIncludeJsr303Annotations() && type instanceof JClass jclass) { - if (!isContainer(jclass) && !isScalar(jclass)) { - return JAnnotatedClass.of(jclass).annotated(getValidClass()); - } - if (null != node && node.has("existingJavaType")) { - return applyToExistingJavaType(jclass); + public JFieldVar apply(String nodeName, JsonNode node, JsonNode parent, JFieldVar field, Schema currentSchema) { + if (ruleFactory.getGenerationConfig().isIncludeJsr303Annotations() && field.type() instanceof JClass jClass) { + if (!isContainer(jClass) && !isScalar(jClass)) { + field.annotate(getValidClass()); + } else { + field.type(decorateAndAnnotate(jClass)); } } - return type; + return field; } - private JClass applyToExistingJavaType(JClass jClass) { + private JClass decorateAndAnnotate(JClass jClass) { if (jClass.isReference() && isContainer(jClass.erasure())) { final var typeParameters = jClass.getTypeParameters(); if ((jClass.owner().ref(Iterable.class).isAssignableFrom(jClass.erasure()) || jClass.owner().ref(Optional.class).isAssignableFrom(jClass.erasure())) && typeParameters.size() == 1 && !isScalar(typeParameters.get(0))) { - return jClass.erasure().narrow(applyToExistingJavaType(typeParameters.get(0))); + return jClass.erasure().narrow(decorateAndAnnotate(typeParameters.get(0))); } else if (jClass.owner().ref(Map.class).isAssignableFrom(jClass.erasure()) && typeParameters.size() == 2 && !isScalar(typeParameters.get(1))) { - return jClass.erasure().narrow(typeParameters.get(0), applyToExistingJavaType(typeParameters.get(1))); + return jClass.erasure().narrow(typeParameters.get(0), decorateAndAnnotate(typeParameters.get(1))); } } if (!isContainer(jClass) && !isScalar(jClass)) { diff --git a/jsonschema2pojo-integration-tests/src/test/java/org/jsonschema2pojo/integration/config/IncludeJsr303AnnotationsIT.java b/jsonschema2pojo-integration-tests/src/test/java/org/jsonschema2pojo/integration/config/IncludeJsr303AnnotationsIT.java index 9f135a087..956530846 100644 --- a/jsonschema2pojo-integration-tests/src/test/java/org/jsonschema2pojo/integration/config/IncludeJsr303AnnotationsIT.java +++ b/jsonschema2pojo-integration-tests/src/test/java/org/jsonschema2pojo/integration/config/IncludeJsr303AnnotationsIT.java @@ -44,6 +44,7 @@ import java.util.UUID; import org.apache.bval.jsr.ApacheValidationProvider; import org.hamcrest.Matcher; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; import org.junit.jupiter.params.ParameterizedClass; @@ -509,6 +510,7 @@ public void jsr303AnnotationsWithAdditionalPropertiesTrue() throws Exception { valueTypeAnnotations[0].annotationType(), is(expectedValidAnnotation)); } + @Disabled("@Valid is present on field's type not on method argument") @Test public void jsr303ValidAnnotationOnClassWithBuilders() throws Exception { schemaRule.generate( diff --git a/jsonschema2pojo-integration-tests/src/test/resources/schema/jsr303/existingJavaTypeProperties.json b/jsonschema2pojo-integration-tests/src/test/resources/schema/jsr303/existingJavaTypeProperties.json index 634f7cc4d..e2d24385a 100644 --- a/jsonschema2pojo-integration-tests/src/test/resources/schema/jsr303/existingJavaTypeProperties.json +++ b/jsonschema2pojo-integration-tests/src/test/resources/schema/jsr303/existingJavaTypeProperties.json @@ -8,7 +8,7 @@ }, "listOfOptionalStringExistingClassMaps": { "type": "object", - "existingJavaType": "java.util.List>>" + "existingJavaType": "java.util.List>>" }, "listOfOptionalStringObjectMaps": { "type": "object",