Skip to content

Commit 10c2c39

Browse files
authored
Merge pull request #2631 from microsoft/fix/additional-properties-to-v3
fix/additional properties to v3
2 parents 5ebf479 + 8745983 commit 10c2c39

File tree

3 files changed

+121
-13
lines changed

3 files changed

+121
-13
lines changed

CHANGELOG.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,13 @@
22

33
## [3.0.1](https://github.com/microsoft/OpenAPI.NET/compare/v3.0.0...v3.0.1) (2025-11-17)
44

5-
65
### Bug Fixes
76

87
* empty strings should be quoted in yaml ([8d215f9](https://github.com/microsoft/OpenAPI.NET/commit/8d215f9ea8a780b1e2e8dd6cefb8d470cc35682d))
98
* empty strings should be quoted in yaml ([0ca10db](https://github.com/microsoft/OpenAPI.NET/commit/0ca10db3bb9ffa937dd35862068926f3586d6991))
109

1110
## [3.0.0](https://github.com/microsoft/OpenAPI.NET/compare/v2.3.9...v3.0.0) (2025-11-11)
1211

13-
1412
### ⚠ BREAKING CHANGES
1513

1614
* adds support for OpenAPI 3.2.0
@@ -23,8 +21,12 @@
2321

2422
* adds support for OpenAPI 3.2.0 ([765a8dd](https://github.com/microsoft/OpenAPI.NET/commit/765a8dd4d6efd1a31b6a76d282ccffa5877a845a))
2523

26-
## [2.3.9](https://github.com/microsoft/OpenAPI.NET/compare/v2.3.8...v2.3.9) (2025-11-06)
24+
## [2.3.10](https://github.com/microsoft/OpenAPI.NET/compare/v2.3.9...v2.3.10) (2025-11-17)
2725

26+
* empty strings should be quoted in yaml ([e919b33](https://github.com/microsoft/OpenAPI.NET/commit/e919b33e9d09159217066248483ef4c767865c82))
27+
* empty strings should be quoted in yaml ([0ca10db](https://github.com/microsoft/OpenAPI.NET/commit/0ca10db3bb9ffa937dd35862068926f3586d6991))
28+
29+
## [2.3.9](https://github.com/microsoft/OpenAPI.NET/compare/v2.3.8...v2.3.9) (2025-11-06)
2830

2931
### Bug Fixes
3032

src/Microsoft.OpenApi/Models/OpenApiSchema.cs

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -471,17 +471,20 @@ private void SerializeInternal(IOpenApiWriter writer, OpenApiSpecVersion version
471471
writer.WriteOptionalMap(OpenApiConstants.Properties, Properties, callback);
472472

473473
// additionalProperties
474-
if (AdditionalPropertiesAllowed)
474+
if (AdditionalProperties is not null && version >= OpenApiSpecVersion.OpenApi3_0)
475475
{
476476
writer.WriteOptionalObject(
477477
OpenApiConstants.AdditionalProperties,
478478
AdditionalProperties,
479479
callback);
480480
}
481-
else
481+
// true is the default in earlier versions 3, no need to write it out
482+
// boolean value is only supported for version 3 and earlier (version 2 is implemented in the other serialize method, the condition is a failsafe)
483+
else if (!AdditionalPropertiesAllowed && version <= OpenApiSpecVersion.OpenApi3_0)
482484
{
483485
writer.WriteProperty(OpenApiConstants.AdditionalProperties, AdditionalPropertiesAllowed);
484486
}
487+
// not having anything is the same as having it set to true (v2/v3) or an empty schema (v3.1+)
485488

486489
// description
487490
writer.WriteProperty(OpenApiConstants.Description, Description);
@@ -732,14 +735,9 @@ private void SerializeAsV2(
732735
});
733736

734737
// additionalProperties
735-
if (AdditionalPropertiesAllowed)
736-
{
737-
writer.WriteOptionalObject(
738-
OpenApiConstants.AdditionalProperties,
739-
AdditionalProperties,
740-
(w, s) => s.SerializeAsV2(w));
741-
}
742-
else
738+
// a schema cannot be serialized in v2
739+
// true is the default, no need to write it out
740+
if (!AdditionalPropertiesAllowed)
743741
{
744742
writer.WriteProperty(OpenApiConstants.AdditionalProperties, AdditionalPropertiesAllowed);
745743
}

test/Microsoft.OpenApi.Tests/Models/OpenApiSchemaTests.cs

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -684,6 +684,114 @@ public async Task SerializeConstAsEnumV20()
684684
Assert.False(v2Node.AsObject().ContainsKey("const"));
685685
}
686686

687+
[Fact]
688+
public async Task SerializeAdditionalPropertiesAsV2DoesNotEmit()
689+
{
690+
var expected = @"{ }";
691+
// Given
692+
var schema = new OpenApiSchema
693+
{
694+
AdditionalProperties = new OpenApiSchema()
695+
};
696+
697+
// When
698+
var actual = await schema.SerializeAsJsonAsync(OpenApiSpecVersion.OpenApi2_0);
699+
700+
// Then
701+
Assert.True(JsonNode.DeepEquals(JsonNode.Parse(expected), JsonNode.Parse(actual)));
702+
}
703+
704+
[Fact]
705+
public async Task SerializeAdditionalPropertiesAllowedAsV2DefaultDoesNotEmit()
706+
{
707+
var expected = @"{ }";
708+
// Given
709+
var schema = new OpenApiSchema
710+
{
711+
AdditionalPropertiesAllowed = true
712+
};
713+
714+
// When
715+
var actual = await schema.SerializeAsJsonAsync(OpenApiSpecVersion.OpenApi2_0);
716+
717+
// Then
718+
Assert.True(JsonNode.DeepEquals(JsonNode.Parse(expected), JsonNode.Parse(actual)));
719+
}
720+
721+
[Fact]
722+
public async Task SerializeAdditionalPropertiesAllowedAsV2FalseEmits()
723+
{
724+
var expected = @"{ ""additionalProperties"": false }";
725+
// Given
726+
var schema = new OpenApiSchema
727+
{
728+
AdditionalPropertiesAllowed = false
729+
};
730+
731+
// When
732+
var actual = await schema.SerializeAsJsonAsync(OpenApiSpecVersion.OpenApi2_0);
733+
734+
// Then
735+
Assert.True(JsonNode.DeepEquals(JsonNode.Parse(expected), JsonNode.Parse(actual)));
736+
}
737+
738+
[Theory]
739+
[InlineData(OpenApiSpecVersion.OpenApi3_0)]
740+
[InlineData(OpenApiSpecVersion.OpenApi3_1)]
741+
[InlineData(OpenApiSpecVersion.OpenApi3_2)]
742+
public async Task SerializeAdditionalPropertiesAllowedAsV3PlusDefaultDoesNotEmit(OpenApiSpecVersion version)
743+
{
744+
var expected = @"{ }";
745+
// Given
746+
var schema = new OpenApiSchema
747+
{
748+
AdditionalPropertiesAllowed = true
749+
};
750+
751+
// When
752+
var actual = await schema.SerializeAsJsonAsync(version);
753+
754+
// Then
755+
Assert.True(JsonNode.DeepEquals(JsonNode.Parse(expected), JsonNode.Parse(actual)));
756+
}
757+
758+
[Fact]
759+
public async Task SerializeAdditionalPropertiesAllowedAsV3FalseEmits()
760+
{
761+
var expected = @"{ ""additionalProperties"": false }";
762+
// Given
763+
var schema = new OpenApiSchema
764+
{
765+
AdditionalPropertiesAllowed = false
766+
};
767+
768+
// When
769+
var actual = await schema.SerializeAsJsonAsync(OpenApiSpecVersion.OpenApi3_0);
770+
771+
// Then
772+
Assert.True(JsonNode.DeepEquals(JsonNode.Parse(expected), JsonNode.Parse(actual)));
773+
}
774+
775+
[Theory]
776+
[InlineData(OpenApiSpecVersion.OpenApi3_0)]
777+
[InlineData(OpenApiSpecVersion.OpenApi3_1)]
778+
[InlineData(OpenApiSpecVersion.OpenApi3_2)]
779+
public async Task SerializeAdditionalPropertiesAsV3PlusEmits(OpenApiSpecVersion version)
780+
{
781+
var expected = @"{ ""additionalProperties"": { } }";
782+
// Given
783+
var schema = new OpenApiSchema
784+
{
785+
AdditionalProperties = new OpenApiSchema()
786+
};
787+
788+
// When
789+
var actual = await schema.SerializeAsJsonAsync(version);
790+
791+
// Then
792+
Assert.True(JsonNode.DeepEquals(JsonNode.Parse(expected), JsonNode.Parse(actual)));
793+
}
794+
687795

688796
internal class SchemaVisitor : OpenApiVisitorBase
689797
{

0 commit comments

Comments
 (0)