77import com .fasterxml .jackson .core .JsonProcessingException ;
88import com .fasterxml .jackson .core .type .TypeReference ;
99import com .fasterxml .jackson .datatype .jsr310 .*;
10+
11+ import io .swagger .v3 .oas .models .Operation ;
1012import io .swagger .v3 .oas .models .media .*;
1113
1214import com .fasterxml .jackson .annotation .JsonInclude .Include ;
@@ -39,6 +41,11 @@ public class DraftV4JsonSchemaGenerator extends BaseJsonSchemaGenerator implemen
3941 private static final String EXTERNALDOCS = "externalDocs" ;
4042 private static final String DEPRECATED = "deprecated" ;
4143
44+ private static final String ALLOF = "anyOf" ;
45+ private static final String ONEOF = "oneOf" ;
46+ private static final String ANYOF = "anyOf" ;
47+
48+
4249 private static final String JSONSCHEMA = "jsonSchema" ;
4350
4451 private static final String TYPES = "types" ;
@@ -59,35 +66,53 @@ public class DraftV4JsonSchemaGenerator extends BaseJsonSchemaGenerator implemen
5966 public static final String NULL = "null" ;
6067 private boolean strict ;
6168
62-
69+
6370 public DraftV4JsonSchemaGenerator (boolean strict ) {
6471 this .strict = strict ;
6572 }
6673
6774 private Map <String , JsonNode > generateForObjects () throws Exception {
6875 for (String ref : getMessageObjects ()) {
6976 String title = ref .replace (DEFINITIONS2 , "" );
70- Map <String , Object > defs = (Map <String , Object >) ((HashMap <String , Schema >) getObjectsDefinitions ()).clone ();
77+ Map <String , Object > defs = (Map <String , Object >) ((HashMap <String , Schema >) getObjectsDefinitions ()).clone ();
7178 Schema <Object > ob = (Schema <Object >) defs .get (title );
7279 defs .remove (title );
7380 Map <String , Object > res = new HashMap <String , Object >();
7481 Map <String ,Object > schemas = new HashMap <>();
7582 schemas .put (SCHEMAS ,defs );
7683 res .put (COMPONENTS , schemas );
77- res .put (TITLE2 , title );
84+ res .put (TITLE2 , title );
7885 log .info ("Generating json schema for object '{}' of type {}" , title ,ob .getClass ());
7986 if (ob instanceof ObjectSchema ) {
8087 res .put (TYPE , ((ObjectSchema ) ob ).getType ());
8188 res .put (PROPERTIES , ob .getProperties ());
82- res .put (REQUIRED ,ob .getRequired ());
83- if (((ObjectSchema ) ob ).getAdditionalProperties ()!=null ) {
84- log .info ("additionalProperties already exists... {}" ,((ObjectSchema ) ob ).getAdditionalProperties ());
85- res .put (ADDITIONAL_PROPERTIES ,((ObjectSchema ) ob ).getAdditionalProperties ());
89+ res .put (REQUIRED , ob .getRequired ());
90+
91+ if (ob .getProperties () == null || ob .getProperties ().isEmpty ()) {
92+ res .put (PROPERTIES , new HashMap <String , Schema >());
93+ log .info ("Object '{}' has no properties, creating empty properties object." );
94+ res .put (ADDITIONAL_PROPERTIES , true );
95+ log .warn ("Forced additionalProperties=true for object {}" ,title );
8696 } else {
87- res .put (ADDITIONAL_PROPERTIES , !this .strict );
97+ if (((ObjectSchema ) ob ).getAdditionalProperties ()!=null ) {
98+ log .info ("additionalProperties already exists... {}" ,((ObjectSchema ) ob ).getAdditionalProperties ());
99+ res .put (ADDITIONAL_PROPERTIES ,((ObjectSchema ) ob ).getAdditionalProperties ());
100+ } else {
101+ res .put (ADDITIONAL_PROPERTIES , !this .strict );
102+ }
88103 }
89104 }
105+ if (ob instanceof ComposedSchema ) {
106+ res .put (TYPE , ((ComposedSchema ) ob ).getType ());
107+ res .put (ALLOF , ob .getAllOf ());
108+ res .put (ONEOF , ob .getOneOf ());
109+ res .put (ANYOF , ob .getAnyOf ());
110+ }
90111 if (ob instanceof ArraySchema ) {
112+ Schema <?> items = ob .getItems ();
113+ if (items instanceof ObjectSchema && items .getProperties () == null ) {
114+ log .info ("Array items of type object has no properties" );
115+ }
91116 res .put (ITEMS , ((ArraySchema ) ob ).getItems ());
92117 res .put (TYPE , ((ArraySchema ) ob ).getType ());
93118 res .put (MIN_ITEMS , ((ArraySchema ) ob ).getMinItems ());
@@ -96,15 +121,14 @@ private Map<String, JsonNode> generateForObjects() throws Exception {
96121 if (ob instanceof MapSchema ) {
97122 res .put (PROPERTIES , ((MapSchema ) ob ).getProperties ());
98123 res .put (TYPE , ((MapSchema ) ob ).getType ());
99- res .put (REQUIRED ,ob .getRequired ());
100- res .put (ADDITIONAL_PROPERTIES ,((MapSchema ) ob ).getAdditionalProperties ());
124+ res .put (REQUIRED , ob .getRequired ());
125+ res .put (ADDITIONAL_PROPERTIES , ((MapSchema ) ob ).getAdditionalProperties ());
101126 }
102127 res .put ($SCHEMA , HTTP_JSON_SCHEMA_ORG_DRAFT_04_SCHEMA );
103128 removeUnusedObject (res ,ob );
104129 getGeneratedObjects ().put (title , postprocess (res ));
105130 }
106131 return getGeneratedObjects ();
107-
108132 }
109133
110134 private void removeUnusedObject (Map <String , Object > res , Schema <Object > ob ) {
@@ -173,8 +197,8 @@ private void navigateModel(String originalRef, List<String> usedDefinition, Map<
173197 if (ms .getAdditionalProperties () instanceof Schema ) {
174198 navigateSchema ("" , (Schema )ms .getAdditionalProperties (), usedDefinition , res );
175199 }
176-
177- } else if (ob instanceof Schema && ((Schema )ob ).get$ref ()!=null ){
200+
201+ } else if (ob instanceof Schema && ((Schema )ob ).get$ref ()!=null ){
178202 navigateModel (((Schema )ob ).get$ref (),usedDefinition ,res ,null );
179203 }
180204 }
@@ -194,11 +218,16 @@ private void navigateSchema(String propertyName, Schema p, List<String> usedDefi
194218 } else if (p instanceof ArraySchema ) {
195219 ArraySchema ap = (ArraySchema ) p ;
196220 log .debug ("Array property={} items={}" ,ap ,ap .getItems ());
221+ if (ap .getItems () instanceof ObjectSchema && ap .getItems ().getProperties () == null ){
222+ log .info ("Array items of type object has no properties" );
223+ }
197224 navigateSchema ("items" ,ap .getItems (),usedDefinition ,res );
198225 } else if (p instanceof ObjectSchema ){
199226 ObjectSchema op = (ObjectSchema ) p ;
200- for (String name : op .getProperties ().keySet ()){
201- navigateSchema (name ,op .getProperties ().get (name ),usedDefinition ,res );
227+ if (op .getProperties () != null ) {
228+ for (String name : op .getProperties ().keySet ()) {
229+ navigateSchema (name , op .getProperties ().get (name ), usedDefinition , res );
230+ }
202231 }
203232 } else if (p instanceof MapSchema ) {
204233 MapSchema mp = (MapSchema )p ;
@@ -216,7 +245,7 @@ public class DynamicMixIn {
216245 }
217246
218247 private JsonNode postprocess (Map <String , Object > res ) throws Exception {
219- //devo gestire i valori nullable potenzialmente presenti su oas 3.0
248+ //need to handle all nullable oas3 possible values
220249 res = handleNullableFields (res );
221250 JsonNode jsonNode = removeNonJsonSchemaProperties (res );
222251 process ("" , jsonNode );
@@ -229,7 +258,7 @@ private JsonNode postprocess(Map<String, Object> res) throws Exception {
229258 }
230259
231260 private JsonNode removeNonJsonSchemaProperties (Map <String , Object > res ) throws JsonProcessingException {
232-
261+
233262 iterateMap (res ,null );
234263 ObjectMapper mapper = new ObjectMapper ();
235264 mapper .setSerializationInclusion (Include .NON_NULL );
@@ -238,14 +267,14 @@ private JsonNode removeNonJsonSchemaProperties(Map<String, Object> res) throws J
238267 JsonNode jsonNode = mapper2 .readValue (json , JsonNode .class );
239268 return jsonNode ;
240269 }
241-
242- //rimuove tutte le properties di oas3 non gestite in json schema
270+
271+ //this method remove all unmanaged oas3 json schema props
243272 private void iterateMap (Map <String , Object > res , String father ) {
244273 if (res ==null )
245274 return ;
246275 for (String k : res .keySet ()) {
247276 if (res .get (k )!=null ) {
248- log .debug ("key={}" ,k );
277+ log .debug ("key={} father={} " ,k , father );
249278 if (!"properties" .equals (father )) {
250279 //devo rimuovere i valori da ignorare (solo se il padre non è un campo 'properties'
251280 if (ignorePropertiesList .contains (k )) {
@@ -259,11 +288,11 @@ private void iterateMap(Map<String, Object> res, String father) {
259288 }
260289 }
261290 }
262-
291+
263292 }
264-
265-
266-
293+
294+
295+
267296
268297 private Map <String , Object > handleNullableFields (Map <String , Object > result ) {
269298 ObjectMapper mapper = new ObjectMapper ();
@@ -286,33 +315,34 @@ private void process(String prefix, JsonNode currentNode) {
286315 currentNode .fields ().forEachRemaining (entry -> process (
287316 !prefix .isEmpty () ? prefix + "-" + entry .getKey () : entry .getKey (), entry .getValue ()));
288317 ObjectNode on = ((ObjectNode ) currentNode );
289- if (currentNode .get (TYPE ) != null ) {
290- String type = currentNode .get (TYPE ).asText ();
291- if ("object" .equals (type )) {
292- if (on .get (ADDITIONAL_PROPERTIES )!=null ) {
293- log .debug ("already defined additionalProperties with value {}" ,on .get (ADDITIONAL_PROPERTIES ).asText ());
294- } else {
295- if (currentNode .get (PROPERTIES )!=null && currentNode .get (PROPERTIES ).isEmpty ()) {
296- //devo settare additionalProperties a true come di default se l'oggetto non specifica nessuna property
297- on .set (ADDITIONAL_PROPERTIES , BooleanNode .valueOf (true ));
298- log .debug ("setting additional properties with value {} as this object has empty properties" , true );
299- } else {
300- on .set (ADDITIONAL_PROPERTIES , BooleanNode .valueOf (!this .strict ));
301- log .debug ("setting additional properties with value {}" , !this .strict );
302- }
303- }
318+ if (currentNode .has (TYPE ) && "object" .equals (currentNode .get (TYPE ).asText ())) {
319+ if (on .get (ADDITIONAL_PROPERTIES ) != null ) {
320+ log .debug ("already defined additionalProperties with value {}" ,on .get (ADDITIONAL_PROPERTIES ).asText ());
321+ }
322+ if (!currentNode .has (PROPERTIES ) || !currentNode .get (PROPERTIES ).fields ().hasNext ()) {
323+ on .put (ADDITIONAL_PROPERTIES , true );
324+ log .debug ("setting additional properties with value {}" , true );
325+ }else {
326+ on .set (ADDITIONAL_PROPERTIES , BooleanNode .valueOf (!this .strict ));
327+ log .debug ("setting additional properties with value {}" , !this .strict );
304328 }
305329 }
306- if (currentNode .get (ORIGINAL_REF ) != null ) {
330+ if (currentNode .has (ORIGINAL_REF )) {
307331 on .remove (ORIGINAL_REF );
308332 log .debug ("removing originalRef field" );
309333 }
334+ if (currentNode .get (EXAMPLESETFLAG ) != null ) {
335+ on .remove (EXAMPLESETFLAG );
336+ log .debug ("removing exampleSetFlag field" );
337+ }
310338 } else {
311339 log .debug (prefix + ": " + currentNode .toString ());
312340 }
313341 }
314342
315343
344+
345+
316346 private boolean isValidJsonSchemaSyntax (JsonNode jsonSchemaFile ) {
317347 SyntaxValidator synValidator = JsonSchemaFactory .byDefault ().getSyntaxValidator ();
318348 ProcessingReport report = synValidator .validateSchema (jsonSchemaFile );
0 commit comments