Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Introduce Option.ACCEPT_SINGLE_VALUE_AS_ARRAY #460

Merged
merged 1 commit into from
Jul 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]
### `jsonschema-generator`
#### Added
- new `Option.ACCEPT_SINGLE_VALUE_AS_ARRAY` to support Jackson `DeserializationFeature` of the same name, i.e., when an array type is declared, an instance of a single item should also be accepted by the schema

#### Changed
- consider `Boolean` values as valid in `const`/`enum` (i.e., no longer ignore them)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import com.fasterxml.classmate.ResolvedType;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.github.victools.jsonschema.generator.MemberScope;
import com.github.victools.jsonschema.generator.Option;
import com.github.victools.jsonschema.generator.OptionPreset;
import com.github.victools.jsonschema.generator.SchemaGenerationContext;
import com.github.victools.jsonschema.generator.SchemaGenerator;
Expand All @@ -42,22 +43,13 @@ public class SingleArrayItemExample implements SchemaGenerationExampleInterface
@Override
public ObjectNode generateSchema() {
SchemaGeneratorConfigBuilder configBuilder = new SchemaGeneratorConfigBuilder(SchemaVersion.DRAFT_2020_12, OptionPreset.PLAIN_JSON);
configBuilder.forFields()
.withTargetTypeOverridesResolver(this::acceptSingleValueAsArray);
configBuilder.with(Option.ACCEPT_SINGLE_VALUE_AS_ARRAY);
SchemaGeneratorConfig config = configBuilder.build();
SchemaGenerator generator = new SchemaGenerator(config);
return generator.generateSchema(Example.class);
}

private List<ResolvedType> acceptSingleValueAsArray(MemberScope<?, ?> scope) {
if (scope.isContainerType() && !scope.isFakeContainerItemScope()) {
return Arrays.asList(scope.getContainerItemType(), scope.getType());
}
return null;
}

static class Example {
@NotNull
public List<ArrayItem> someArray;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import com.github.victools.jsonschema.generator.impl.module.MethodExclusionModule;
import com.github.victools.jsonschema.generator.impl.module.SimpleTypeModule;
import com.github.victools.jsonschema.generator.impl.module.SimplifiedOptionalModule;
import com.github.victools.jsonschema.generator.impl.module.SingleValueAsArrayModule;
import java.util.Collections;
import java.util.Set;
import java.util.function.Supplier;
Expand Down Expand Up @@ -222,6 +223,14 @@ public enum Option {
* @since 4.11.0
*/
MAP_VALUES_AS_ADDITIONAL_PROPERTIES(AdditionalPropertiesModule::forMapValues, null),
/**
* Whether each property with a container/{@link java.util.Collection Collection} type should also allow for a single collection item to be
* provided instead of an array. This corresponds to the Jackson
* {@link com.fasterxml.jackson.databind.DeserializationFeature#ACCEPT_SINGLE_VALUE_AS_ARRAY ACCEPT_SINGLE_VALUE_AS_ARRAY} feature.
*
* @since 4.36.0
*/
ACCEPT_SINGLE_VALUE_AS_ARRAY(SingleValueAsArrayModule::new, null),
/**
* Whether allowed values should always be included in an {@link SchemaKeyword#TAG_ENUM "enum"} keyword. If there is exactly one allowed value, it
* will otherwise be represented by a {@link SchemaKeyword#TAG_CONST "const"} keyword instead.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*
* Copyright 2024 VicTools.
*
* 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.github.victools.jsonschema.generator.impl.module;

import com.fasterxml.classmate.ResolvedType;
import com.github.victools.jsonschema.generator.MemberScope;
import com.github.victools.jsonschema.generator.Module;
import com.github.victools.jsonschema.generator.SchemaGeneratorConfigBuilder;
import java.util.Arrays;
import java.util.List;

/**
* Default module being included if {@code Option.ACCEPT_SINGLE_VALUE_AS_ARRAY} is enabled.
*
* @since 4.36.0
*/
public class SingleValueAsArrayModule implements Module {

/**
* Allow a container's item type as single instance alternative to an array, or return null for non-containers.
*
* @param scope targeted field/method
* @return collection containing both the container's item type and the container type as such, or null
*/
private static List<ResolvedType> acceptSingleValueAsArray(MemberScope<?, ?> scope) {
if (scope.isContainerType() && !scope.isFakeContainerItemScope()) {
return Arrays.asList(scope.getContainerItemType(), scope.getType());
}
return null;
}

@Override
public void applyToConfigBuilder(SchemaGeneratorConfigBuilder builder) {
builder.forFields()
.withTargetTypeOverridesResolver(SingleValueAsArrayModule::acceptSingleValueAsArray);
builder.forMethods()
.withTargetTypeOverridesResolver(SingleValueAsArrayModule::acceptSingleValueAsArray);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,8 @@ static Stream<Arguments> parametersForTestGenerateSchema() {
.withAnchorResolver(scope -> scope.isContainerType() ? null : "#anchor")
.withPropertySorter((_prop1, _prop2) -> 0),
"for type in general: ");
Module methodModule = configBuilder -> populateConfigPart(configBuilder.with(Option.FIELDS_DERIVED_FROM_ARGUMENTFREE_METHODS)
Module methodModule = configBuilder -> populateConfigPart(configBuilder.with(Option.FIELDS_DERIVED_FROM_ARGUMENTFREE_METHODS,
Option.ACCEPT_SINGLE_VALUE_AS_ARRAY)
.forMethods(), "looked-up from method: ");
Module fieldModule = configBuilder -> populateConfigPart(configBuilder.with(Option.INLINE_ALL_SCHEMAS).forFields(), "looked-up from field: ");
Module enumToStringModule = configBuilder -> configBuilder.with(Option.FLATTENED_ENUMS_FROM_TOSTRING);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,16 @@
"const": 5
},
"genericArray": {
"type": "array",
"items": {
"type": "string"
}
"anyOf": [
{
"type": "string"
}, {
"type": "array",
"items": {
"type": "string"
}
}
]
},
"genericValue": {
"type": ["string", "null"],
Expand Down
Loading
Loading