Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
2e07dc2
chore(deps): bump github/codeql-action from 3 to 4
dependabot[bot] Oct 13, 2025
c213751
Bump PublicApiGenerator from 11.4.6 to 11.5.0
dependabot[bot] Oct 13, 2025
534594a
Merge pull request #2536 from microsoft/dependabot/nuget/test/Microso…
github-actions[bot] Oct 14, 2025
9b11f2e
Merge pull request #2535 from microsoft/dependabot/github_actions/git…
baywet Oct 14, 2025
8b91278
fix: use settings for terse output in serialization extension methods
baywet Oct 14, 2025
d50b393
tests: adds unit tests to validate terse from settings is being used
baywet Oct 14, 2025
6082b4e
chore: formatting
baywet Oct 14, 2025
246039b
Merge pull request #2537 from microsoft/fix/terse-output-extensions
baywet Oct 14, 2025
afd088c
chore(main): release 2.3.5
release-please-token-provider[bot] Oct 14, 2025
3a837b6
Merge pull request #2538 from microsoft/release-please--branches--mai…
baywet Oct 14, 2025
7c918a4
Bump Microsoft.Extensions.Logging.Abstractions from 9.0.9 to 9.0.10
dependabot[bot] Oct 14, 2025
ca37cbd
Bump Microsoft.Windows.Compatibility from 9.0.9 to 9.0.10
dependabot[bot] Oct 14, 2025
ace9388
Bump Microsoft.Extensions.DependencyInjection from 9.0.9 to 9.0.10
dependabot[bot] Oct 14, 2025
085f33e
Merge pull request #2545 from microsoft/dependabot/nuget/performance/…
github-actions[bot] Oct 15, 2025
7201159
Merge pull request #2544 from microsoft/dependabot/nuget/src/Microsof…
github-actions[bot] Oct 15, 2025
724f51c
Merge pull request #2541 from microsoft/dependabot/nuget/src/Microsof…
github-actions[bot] Oct 15, 2025
bd88e40
Bump Microsoft.Extensions.Logging and Microsoft.Extensions.Logging.Debug
dependabot[bot] Oct 15, 2025
87be345
chore(deps): bump dotnet-sdk from 8.0.414 to 8.0.415
dependabot[bot] Oct 15, 2025
1d34cf8
Merge pull request #2548 from microsoft/dependabot/nuget/performance/…
github-actions[bot] Oct 15, 2025
ac47553
Merge pull request #2549 from microsoft/dependabot/dotnet_sdk/dotnet-…
github-actions[bot] Oct 15, 2025
ec5ca47
Bump Microsoft.Extensions.Logging.Console and System.Text.Json
dependabot[bot] Oct 15, 2025
904c8de
chore: updates xunit deps
baywet Oct 15, 2025
bdaa23c
chore: updates STJ deps
baywet Oct 15, 2025
4f0554d
chore: updates verify deps
baywet Oct 15, 2025
5fa053a
Merge pull request #2551 from microsoft/dependabot/nuget/performance/…
github-actions[bot] Oct 16, 2025
7d7856d
tests: adds a unit test to validate empty defaults are serialized
baywet Oct 20, 2025
4c4d257
fix: a bug where empty collections would not be serialized for defaul…
baywet Oct 20, 2025
f702dbc
Merge pull request #2556 from microsoft/fix/empty-default
baywet Oct 20, 2025
e79e4ca
chore(main): release 2.3.6
release-please-token-provider[bot] Oct 20, 2025
a7f0182
Merge pull request #2557 from microsoft/release-please--branches--mai…
baywet Oct 20, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/codeql-analysis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ jobs:

- name: Initialize CodeQL
id: init_codeql
uses: github/codeql-action/init@v3
uses: github/codeql-action/init@v4
with:
queries: security-and-quality

Expand All @@ -49,6 +49,6 @@ jobs:

