Parsing recursive annotations #335
-
Hello,
Could I somehow mark a field with this annotation and have the JSON schema generator pick up the Thanks |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment 8 replies
-
Hi @roded, Not out-of-the-box right now. I've created an example, that covers this requirement at least partially: public class GroupedAnnotationExample {
public ObjectNode generateSchema() {
SchemaGeneratorConfig config = new SchemaGeneratorConfigBuilder(SchemaVersion.DRAFT_2019_09, OptionPreset.PLAIN_JSON)
.with(new GroupedAnnotationAwareJakartaValidationModule(JakartaValidationOption.INCLUDE_PATTERN_EXPRESSIONS))
.build();
SchemaGenerator generator = new SchemaGenerator(config);
return generator.generateSchema(Example.class);
}
static class GroupedAnnotationAwareJakartaValidationModule extends JakartaValidationModule {
GroupedAnnotationAwareJakartaValidationModule(JakartaValidationOption... options) {
super(options);
}
@Override
protected <A extends Annotation> A getAnnotationFromFieldOrGetter(MemberScope<?, ?> member, Class<A> annotationClass,
Function<A, Class<?>[]> validationGroupsLookup) {
A annotation = super.getAnnotationFromFieldOrGetter(member, annotationClass, validationGroupsLookup);
if (annotation != null) {
return annotation;
}
if (!member.isFakeContainerItemScope()) {
Optional<A> groupedAnnotationFromMember = this.getGroupedAnnotationFromMember(member, annotationClass);
if (!groupedAnnotationFromMember.isPresent()) {
MemberScope<?, ?> alternativeMember;
if (member instanceof FieldScope) {
alternativeMember = ((FieldScope) member).findGetter();
} else if (member instanceof MethodScope) {
alternativeMember = ((MethodScope) member).findGetterField();
} else {
return null;
}
if (alternativeMember != null) {
groupedAnnotationFromMember = this.getGroupedAnnotationFromMember(alternativeMember, annotationClass);
}
}
return groupedAnnotationFromMember.orElse(null);
}
return null;
}
private <A extends Annotation> Optional<A> getGroupedAnnotationFromMember(MemberScope<?, ?> member, Class<A> annotationClass) {
for (Annotation otherAnnotation : member.getMember().getAnnotations()) {
A annotation = otherAnnotation.annotationType().getDeclaredAnnotation(annotationClass);
if (annotation != null) {
return Optional.of(annotation);
}
}
return Optional.empty();
}
}
static class Example {
@CustomIdentifier
private String identifier;
}
@Pattern(regexp = "^[a-zA-Z#@$][a-zA-Z#@$0-9]*$")
@Size(min = 1, max = 8)
@Constraint(validatedBy = {})
@Target({ElementType.PARAMETER, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface CustomIdentifier {
String message() default "";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
} The generated schema looks like this then: {
"$schema" : "https://json-schema.org/draft/2019-09/schema",
"type" : "object",
"properties" : {
"identifier" : {
"type" : "string",
"minLength" : 1,
"maxLength" : 8,
"pattern" : "^[a-zA-Z#@$][a-zA-Z#@$0-9]*$"
}
}
} |
Beta Was this translation helpful? Give feedback.
Hi @roded,
Not out-of-the-box right now.
Edit: starting with release 4.30.0, this is in fact supported by the
jakarta
andjavax
validation modules without requiring further configuration. Until then, the below workaround can still be used.I've created an example, that covers this requirement at least partially: