@@ -101,10 +101,17 @@ public class OpenAPINormalizer {
101101 final String X_INTERNAL = "x-internal" ;
102102 boolean removeXInternal ;
103103
104- // when set (e.g. operationId:getPetById, addPet), filter out (or remove) everything else
104+ // when set (e.g. operationId:getPetById| addPet), filter out (or remove) everything else
105105 final String FILTER = "FILTER" ;
106106 HashSet <String > operationIdFilters = new HashSet <>();
107107
108+ // when set (e.g. operationId:getPetById|addPet), filter out (or remove) everything else
109+ final String SET_CONTAINER_TO_NULLABLE = "SET_CONTAINER_TO_NULLABLE" ;
110+ HashSet <String > setContainerToNullable = new HashSet <>();
111+ boolean updateArrayToNullable ;
112+ boolean updateSetToNullable ;
113+ boolean updateMapToNullable ;
114+
108115 // ============= end of rules =============
109116
110117 /**
@@ -199,6 +206,23 @@ public void processRules(Map<String, String> inputRules) {
199206 }
200207 }
201208 }
209+
210+ if (inputRules .get (SET_CONTAINER_TO_NULLABLE ) != null ) {
211+ rules .put (SET_CONTAINER_TO_NULLABLE , true );
212+ setContainerToNullable = new HashSet <>(Arrays .asList (inputRules .get (SET_CONTAINER_TO_NULLABLE ).split ("[|]" )));
213+ if (setContainerToNullable .contains ("array" )) {
214+ updateArrayToNullable = true ;
215+ }
216+ if (setContainerToNullable .contains ("set" )) {
217+ updateSetToNullable = true ;
218+ }
219+ if (setContainerToNullable .contains ("map" )) {
220+ updateMapToNullable = true ;
221+ }
222+ if (!updateArrayToNullable && !updateSetToNullable && !updateMapToNullable ) {
223+ LOGGER .error ("SET_CONTAINER_TO_NULLABLE rule must be in the form of `array|set|map`, e.g. `set`, `array|map`: {}" , inputRules .get (SET_CONTAINER_TO_NULLABLE ));
224+ }
225+ }
202226 }
203227
204228 /**
@@ -445,8 +469,10 @@ public Schema normalizeSchema(Schema schema, Set<Schema> visitedSchemas) {
445469 }
446470
447471 if (schema instanceof ArraySchema ) { // array
472+ normalizeArraySchema (schema );
448473 normalizeSchema (schema .getItems (), visitedSchemas );
449474 } else if (schema .getAdditionalProperties () instanceof Schema ) { // map
475+ normalizeMapSchema (schema );
450476 normalizeSchema ((Schema ) schema .getAdditionalProperties (), visitedSchemas );
451477 } else if (ModelUtils .isOneOf (schema )) { // oneOf
452478 return normalizeOneOf (schema , visitedSchemas );
@@ -498,6 +524,14 @@ public Schema normalizeSchema(Schema schema, Set<Schema> visitedSchemas) {
498524 return schema ;
499525 }
500526
527+ private Schema normalizeArraySchema (Schema schema ) {
528+ return processSetArraytoNullable (schema );
529+ }
530+
531+ private Schema normalizeMapSchema (Schema schema ) {
532+ return processSetMapToNullable (schema );
533+ }
534+
501535 private Schema normalizeSimpleSchema (Schema schema , Set <Schema > visitedSchemas ) {
502536 return processNormalize31Spec (schema , visitedSchemas );
503537 }
@@ -864,6 +898,60 @@ private Schema processSimplifyOneOf(Schema schema) {
864898 return schema ;
865899 }
866900
901+ /**
902+ * Set nullable to true in array/set if needed.
903+ *
904+ * @param schema Schema
905+ * @return Schema
906+ */
907+ private Schema processSetArraytoNullable (Schema schema ) {
908+ if (!getRule (SET_CONTAINER_TO_NULLABLE )) {
909+ return schema ;
910+ }
911+
912+ if (Boolean .TRUE .equals (schema .getUniqueItems ())) { // a set
913+ if (updateSetToNullable ) {
914+ if (schema .getNullable () != null || (schema .getExtensions () != null && schema .getExtensions ().containsKey ("x-nullable" ))) {
915+ // already set, don't overwrite
916+ return schema ;
917+ }
918+ schema .setNullable (true );
919+ }
920+ } else { // array
921+ if (updateArrayToNullable ) {
922+ if (schema .getNullable () != null || (schema .getExtensions () != null && schema .getExtensions ().containsKey ("x-nullable" ))) {
923+ // already set, don't overwrite
924+ return schema ;
925+ }
926+ schema .setNullable (true );
927+ }
928+ }
929+
930+ return schema ;
931+ }
932+
933+ /**
934+ * Set nullable to true in map if needed.
935+ *
936+ * @param schema Schema
937+ * @return Schema
938+ */
939+ private Schema processSetMapToNullable (Schema schema ) {
940+ if (!getRule (SET_CONTAINER_TO_NULLABLE )) {
941+ return schema ;
942+ }
943+
944+ if (updateMapToNullable ) {
945+ if (schema .getNullable () != null || (schema .getExtensions () != null && schema .getExtensions ().containsKey ("x-nullable" ))) {
946+ // already set, don't override
947+ return schema ;
948+ }
949+ schema .setNullable (true );
950+ }
951+
952+ return schema ;
953+ }
954+
867955 /**
868956 * If the schema is anyOf and the sub-schemas is null, set `nullable: true` instead.
869957 * If there's only one sub-schema, simply return the sub-schema directly.
0 commit comments