@@ -112,6 +112,8 @@ public string? ExclusiveMinimum
112112 private bool HasNullType
113113 => Type . HasValue && Type . Value . HasFlag ( JsonSchemaType . Null ) ;
114114
115+ internal bool IsNullableCompatibility { get ; set ; }
116+
115117 internal bool WasConstExplicitlySet { get ; private set ; }
116118
117119 /// <inheritdoc />
@@ -368,6 +370,7 @@ internal OpenApiSchema(IOpenApiSchema schema)
368370 {
369371 IsExclusiveMaximum = eMSchema . IsExclusiveMaximum ;
370372 IsExclusiveMinimum = eMSchema . IsExclusiveMinimum ;
373+ IsNullableCompatibility = eMSchema . IsNullableCompatibility ;
371374 }
372375 Type = schema . Type ?? Type ;
373376 Format = schema . Format ?? Format ;
@@ -436,12 +439,6 @@ private static void SerializeBounds(IOpenApiWriter writer, OpenApiSpecVersion ve
436439 writer . WritePropertyName ( exclusivePropertyName ) ;
437440 writer . WriteRaw ( exclusiveValue ) ;
438441 }
439- else if ( isExclusiveValue == true && ! string . IsNullOrEmpty ( value ) && value is not null )
440- {
441- // came from parsing an old document
442- writer . WritePropertyName ( exclusivePropertyName ) ;
443- writer . WriteRaw ( value ) ;
444- }
445442 else if ( ! string . IsNullOrEmpty ( value ) && value is not null )
446443 {
447444 // was explicitly set in the document or object model
@@ -451,14 +448,7 @@ private static void SerializeBounds(IOpenApiWriter writer, OpenApiSpecVersion ve
451448 }
452449 else
453450 {
454- if ( ! string . IsNullOrEmpty ( exclusiveValue ) && exclusiveValue is not null )
455- {
456- // was explicitly set in a new document being downcast or object model
457- writer . WritePropertyName ( propertyName ) ;
458- writer . WriteRaw ( exclusiveValue ) ;
459- writer . WriteProperty ( isExclusivePropertyName , true ) ;
460- }
461- else if ( ! string . IsNullOrEmpty ( value ) && value is not null )
451+ if ( ! string . IsNullOrEmpty ( value ) && value is not null )
462452 {
463453 // came from parsing an old document, we're just mirroring the information
464454 writer . WritePropertyName ( propertyName ) ;
@@ -487,11 +477,11 @@ private void SerializeInternal(IOpenApiWriter writer, OpenApiSpecVersion version
487477
488478 // maximum
489479 // exclusiveMaximum
490- SerializeBounds ( writer , version , OpenApiConstants . Maximum , OpenApiConstants . ExclusiveMaximum , OpenApiConstants . V31ExclusiveMaximum , Maximum , ExclusiveMaximum , IsExclusiveMaximum ) ;
480+ SerializeBounds ( writer , version , OpenApiConstants . Maximum , OpenApiConstants . ExclusiveMaximum , OpenApiConstants . V31ExclusiveMaximum , _maximum , ExclusiveMaximum , IsExclusiveMaximum ) ;
491481
492482 // minimum
493483 // exclusiveMinimum
494- SerializeBounds ( writer , version , OpenApiConstants . Minimum , OpenApiConstants . ExclusiveMinimum , OpenApiConstants . V31ExclusiveMinimum , Minimum , ExclusiveMinimum , IsExclusiveMinimum ) ;
484+ SerializeBounds ( writer , version , OpenApiConstants . Minimum , OpenApiConstants . ExclusiveMinimum , OpenApiConstants . V31ExclusiveMinimum , _minimum , ExclusiveMinimum , IsExclusiveMinimum ) ;
495485
496486 // maxLength
497487 writer . WriteProperty ( OpenApiConstants . MaxLength , MaxLength ) ;
@@ -527,12 +517,7 @@ private void SerializeInternal(IOpenApiWriter writer, OpenApiSpecVersion version
527517 } ) ;
528518
529519 // enum
530- var enumValue = Enum is not { Count : > 0 }
531- && WasConstExplicitlySet
532- && version < OpenApiSpecVersion . OpenApi3_1
533- ? new List < JsonNode > { JsonValue . Create ( Const ) ! }
534- : Enum ;
535- writer . WriteOptionalCollection ( OpenApiConstants . Enum , enumValue , ( nodeWriter , s ) => nodeWriter . WriteAny ( s ) ) ;
520+ writer . WriteOptionalCollection ( OpenApiConstants . Enum , Enum , ( nodeWriter , s ) => nodeWriter . WriteAny ( s ) ) ;
536521
537522 // type
538523 TrySerializeTypeProperty ( writer , version ) ;
@@ -601,10 +586,12 @@ private void SerializeInternal(IOpenApiWriter writer, OpenApiSpecVersion version
601586 writer . WriteOptionalObject ( OpenApiConstants . ExternalDocs , ExternalDocs , callback ) ;
602587
603588 // example
589+ #pragma warning disable CS0618
604590 writer . WriteOptionalObject (
605591 OpenApiConstants . Example ,
606- version < OpenApiSpecVersion . OpenApi3_1 ? GetCompatibilityExample ( ) : Example ,
592+ Example ,
607593 ( w , e ) => w . WriteAny ( e ) ) ;
594+ #pragma warning restore CS0618
608595
609596 // deprecated
610597 writer . WriteProperty ( OpenApiConstants . Deprecated , Deprecated , false ) ;
@@ -730,7 +717,6 @@ private void WriteV3CompatibilityKeywords(IOpenApiWriter writer, Action<IOpenApi
730717 writer . WriteOptionalObject ( OpenApiConstants . IfExtension , If , callback ) ;
731718 writer . WriteOptionalObject ( OpenApiConstants . ThenExtension , Then , callback ) ;
732719 writer . WriteOptionalObject ( OpenApiConstants . ElseExtension , Else , callback ) ;
733- writer . WriteOptionalCollection ( OpenApiConstants . JsonSchemaExamplesExtension , GetCompatibilityExamplesExtension ( ) , ( nodeWriter , s ) => nodeWriter . WriteAny ( s ) ) ;
734720 }
735721
736722 internal void WriteAsItemsProperties ( IOpenApiWriter writer )
@@ -757,11 +743,11 @@ internal void WriteAsItemsProperties(IOpenApiWriter writer)
757743
758744 // maximum
759745 // exclusiveMaximum
760- SerializeBounds ( writer , OpenApiSpecVersion . OpenApi2_0 , OpenApiConstants . Maximum , OpenApiConstants . ExclusiveMaximum , OpenApiConstants . V31ExclusiveMaximum , Maximum , ExclusiveMaximum , IsExclusiveMaximum ) ;
746+ SerializeBounds ( writer , OpenApiSpecVersion . OpenApi2_0 , OpenApiConstants . Maximum , OpenApiConstants . ExclusiveMaximum , OpenApiConstants . V31ExclusiveMaximum , _maximum , ExclusiveMaximum , IsExclusiveMaximum ) ;
761747
762748 // minimum
763749 // exclusiveMinimum
764- SerializeBounds ( writer , OpenApiSpecVersion . OpenApi2_0 , OpenApiConstants . Minimum , OpenApiConstants . ExclusiveMinimum , OpenApiConstants . V31ExclusiveMinimum , Minimum , ExclusiveMinimum , IsExclusiveMinimum ) ;
750+ SerializeBounds ( writer , OpenApiSpecVersion . OpenApi2_0 , OpenApiConstants . Minimum , OpenApiConstants . ExclusiveMinimum , OpenApiConstants . V31ExclusiveMinimum , _minimum , ExclusiveMinimum , IsExclusiveMinimum ) ;
765751
766752 // maxLength
767753 writer . WriteProperty ( OpenApiConstants . MaxLength , MaxLength ) ;
@@ -790,15 +776,7 @@ internal void WriteAsItemsProperties(IOpenApiWriter writer)
790776
791777 private void WriteFormatProperty ( IOpenApiWriter writer )
792778 {
793- var formatToWrite = Format ;
794- if ( string . IsNullOrEmpty ( formatToWrite ) )
795- {
796- formatToWrite = AllOf ? . FirstOrDefault ( static x => ! string . IsNullOrEmpty ( x . Format ) ) ? . Format ??
797- AnyOf ? . FirstOrDefault ( static x => ! string . IsNullOrEmpty ( x . Format ) ) ? . Format ??
798- OneOf ? . FirstOrDefault ( static x => ! string . IsNullOrEmpty ( x . Format ) ) ? . Format ;
799- }
800-
801- writer . WriteProperty ( OpenApiConstants . Format , formatToWrite ) ;
779+ writer . WriteProperty ( OpenApiConstants . Format , Format ) ;
802780 }
803781
804782 /// <summary>
@@ -837,11 +815,11 @@ private void SerializeAsV2(
837815
838816 // maximum
839817 // exclusiveMaximum
840- SerializeBounds ( writer , OpenApiSpecVersion . OpenApi2_0 , OpenApiConstants . Maximum , OpenApiConstants . ExclusiveMaximum , OpenApiConstants . V31ExclusiveMaximum , Maximum , ExclusiveMaximum , IsExclusiveMaximum ) ;
818+ SerializeBounds ( writer , OpenApiSpecVersion . OpenApi2_0 , OpenApiConstants . Maximum , OpenApiConstants . ExclusiveMaximum , OpenApiConstants . V31ExclusiveMaximum , _maximum , ExclusiveMaximum , IsExclusiveMaximum ) ;
841819
842820 // minimum
843821 // exclusiveMinimum
844- SerializeBounds ( writer , OpenApiSpecVersion . OpenApi2_0 , OpenApiConstants . Minimum , OpenApiConstants . ExclusiveMinimum , OpenApiConstants . V31ExclusiveMinimum , Minimum , ExclusiveMinimum , IsExclusiveMinimum ) ;
822+ SerializeBounds ( writer , OpenApiSpecVersion . OpenApi2_0 , OpenApiConstants . Minimum , OpenApiConstants . ExclusiveMinimum , OpenApiConstants . V31ExclusiveMinimum , _minimum , ExclusiveMinimum , IsExclusiveMinimum ) ;
845823
846824 // maxLength
847825 writer . WriteProperty ( OpenApiConstants . MaxLength , MaxLength ) ;
@@ -877,10 +855,7 @@ private void SerializeAsV2(
877855 } ) ;
878856
879857 // enum
880- var enumValue = Enum is not { Count : > 0 } && WasConstExplicitlySet
881- ? new List < JsonNode > { JsonValue . Create ( Const ) ! }
882- : Enum ;
883- writer . WriteOptionalCollection ( OpenApiConstants . Enum , enumValue , ( nodeWriter , s ) => nodeWriter . WriteAny ( s ) ) ;
858+ writer . WriteOptionalCollection ( OpenApiConstants . Enum , Enum , ( nodeWriter , s ) => nodeWriter . WriteAny ( s ) ) ;
884859
885860 // items
886861 writer . WriteOptionalObject ( OpenApiConstants . Items , Items , ( w , s ) => s . SerializeAsV2 ( w ) ) ;
@@ -945,7 +920,6 @@ private void SerializeAsV2(
945920
946921 // example
947922#pragma warning disable CS0618
948- writer . WriteOptionalObject ( OpenApiConstants . Example , GetCompatibilityExample ( ) , ( w , e ) => w . WriteAny ( e ) ) ;
949923 writer . WriteOptionalObject ( OpenApiConstants . Example , Example , ( w , e ) => w . WriteAny ( e ) ) ;
950924#pragma warning restore CS0618
951925
@@ -978,7 +952,6 @@ private void SerializeAsV2(
978952 writer . WriteOptionalMap ( OpenApiConstants . PatternPropertiesExtension , PatternProperties , ( w , s ) => s . SerializeAsV2 ( w ) ) ;
979953 }
980954
981- writer . WriteOptionalCollection ( OpenApiConstants . JsonSchemaExamplesExtension , GetCompatibilityExamplesExtension ( ) , ( nodeWriter , s ) => nodeWriter . WriteAny ( s ) ) ;
982955
983956 // extensions
984957 writer . WriteExtensions ( Extensions , OpenApiSpecVersion . OpenApi2_0 ) ;
@@ -999,10 +972,19 @@ private bool TrySerializeTypeProperty(IOpenApiWriter writer, OpenApiSpecVersion
999972 switch ( version )
1000973 {
1001974 case OpenApiSpecVersion . OpenApi2_0 or OpenApiSpecVersion . OpenApi3_0 :
1002- var typeWithoutNull = typeToUse . Value & ~ JsonSchemaType . Null ;
1003- if ( typeWithoutNull != 0 && ! HasMultipleTypes ( typeWithoutNull ) )
975+ if ( typeToUse . Value . HasFlag ( JsonSchemaType . Null ) )
976+ {
977+ if ( ! IsNullableCompatibility )
978+ {
979+ return false ;
980+ }
981+
982+ typeToUse &= ~ JsonSchemaType . Null ;
983+ }
984+
985+ if ( typeToUse != 0 && ! HasMultipleTypes ( typeToUse . Value ) )
1004986 {
1005- writer . WriteProperty ( OpenApiConstants . Type , typeWithoutNull . ToFirstIdentifier ( ) ) ;
987+ writer . WriteProperty ( OpenApiConstants . Type , typeToUse . Value . ToFirstIdentifier ( ) ) ;
1006988 return true ;
1007989 }
1008990 break ;
@@ -1014,30 +996,6 @@ private bool TrySerializeTypeProperty(IOpenApiWriter writer, OpenApiSpecVersion
1014996 return false ;
1015997 }
1016998
1017- private JsonNode ? GetCompatibilityExample ( )
1018- {
1019- #pragma warning disable CS0618 // Type or member is obsolete
1020- return Example ?? Examples ? . FirstOrDefault ( ) ;
1021- #pragma warning restore CS0618 // Type or member is obsolete
1022- }
1023-
1024- private IEnumerable < JsonNode > ? GetCompatibilityExamplesExtension ( )
1025- {
1026- if ( Examples is null || Examples . Count == 0 )
1027- {
1028- return null ;
1029- }
1030-
1031- #pragma warning disable CS0618 // Type or member is obsolete
1032- if ( Example is not null )
1033- {
1034- return Examples ;
1035- }
1036- #pragma warning restore CS0618 // Type or member is obsolete
1037-
1038- return Examples . Count > 1 ? Examples . Skip ( 1 ) : null ;
1039- }
1040-
1041999 private static bool IsPowerOfTwo ( int x )
10421000 {
10431001 return x != 0 && ( x & ( x - 1 ) ) == 0 ;
@@ -1072,7 +1030,7 @@ where type.HasFlag(flag)
10721030
10731031 private void SerializeNullable ( IOpenApiWriter writer , OpenApiSpecVersion version )
10741032 {
1075- if ( HasNullType )
1033+ if ( IsNullableCompatibility )
10761034 {
10771035 switch ( version )
10781036 {
@@ -1086,6 +1044,62 @@ private void SerializeNullable(IOpenApiWriter writer, OpenApiSpecVersion version
10861044 }
10871045 }
10881046
1047+ internal void ConvertNullTypeToNullableCompatibility ( )
1048+ {
1049+ if ( Type is not { } type || ! type . HasFlag ( JsonSchemaType . Null ) )
1050+ {
1051+ return ;
1052+ }
1053+
1054+ var typeWithoutNull = type & ~ JsonSchemaType . Null ;
1055+ if ( typeWithoutNull != 0 )
1056+ {
1057+ Type = typeWithoutNull ;
1058+ IsNullableCompatibility = true ;
1059+ }
1060+ }
1061+
1062+ internal void ConvertNullableCompatibilityToNullType ( )
1063+ {
1064+ if ( IsNullableCompatibility && Type is not null and not 0 )
1065+ {
1066+ Type |= JsonSchemaType . Null ;
1067+ IsNullableCompatibility = false ;
1068+ }
1069+ }
1070+
1071+ internal void ConvertExclusiveBoundsToCompatibilityBooleans ( )
1072+ {
1073+ if ( ! string . IsNullOrEmpty ( _exclusiveMaximum ) )
1074+ {
1075+ _maximum = _exclusiveMaximum ;
1076+ _exclusiveMaximum = null ;
1077+ IsExclusiveMaximum = true ;
1078+ }
1079+
1080+ if ( ! string . IsNullOrEmpty ( _exclusiveMinimum ) )
1081+ {
1082+ _minimum = _exclusiveMinimum ;
1083+ _exclusiveMinimum = null ;
1084+ IsExclusiveMinimum = true ;
1085+ }
1086+ }
1087+
1088+ internal void ConvertCompatibilityBooleansToExclusiveBounds ( )
1089+ {
1090+ if ( IsExclusiveMaximum == true && ! string . IsNullOrEmpty ( _maximum ) )
1091+ {
1092+ _exclusiveMaximum = _maximum ;
1093+ _maximum = null ;
1094+ }
1095+
1096+ if ( IsExclusiveMinimum == true && ! string . IsNullOrEmpty ( _minimum ) )
1097+ {
1098+ _exclusiveMinimum = _minimum ;
1099+ _minimum = null ;
1100+ }
1101+ }
1102+
10891103#if NET5_0_OR_GREATER
10901104 private static readonly Array jsonSchemaTypeValues = System . Enum . GetValues < JsonSchemaType > ( ) ;
10911105#else
0 commit comments