Skip to content

Commit

Permalink
feat: add JacksonOption.RESPECT_JSONPROPERTY_REQUIRED (#163, #164)
Browse files Browse the repository at this point in the history
  • Loading branch information
CarstenWickner committed Jan 22, 2021
1 parent b7a0bf7 commit 3fac9c7
Show file tree
Hide file tree
Showing 7 changed files with 85 additions and 13 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]
### `jsonschema-module-jackson`
#### Added
- New `JacksonOption.RESPECT_JSONPROPERTY_REQUIRED` to set a field as "required" based on `@JsonProperty` annotations

## [4.17.0] - 2020-12-24
### `jsonschema-module-jakarta-validation`
Expand Down
15 changes: 8 additions & 7 deletions jsonschema-module-jackson/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,14 @@ Module for the [jsonschema-generator](../jsonschema-generator) – deriving JSON
3. Apply alternative field names defined in `@JsonProperty` annotations or as per `@JsonNaming` annotations.
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. Optionally: treat enum types as plain strings, as per each enum constant's `@JsonProperty` annotation
8. Optionally: sort an object's properties according to its `@JsonPropertyOrder` annotation
9. Optionally: resolve subtypes according to `@JsonSubTypes` on a supertype in general or directly on specific fields/methods as an override of the per-type behavior.
10. Optionally: apply structural changes for subtypes according to `@JsonTypeInfo` on a supertype in general or directly on specific fields/methods as an override of the per-type behavior.
- 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`
6. Optionally: set a field as "required" as per `@JsonProperty` annotations, if the `JacksonOption.RESPECT_JSONPROPERTY_REQUIRED` was provided (i.e. this is an "opt-in").
7. Optionally: treat enum types as plain strings as per the `@JsonValue` annotated method, if there is one and the `JacksonOption.FLATTENED_ENUMS_FROM_JSONVALUE` was provided (i.e. this is an "opt-in").
8. Optionally: treat enum types as plain strings, as per each enum constant's `@JsonProperty` annotation, if all values of an enum have such annotations and the `JacksonOption.FLATTENED_ENUMS_FROM_JSONPROPERTY` was provided (i.e. this is an "opt-in").
9. Optionally: sort an object's properties according to its `@JsonPropertyOrder` annotation, if the `JacksonOption.RESPECT_JSONPROPERTY_ORDER` was provided (i.e. this is an "opt-in").
10. Subtype resolution according to `@JsonSubTypes` on a supertype in general or directly on specific fields/methods as an override of the per-type behavior unless `JacksonOption.SKIP_SUBTYPE_LOOKUP` was provided (i.e. this is an "opt-out").
11. Apply structural changes for subtypes according to `@JsonTypeInfo` on a supertype in general or directly on specific fields/methods as an override of the per-type behavior unless `JacksonOption.IGNORE_TYPE_INFO_TRANSFORM` was provided (i.e. this is an "opt-out").
* 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.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,10 @@ public void applyToConfigBuilder(SchemaGeneratorConfigBuilder builder) {
generalConfigPart.withPropertySorter(new JsonPropertySorter(true));
}

if (this.options.contains(JacksonOption.RESPECT_JSONPROPERTY_REQUIRED)) {
fieldConfigPart.withRequiredCheck(this::getRequiredCheckBasedOnJsonPropertyAnnotation);
}

boolean lookUpSubtypes = !this.options.contains(JacksonOption.SKIP_SUBTYPE_LOOKUP);
boolean includeTypeInfoTransform = !this.options.contains(JacksonOption.IGNORE_TYPE_INFO_TRANSFORM);
if (lookUpSubtypes || includeTypeInfoTransform) {
Expand Down Expand Up @@ -248,4 +252,16 @@ protected boolean shouldIgnoreField(FieldScope field) {
return beanDescription.findProperties().stream()
.noneMatch(propertyDefinition -> fieldName.equals(propertyDefinition.getInternalName()));
}

/**
* Look-up the given field's {@link JsonProperty} annotation and consider its "required" attribute.
*
* @param field field to look-up required strategy for
* @return whether the field should be in the "required" list or not
*/
private boolean getRequiredCheckBasedOnJsonPropertyAnnotation(FieldScope field) {
JsonProperty jsonProperty = field.getAnnotationConsideringFieldAndGetter(JsonProperty.class) ;
return jsonProperty != null && jsonProperty.required();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -63,5 +63,11 @@ public enum JacksonOption {
/**
* Use this option to skip the transformation according to {@code @JsonTypeInfo} annotations (typically used to identify specific subtypes).
*/
IGNORE_TYPE_INFO_TRANSFORM;
IGNORE_TYPE_INFO_TRANSFORM,
/**
* Use this option to include fields annotated with {@code @JsonProperty(required = true)} in the containing type's list of "required" properties.
*
* @since 4.18.0
*/
RESPECT_JSONPROPERTY_REQUIRED
}
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,29 @@ public void testDescriptionResolver(String fieldName, String expectedDescription
Assert.assertEquals(expectedDescription, description);
}

Object parametersForTestRequiredProperty() {
return new Object[][]{
{null, "requiredTrue", false},
{null, "requiredFalse", false},
{null, "requiredDefault", false},
{null, "requiredAbsent", false},
{JacksonOption.RESPECT_JSONPROPERTY_REQUIRED, "requiredTrue", true},
{JacksonOption.RESPECT_JSONPROPERTY_REQUIRED, "requiredFalse", false},
{JacksonOption.RESPECT_JSONPROPERTY_REQUIRED, "requiredDefault", false},
{JacksonOption.RESPECT_JSONPROPERTY_REQUIRED, "requiredAbsent", false}
};
}

@Test
@Parameters
public void testRequiredProperty(JacksonOption requiredOption, String fieldName, boolean expectedRequired) {
new JacksonModule(requiredOption).applyToConfigBuilder(this.configBuilder);

FieldScope field = new TestType(TestClassWithRequiredAnnotatedFields.class).getMemberField(fieldName);

Assert.assertEquals(this.fieldConfigPart.isRequired(field), expectedRequired);
}

Object parametersForTestDescriptionForTypeResolver() {
return new Object[][]{
{"unannotatedField", null},
Expand Down Expand Up @@ -292,4 +315,19 @@ public Long getFieldWithDescriptionAndOnGetter() {
return fieldWithDescriptionAndOnGetter;
}
}

private static class TestClassWithRequiredAnnotatedFields {
@JsonProperty(required = true)
private String requiredTrue;

@JsonProperty(required = false)
private String requiredFalse;

@JsonProperty
private String requiredDefault;

private String requiredAbsent;

}

}
7 changes: 7 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,13 @@
<role>Provided PR #156 (port of JavaxValidationModule to JakartaValidationModule and its support in the maven plugin)</role>
</roles>
</contributor>
<contributor>
<name>Jean-Marc Bourdaret</name>
<url>https://github.com/jjmbourdaret</url>
<roles>
<role>Provided PR #163 (introducing support for @JsonProperty(required))</role>
</roles>
</contributor>
</contributors>

<distributionManagement>
Expand Down
11 changes: 6 additions & 5 deletions slate-docs/source/includes/_jackson-module.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,12 @@ SchemaGeneratorConfigBuilder configBuilder = new SchemaGeneratorConfigBuilder(Sc
3. Override a field's property name as per `@JsonProperty` annotations.
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. Treat enum types as plain strings as per the `@JsonValue` annotated method, if there is one and the `JacksonOption.FLATTENED_ENUMS_FROM_JSONVALUE` was provided (i.e. this is an "opt-in").
7. Optionally: treat enum types as plain strings, as per each enum constant's `@JsonProperty` annotation, if all values of an enum have such annotations and the `JacksonOption.FLATTENED_ENUMS_FROM_JSONPROPERTY` was provided (i.e. this is an "opt-in").
8. Optionally: sort an object's properties according to its `@JsonPropertyOrder` annotation, if the `JacksonOption.RESPECT_JSONPROPERTY_ORDER` was provided (i.e. this is an "opt-in").
9. Subtype resolution according to `@JsonSubTypes` on a supertype in general or directly on specific fields/methods as an override of the per-type behavior unless `JacksonOption.SKIP_SUBTYPE_LOOKUP` was provided (i.e. this is an "opt-out").
10. Apply structural changes for subtypes according to `@JsonTypeInfo` on a supertype in general or directly on specific fields/methods as an override of the per-type behavior unless `JacksonOption.IGNORE_TYPE_INFO_TRANSFORM` was provided (i.e. this is an "opt-out").
6. Optionally: set a field as "required" as per `@JsonProperty` annotations, if the `JacksonOption.RESPECT_JSONPROPERTY_REQUIRED` was provided (i.e. this is an "opt-in").
7. Optionally: treat enum types as plain strings as per the `@JsonValue` annotated method, if there is one and the `JacksonOption.FLATTENED_ENUMS_FROM_JSONVALUE` was provided (i.e. this is an "opt-in").
8. Optionally: treat enum types as plain strings, as per each enum constant's `@JsonProperty` annotation, if all values of an enum have such annotations and the `JacksonOption.FLATTENED_ENUMS_FROM_JSONPROPERTY` was provided (i.e. this is an "opt-in").
9. Optionally: sort an object's properties according to its `@JsonPropertyOrder` annotation, if the `JacksonOption.RESPECT_JSONPROPERTY_ORDER` was provided (i.e. this is an "opt-in").
10. Subtype resolution according to `@JsonSubTypes` on a supertype in general or directly on specific fields/methods as an override of the per-type behavior unless `JacksonOption.SKIP_SUBTYPE_LOOKUP` was provided (i.e. this is an "opt-out").
11. Apply structural changes for subtypes according to `@JsonTypeInfo` on a supertype in general or directly on specific fields/methods as an override of the per-type behavior unless `JacksonOption.IGNORE_TYPE_INFO_TRANSFORM` was provided (i.e. this is an "opt-out").
* 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`

Expand Down

0 comments on commit 3fac9c7

Please sign in to comment.