@@ -195,11 +195,6 @@ internal static void ApplyDefaultValue(this JsonNode schema, object? defaultValu
195195 /// underlying schema generator does not support this, we need to manually apply the
196196 /// supported formats to the schemas associated with the generated type.
197197 ///
198- /// Whereas JsonSchema represents nullable types via `type: ["string", "null"]`, OpenAPI
199- /// v3 exposes a nullable property on the schema. This method will set the nullable property
200- /// based on whether the underlying schema generator returned an array type containing "null" to
201- /// represent a nullable type or if the type was denoted as nullable from our lookup cache.
202- ///
203198 /// Note that this method targets <see cref="JsonNode"/> and not <see cref="OpenApiSchema"/> because
204199 /// it is is designed to be invoked via the `OnGenerated` callback in the underlying schema generator as
205200 /// opposed to after the generated schemas have been mapped to OpenAPI schemas.
@@ -349,8 +344,6 @@ internal static void ApplyParameterInfo(this JsonNode schema, ApiParameterDescri
349344 {
350345 schema . ApplyValidationAttributes ( validationAttributes ) ;
351346 }
352-
353- schema . ApplyNullabilityContextInfo ( parameterInfo ) ;
354347 }
355348 // Route constraints are only defined on parameters that are sourced from the path. Since
356349 // they are encoded in the route template, and not in the type information based to the underlying
@@ -451,42 +444,49 @@ private static bool IsNonAbstractTypeWithoutDerivedTypeReference(JsonSchemaExpor
451444 }
452445
453446 /// <summary>
454- /// Support applying nullability status for reference types provided as a parameter .
447+ /// Support applying nullability status for reference types provided as a property or field .
455448 /// </summary>
456449 /// <param name="schema">The <see cref="JsonNode"/> produced by the underlying schema generator.</param>
457- /// <param name="parameterInfo ">The <see cref="ParameterInfo " /> associated with the schema.</param>
458- internal static void ApplyNullabilityContextInfo ( this JsonNode schema , ParameterInfo parameterInfo )
450+ /// <param name="propertyInfo ">The <see cref="JsonPropertyInfo " /> associated with the schema.</param>
451+ internal static void ApplyNullabilityContextInfo ( this JsonNode schema , JsonPropertyInfo propertyInfo )
459452 {
460- if ( parameterInfo . ParameterType . IsValueType )
453+ // Avoid setting explicit nullability annotations for `object` types so they continue to match on the catch
454+ // all schema (no type, no format, no constraints).
455+ if ( propertyInfo . PropertyType != typeof ( object ) && ( propertyInfo . IsGetNullable || propertyInfo . IsSetNullable ) )
461456 {
462- return ;
457+ if ( MapJsonNodeToSchemaType ( schema [ OpenApiSchemaKeywords . TypeKeyword ] ) is { } schemaTypes &&
458+ ! schemaTypes . HasFlag ( JsonSchemaType . Null ) )
459+ {
460+ schema [ OpenApiSchemaKeywords . TypeKeyword ] = ( schemaTypes | JsonSchemaType . Null ) . ToString ( ) ;
461+ }
463462 }
464-
465- var nullabilityInfoContext = new NullabilityInfoContext ( ) ;
466- var nullabilityInfo = nullabilityInfoContext . Create ( parameterInfo ) ;
467- if ( nullabilityInfo . WriteState == NullabilityState . Nullable
468- && MapJsonNodeToSchemaType ( schema [ OpenApiSchemaKeywords . TypeKeyword ] ) is { } schemaTypes
469- && ! schemaTypes . HasFlag ( JsonSchemaType . Null ) )
463+ if ( schema [ OpenApiConstants . SchemaId ] is not null &&
464+ propertyInfo . PropertyType != typeof ( object ) && propertyInfo . ShouldApplyNullablePropertySchema ( ) )
470465 {
471- schema [ OpenApiSchemaKeywords . TypeKeyword ] = ( schemaTypes | JsonSchemaType . Null ) . ToString ( ) ;
466+ schema [ OpenApiConstants . NullableProperty ] = true ;
472467 }
473468 }
474469
475470 /// <summary>
476- /// Support applying nullability status for reference types provided as a property or field.
471+ /// Prunes the "null" type from the schema for types that are componentized. These
472+ /// types should represent their nullability using oneOf with null instead.
477473 /// </summary>
478474 /// <param name="schema">The <see cref="JsonNode"/> produced by the underlying schema generator.</param>
479- /// <param name="propertyInfo">The <see cref="JsonPropertyInfo" /> associated with the schema.</param>
480- internal static void ApplyNullabilityContextInfo ( this JsonNode schema , JsonPropertyInfo propertyInfo )
475+ internal static void PruneNullTypeForComponentizedTypes ( this JsonNode schema )
481476 {
482- // Avoid setting explicit nullability annotations for `object` types so they continue to match on the catch
483- // all schema (no type, no format, no constraints).
484- if ( propertyInfo . PropertyType != typeof ( object ) && ( propertyInfo . IsGetNullable || propertyInfo . IsSetNullable ) )
477+ if ( schema [ OpenApiConstants . SchemaId ] is not null &&
478+ schema [ OpenApiSchemaKeywords . TypeKeyword ] is JsonArray typeArray )
485479 {
486- if ( MapJsonNodeToSchemaType ( schema [ OpenApiSchemaKeywords . TypeKeyword ] ) is { } schemaTypes &&
487- ! schemaTypes . HasFlag ( JsonSchemaType . Null ) )
480+ for ( var i = typeArray . Count - 1 ; i >= 0 ; i -- )
488481 {
489- schema [ OpenApiSchemaKeywords . TypeKeyword ] = ( schemaTypes | JsonSchemaType . Null ) . ToString ( ) ;
482+ if ( typeArray [ i ] ? . GetValue < string > ( ) == "null" )
483+ {
484+ typeArray . RemoveAt ( i ) ;
485+ }
486+ }
487+ if ( typeArray . Count == 1 )
488+ {
489+ schema [ OpenApiSchemaKeywords . TypeKeyword ] = typeArray [ 0 ] ? . GetValue < string > ( ) ;
490490 }
491491 }
492492 }
0 commit comments