diff --git a/src/NJsonSchema.Tests/Generation/AttributeGenerationTests.cs b/src/NJsonSchema.Tests/Generation/AttributeGenerationTests.cs index de7547f7d..2e48d0cfa 100644 --- a/src/NJsonSchema.Tests/Generation/AttributeGenerationTests.cs +++ b/src/NJsonSchema.Tests/Generation/AttributeGenerationTests.cs @@ -378,6 +378,20 @@ public void When_property_has_required_keyword_and_nullable_type_then_it_is_requ Assert.True(schema.Properties["Name"].IsNullable(SchemaType.JsonSchema)); Assert.Null(schema.Properties["Name"].MinLength); } + + [Fact] + public void When_property_has_required_keyword_and_nullable_type_then_it_is_nullable_in_Swagger2() + { + // Act + var schema = NewtonsoftJsonSchemaGenerator.FromType( + new NewtonsoftJsonSchemaGeneratorSettings { SchemaType = SchemaType.Swagger2 }); + + // Assert: the required keyword forces the nullable property into Swagger2's required + // array, which by itself would signal "non-null". x-nullable must be emitted so the + // declared nullability is not silently lost. + Assert.Contains("Name", schema.RequiredProperties); + Assert.True(schema.Properties["Name"].IsNullable(SchemaType.Swagger2)); + } #endif #if NET6_0_OR_GREATER diff --git a/src/NJsonSchema.Tests/Generation/DataContractTests.cs b/src/NJsonSchema.Tests/Generation/DataContractTests.cs index f56dbec29..d307dae6a 100644 --- a/src/NJsonSchema.Tests/Generation/DataContractTests.cs +++ b/src/NJsonSchema.Tests/Generation/DataContractTests.cs @@ -41,7 +41,7 @@ public void When_DataContractRequired_is_set_property_is_nullable_in_OpenApi3() } [Fact] - public void When_DataContractRequired_is_set_property_is_not_nullable_in_Swagger2() + public void When_DataContractRequired_is_set_property_is_nullable_in_Swagger2() { // Act var schema = NewtonsoftJsonSchemaGenerator.FromType(new NewtonsoftJsonSchemaGeneratorSettings { SchemaType = SchemaType.Swagger2 }); @@ -49,11 +49,11 @@ public void When_DataContractRequired_is_set_property_is_not_nullable_in_Swagger // Assert Assert.True(schema.ActualProperties["middleName"].IsRequired); - Assert.False(schema.ActualProperties["middleName"].IsNullable(SchemaType.Swagger2)); + Assert.True(schema.ActualProperties["middleName"].IsNullable(SchemaType.Swagger2)); - // not nullable, because Swagger2 does not know null but it has to set "required" - // because not setting the property would result in a serialization error, - // see When_DataContractRequired_is_set_then_undefined_is_not_allowed + // The property must be "required" because not setting it would result in a serialization + // error, but its value may still be null. Swagger2 has no native null, so x-nullable + // is emitted to express that the required value can be null. } } } diff --git a/src/NJsonSchema.Tests/Generation/SystemTextJson/SystemTextJsonTests.cs b/src/NJsonSchema.Tests/Generation/SystemTextJson/SystemTextJsonTests.cs index d31dbdbfa..dbda0d5a6 100644 --- a/src/NJsonSchema.Tests/Generation/SystemTextJson/SystemTextJsonTests.cs +++ b/src/NJsonSchema.Tests/Generation/SystemTextJson/SystemTextJsonTests.cs @@ -176,6 +176,20 @@ public void When_property_has_required_keyword_and_nullable_type_then_it_is_requ Assert.Null(schema.Properties["Name"].MinLength); } + [Fact] + public void When_property_has_required_keyword_and_nullable_type_then_it_is_nullable_in_Swagger2() + { + // Act + var schema = JsonSchema.FromType( + new SystemTextJsonSchemaGeneratorSettings { SchemaType = SchemaType.Swagger2 }); + + // Assert: the required keyword forces the nullable property into Swagger2's required + // array, which by itself would signal "non-null". x-nullable must be emitted so the + // declared nullability is not silently lost. + Assert.Contains("Name", schema.RequiredProperties); + Assert.True(schema.Properties["Name"].IsNullable(SchemaType.Swagger2)); + } + public class ClassWithJsonRequired { [System.Text.Json.Serialization.JsonRequired] diff --git a/src/NJsonSchema/Generation/JsonSchemaGenerator.cs b/src/NJsonSchema/Generation/JsonSchemaGenerator.cs index ff4d7504d..5f9a17f7e 100644 --- a/src/NJsonSchema/Generation/JsonSchemaGenerator.cs +++ b/src/NJsonSchema/Generation/JsonSchemaGenerator.cs @@ -1222,9 +1222,17 @@ public void AddProperty( propertySchema.MinLength = 1; } - if (!isNullable && Settings.SchemaType == SchemaType.Swagger2) + if (Settings.SchemaType == SchemaType.Swagger2) { - if (!parentSchema.RequiredProperties.Contains(propertyName)) + if (parentSchema.RequiredProperties.Contains(propertyName)) + { + if (isNullable) + { + // Swagger2 has no native nullable + propertySchema.IsNullableRaw = true; + } + } + else if (!isNullable) { parentSchema.RequiredProperties.Add(propertyName); }