- name: Perform CodeQL Analysis
id: analyze_codeql
uses: github/codeql-action/analyze@v3
uses: github/codeql-action/analyze@v4

# Built with ❤ by [Pipeline Foundation](https://pipeline.foundation)
2 changes: 1 addition & 1 deletion .release-please-manifest.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
".": "2.3.4"
".": "2.3.6"
}
15 changes: 15 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,20 @@
# Changelog

## [2.3.6](https://github.com/microsoft/OpenAPI.NET/compare/v2.3.5...v2.3.6) (2025-10-20)


### Bug Fixes

* a bug where empty collections would not be serialized for default values ([4c4d257](https://github.com/microsoft/OpenAPI.NET/commit/4c4d257c0cf10d1742fae9f3961e4a6242c0ce1d))

## [2.3.5](https://github.com/microsoft/OpenAPI.NET/compare/v2.3.4...v2.3.5) (2025-10-14)


### Bug Fixes

* use settings for terse output in serialization extension methods ([246039b](https://github.com/microsoft/OpenAPI.NET/commit/246039bfa8a16c042a10a87126289de82d18b321))
* use settings for terse output in serialization extension methods ([8b91278](https://github.com/microsoft/OpenAPI.NET/commit/8b912788ef18b44a083d3fd2a1d6e25c9e6e17cb))

## [2.3.4](https://github.com/microsoft/OpenAPI.NET/compare/v2.3.3...v2.3.4) (2025-10-06)


Expand Down
2 changes: 1 addition & 1 deletion Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
<PackageProjectUrl>https://github.com/Microsoft/OpenAPI.NET</PackageProjectUrl>
<Copyright>© Microsoft Corporation. All rights reserved.</Copyright>
<PackageTags>OpenAPI .NET</PackageTags>
<Version>2.3.4</Version>
<Version>2.3.6</Version>
</PropertyGroup>
<!-- https://github.com/clairernovotny/DeterministicBuilds#deterministic-builds -->
<PropertyGroup Condition="'$(TF_BUILD)' == 'true'">
Expand Down
2 changes: 1 addition & 1 deletion global.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"sdk": {
"version": "8.0.414"
"version": "8.0.415"
}
}
10 changes: 5 additions & 5 deletions performance/resultsComparer/resultsComparer.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.9" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="9.0.9" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="9.0.9" />
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="9.0.9" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.10" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="9.0.10" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="9.0.10" />
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="9.0.10" />
<PackageReference Include="System.CommandLine" Version="2.0.0-beta5.25306.1" />
<PackageReference Include="system.text.json" Version="9.0.9" />
<PackageReference Include="system.text.json" Version="9.0.10" />
</ItemGroup>

</Project>
8 changes: 4 additions & 4 deletions src/Microsoft.OpenApi.Hidi/Microsoft.OpenApi.Hidi.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,10 @@

<ItemGroup>
<PackageReference Include="Humanizer.Core" Version="2.14.1" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="9.0.9" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="9.0.9" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="9.0.9" />
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="9.0.9" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="9.0.10" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="9.0.10" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="9.0.10" />
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="9.0.10" />
<PackageReference Include="Microsoft.VisualStudio.Threading.Analyzers" Version="17.14.15">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
Expand Down
7 changes: 4 additions & 3 deletions src/Microsoft.OpenApi.Hidi/OpenApiService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -192,16 +192,17 @@ private static async Task WriteOpenApiAsync(HidiOptions options, string openApiF
using var outputStream = options.Output.Create();
using var textWriter = new StreamWriter(outputStream);

var settings = new OpenApiWriterSettings
var settings = new OpenApiJsonWriterSettings
{
InlineLocalReferences = options.InlineLocal,
InlineExternalReferences = options.InlineExternal
InlineExternalReferences = options.InlineExternal,
Terse = options.TerseOutput
};
#pragma warning disable CA1308
IOpenApiWriter writer = openApiFormat.ToLowerInvariant() switch
#pragma warning restore CA1308
{
OpenApiConstants.Json => options.TerseOutput ? new(textWriter, settings, options.TerseOutput) : new OpenApiJsonWriter(textWriter, settings, false),
OpenApiConstants.Json => new OpenApiJsonWriter(textWriter, settings),
OpenApiConstants.Yaml => new OpenApiYamlWriter(textWriter, settings),
_ => throw new ArgumentException("Unknown format"),
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Microsoft.Windows.Compatibility" Version="9.0.9" />
<PackageReference Include="Microsoft.Windows.Compatibility" Version="9.0.10" />
</ItemGroup>
<ItemGroup>
<Resource Include="Themes\Metro\HowToApplyTheme.txt" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,8 @@ public static Task SerializeAsync<T>(

IOpenApiWriter writer = format.ToLowerInvariant() switch
{
OpenApiConstants.Json => new OpenApiJsonWriter(streamWriter, settings, false),
OpenApiConstants.Json when settings is OpenApiJsonWriterSettings jsonSettings => new OpenApiJsonWriter(streamWriter, jsonSettings),
OpenApiConstants.Json => new OpenApiJsonWriter(streamWriter, settings),
OpenApiConstants.Yaml => new OpenApiYamlWriter(streamWriter, settings),
_ => throw new OpenApiException(string.Format(SRResource.OpenApiFormatNotSupported, format)),
};
Expand Down
20 changes: 18 additions & 2 deletions src/Microsoft.OpenApi/Writers/OpenApiJsonWriter.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.

using System;
using System.ComponentModel;
using System.IO;

namespace Microsoft.OpenApi
Expand All @@ -14,7 +16,17 @@ public class OpenApiJsonWriter : OpenApiWriterBase
/// Initializes a new instance of the <see cref="OpenApiJsonWriter"/> class.
/// </summary>
/// <param name="textWriter">The text writer.</param>
public OpenApiJsonWriter(TextWriter textWriter) : base(textWriter, null)
public OpenApiJsonWriter(TextWriter textWriter) : this(textWriter, (OpenApiWriterSettings?)null)
{
// this constructor is kept for binary compatibility
// TODO remove in next major version and make the settings an optional parameter in the other constructor
}
/// <summary>
/// Initializes a new instance of the <see cref="OpenApiJsonWriter"/> class.
/// </summary>
/// <param name="settings">Settings for controlling how the OpenAPI document will be written out.</param>
/// <param name="textWriter">The text writer.</param>
public OpenApiJsonWriter(TextWriter textWriter, OpenApiWriterSettings? settings) : base(textWriter, settings ?? new OpenApiJsonWriterSettings())
{
}

Expand All @@ -34,9 +46,13 @@ public OpenApiJsonWriter(TextWriter textWriter, OpenApiJsonWriterSettings settin
/// <param name="textWriter">The text writer.</param>
/// <param name="settings">Settings for controlling how the OpenAPI document will be written out.</param>
/// <param name="terseOutput"> Setting for allowing the JSON emitted to be in terse format.</param>
public OpenApiJsonWriter(TextWriter textWriter, OpenApiWriterSettings? settings, bool terseOutput = false) : base(textWriter, settings)
[Obsolete("Use OpenApiJsonWriter(TextWriter textWriter, OpenApiJsonWriterSettings settings) instead.")]
[EditorBrowsable(EditorBrowsableState.Never)]
public OpenApiJsonWriter(TextWriter textWriter, OpenApiWriterSettings? settings, bool terseOutput) : base(textWriter, settings)
{
_produceTerseOutput = terseOutput;
// this constructor is kept for binary compatibility, terse information should be read from the settings to avoid fork APIs.
// TODO remove in next major version
}

/// <summary>
Expand Down
5 changes: 1 addition & 4 deletions src/Microsoft.OpenApi/Writers/OpenApiWriterBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,7 @@ protected OpenApiWriterBase(TextWriter textWriter, OpenApiWriterSettings? settin
Writer.NewLine = "\n";

Scopes = new();
if (settings == null)
{
settings = new();
}
settings ??= new();
Settings = settings;
}

Expand Down
4 changes: 2 additions & 2 deletions src/Microsoft.OpenApi/Writers/OpenApiWriterExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -141,9 +141,9 @@ public static void WriteOptionalObject<T>(
{
if (value != null)
{
if (value is IEnumerable values && !values.GetEnumerator().MoveNext())
if (value is IEnumerable values && value is not JsonArray && !values.GetEnumerator().MoveNext())
{
return; // Don't render optional empty collections
return; // Don't render optional empty collections except for the Default properties which are JsonArray
}

writer.WriteRequiredObject(name, value, action);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.0.0" />
<PackageReference Include="Moq" Version="4.20.72" />
<PackageReference Include="xunit" Version="2.9.3" />
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.4" PrivateAssets="all" />
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.5" PrivateAssets="all" />
<PackageReference Include="coverlet.collector" Version="6.0.4" PrivateAssets="all" />
</ItemGroup>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@
<PackageReference Include="coverlet.msbuild" Version="6.0.4" PrivateAssets="all" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.0.0" />
<PackageReference Include="FluentAssertions" Version="7.2.0" />
<PackageReference Include="system.text.json" Version="9.0.9" />
<PackageReference Include="system.text.json" Version="9.0.10" />
<PackageReference Include="xunit" Version="2.9.3" />
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.4" PrivateAssets="all" />
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.5" PrivateAssets="all" />
</ItemGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,40 @@ public void CloningSchemaWithExamplesAndEnumsShouldSucceed()
Assert.Equivalent(6, clone.Default.GetValue<int>());
}

[Fact]
public void DefaultEmptyCollectionShouldRoundTrip()
{
// Given
var serializedSchema =
"""
{
"type": "array",
"items": {
"type": "string",
"default": []
}
}
""";
using var textWriter = new StringWriter();
var writer = new OpenApiJsonWriter(textWriter);

// When
var schema = OpenApiModelFactory.Parse<OpenApiSchema>(serializedSchema, OpenApiSpecVersion.OpenApi3_1, new(), out _, "json", SettingsFixture.ReaderSettings);

var deserializedArray = Assert.IsType<JsonArray>(schema.Items.Default);
Assert.Empty(deserializedArray);

schema.SerializeAsV31(writer);
var roundTrippedSchema = textWriter.ToString();

// Then
var parsedResult = JsonNode.Parse(roundTrippedSchema);
var parsedExpected = JsonNode.Parse(serializedSchema);
Assert.True(JsonNode.DeepEquals(parsedExpected, parsedResult));
var resultingArray = Assert.IsType<JsonArray>(parsedResult["items"]?["default"]);
Assert.Empty(resultingArray);
}

[Fact]
public async Task SerializeV31SchemaWithMultipleTypesAsV3Works()
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
using System.IO;
using System.Threading.Tasks;
using Xunit;

namespace Microsoft.OpenApi.Tests.Extensions;

public class OpenApiSerializableExtensionsTests
{
[Fact]
public async Task UsesTheTerseOutputInformationFromSettingsTrue()
{
var parameter = new OpenApiParameter
{
Name = "param1",
In = ParameterLocation.Query,
Description = "A sample parameter",
Required = false,
Schema = new OpenApiSchema
{
Type = JsonSchemaType.String
}
};

var settings = new OpenApiJsonWriterSettings
{
Terse = true
};

using var stream = new MemoryStream();
await parameter.SerializeAsync(stream, OpenApiSpecVersion.OpenApi3_1, OpenApiConstants.Json, settings);

stream.Position = 0;
using var reader = new StreamReader(stream);
var output = await reader.ReadToEndAsync();

Assert.Equal("{\"name\":\"param1\",\"in\":\"query\",\"description\":\"A sample parameter\",\"schema\":{\"type\":\"string\"}}", output);
}

[Fact]
public async Task UsesTheTerseOutputInformationFromSettingsFalse()
{
var parameter = new OpenApiParameter
{
Name = "param1",
In = ParameterLocation.Query,
Description = "A sample parameter",
Required = false,
Schema = new OpenApiSchema
{
Type = JsonSchemaType.String
}
};

var settings = new OpenApiJsonWriterSettings
{
Terse = false
};

using var stream = new MemoryStream();
await parameter.SerializeAsync(stream, OpenApiSpecVersion.OpenApi3_1, OpenApiConstants.Json, settings);

stream.Position = 0;
using var reader = new StreamReader(stream);
var output = await reader.ReadToEndAsync();

Assert.Equal("{\n \"name\": \"param1\",\n \"in\": \"query\",\n \"description\": \"A sample parameter\",\n \"schema\": {\n \"type\": \"string\"\n }\n}", output);
}

[Fact]
public async Task UsesTheTerseOutputInformationFromSettingsNoSettings()
{
var parameter = new OpenApiParameter
{
Name = "param1",
In = ParameterLocation.Query,
Description = "A sample parameter",
Required = false,
Schema = new OpenApiSchema
{
Type = JsonSchemaType.String
}
};

using var stream = new MemoryStream();
await parameter.SerializeAsync(stream, OpenApiSpecVersion.OpenApi3_1, OpenApiConstants.Json, null);

stream.Position = 0;
using var reader = new StreamReader(stream);
var output = await reader.ReadToEndAsync();

Assert.Equal("{\n \"name\": \"param1\",\n \"in\": \"query\",\n \"description\": \"A sample parameter\",\n \"schema\": {\n \"type\": \"string\"\n }\n}", output);
}
}
8 changes: 4 additions & 4 deletions test/Microsoft.OpenApi.Tests/Microsoft.OpenApi.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,12 @@
<PackageReference Include="FluentAssertions" Version="7.2.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.0.0" />
<PackageReference Include="Moq" Version="4.20.72" />
<PackageReference Include="System.Text.Json" Version="9.0.9" />
<PackageReference Include="Verify.Xunit" Version="30.11.0" />
<PackageReference Include="System.Text.Json" Version="9.0.10" />
<PackageReference Include="Verify.Xunit" Version="31.0.1" />
<PackageReference Include="xunit" Version="2.9.3" />
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.4" PrivateAssets="all" />
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.5" PrivateAssets="all" />
<PackageReference Include="Microsoft.CSharp" Version="4.7.0" />
<PackageReference Include="PublicApiGenerator" Version="11.4.6" />
<PackageReference Include="PublicApiGenerator" Version="11.5.0" />
</ItemGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -795,7 +795,10 @@ namespace Microsoft.OpenApi
{
public OpenApiJsonWriter(System.IO.TextWriter textWriter) { }
public OpenApiJsonWriter(System.IO.TextWriter textWriter, Microsoft.OpenApi.OpenApiJsonWriterSettings settings) { }
public OpenApiJsonWriter(System.IO.TextWriter textWriter, Microsoft.OpenApi.OpenApiWriterSettings? settings, bool terseOutput = false) { }
public OpenApiJsonWriter(System.IO.TextWriter textWriter, Microsoft.OpenApi.OpenApiWriterSettings? settings) { }
[System.Obsolete("Use OpenApiJsonWriter(TextWriter textWriter, OpenApiJsonWriterSettings settings) " +
"instead.")]
public OpenApiJsonWriter(System.IO.TextWriter textWriter, Microsoft.OpenApi.OpenApiWriterSettings? settings, bool terseOutput) { }
protected override int BaseIndentation { get; }
public override void WriteEndArray() { }
public override void WriteEndObject() { }
Expand Down