diff --git a/.azure-pipelines/ci-build.yml b/.azure-pipelines/ci-build.yml
index 53b181b22..8bef85221 100644
--- a/.azure-pipelines/ci-build.yml
+++ b/.azure-pipelines/ci-build.yml
@@ -65,6 +65,11 @@ extends:
displayName: 'Use .NET 8'
inputs:
version: 8.x
+
+ - task: UseDotNet@2
+ displayName: 'Use .NET 10'
+ inputs:
+ version: 10.x
# Install the nuget tool.
- task: NuGetToolInstaller@1
@@ -77,7 +82,7 @@ extends:
- task: DotNetCoreCLI@2
displayName: 'build'
inputs:
- projects: '$(Build.SourcesDirectory)\Microsoft.OpenApi.sln'
+ projects: '$(Build.SourcesDirectory)\Microsoft.OpenApi.slnx'
arguments: '--configuration $(BuildConfiguration) --no-incremental'
# Run the Unit test
@@ -85,7 +90,7 @@ extends:
displayName: 'test'
inputs:
command: test
- projects: '$(Build.SourcesDirectory)\Microsoft.OpenApi.sln'
+ projects: '$(Build.SourcesDirectory)\Microsoft.OpenApi.slnx'
arguments: '--configuration $(BuildConfiguration) --no-build'
- task: EsrpCodeSigning@5
diff --git a/.github/workflows/auto-merge-dependabot.yml b/.github/workflows/auto-merge-dependabot.yml
index 62a99a480..9d9039433 100644
--- a/.github/workflows/auto-merge-dependabot.yml
+++ b/.github/workflows/auto-merge-dependabot.yml
@@ -19,7 +19,7 @@ jobs:
steps:
- name: Dependabot metadata
id: metadata
- uses: dependabot/fetch-metadata@v3.0.0
+ uses: dependabot/fetch-metadata@v3.1.0
with:
github-token: "${{ secrets.GITHUB_TOKEN }}"
diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml
index df49f2842..b9d453d2a 100644
--- a/.github/workflows/ci-cd.yml
+++ b/.github/workflows/ci-cd.yml
@@ -13,11 +13,16 @@ jobs:
ARTIFACTS_FOLDER: ${{ github.workspace }}/Artifacts
GITHUB_RUN_NUMBER: ${{ github.run_number }}
steps:
- - name: Setup .NET
+ - name: Setup .NET 8
uses: actions/setup-dotnet@v5
with:
dotnet-version: 8.x
+ - name: Setup .NET 10
+ uses: actions/setup-dotnet@v5
+ with:
+ dotnet-version: 10.x
+
- name: Checkout repository
id: checkout_repo
uses: actions/checkout@v6
@@ -29,13 +34,13 @@ jobs:
id: build_projects
shell: pwsh
run: |
- dotnet build Microsoft.OpenApi.sln -c Release
+ dotnet build Microsoft.OpenApi.slnx -c Release
- name: Run unit tests
id: run_unit_tests
shell: pwsh
run: |
- dotnet test Microsoft.OpenApi.sln -c Release -v n
+ dotnet test Microsoft.OpenApi.slnx -c Release -v n
validate-trimming:
name: Validate Project for Trimming
@@ -43,13 +48,18 @@ jobs:
steps:
- uses: actions/checkout@v6
- - name: Setup .NET
+ - name: Setup .NET 8
uses: actions/setup-dotnet@v5
with:
dotnet-version: 8.x
+ - name: Setup .NET 10
+ uses: actions/setup-dotnet@v5
+ with:
+ dotnet-version: 10.x
+
- name: Validate Trimming warnings
- run: dotnet publish -c Release -r win-x64 /p:TreatWarningsAsErrors=true /warnaserror -f net8.0
+ run: dotnet publish -c Release -r win-x64 /p:TreatWarningsAsErrors=true /warnaserror -f net10.0
working-directory: ./test/Microsoft.OpenApi.Trimming.Tests
validate-performance:
@@ -60,11 +70,16 @@ jobs:
- name: Checkout repository
uses: actions/checkout@v6
- - name: Setup .NET
+ - name: Setup .NET 8
uses: actions/setup-dotnet@v5
with:
dotnet-version: 8.x
+ - name: Setup .NET 10
+ uses: actions/setup-dotnet@v5
+ with:
+ dotnet-version: 10.x
+
- name: Copy committed results
run: |
mkdir -p ./performanceResults
diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
index 30c8acef4..8ae3c621e 100644
--- a/.github/workflows/codeql-analysis.yml
+++ b/.github/workflows/codeql-analysis.yml
@@ -22,11 +22,16 @@ jobs:
id: checkout_repo
uses: actions/checkout@v6
- - name: Setup .NET
+ - name: Setup .NET 8
uses: actions/setup-dotnet@v5
with:
dotnet-version: 8.0.x
+ - name: Setup .NET 10
+ uses: actions/setup-dotnet@v5
+ with:
+ dotnet-version: 10.0.x
+
- name: Initialize CodeQL
id: init_codeql
uses: github/codeql-action/init@v4
diff --git a/.github/workflows/release-please-gha.yml b/.github/workflows/release-please-gha.yml
index 2b389c678..a6fdc1927 100644
--- a/.github/workflows/release-please-gha.yml
+++ b/.github/workflows/release-please-gha.yml
@@ -22,7 +22,16 @@ permissions:
contents: read
jobs:
+ check-secret:
+ runs-on: ubuntu-latest
+ outputs:
+ has-token: ${{ steps.check.outputs.has-token }}
+ steps:
+ - id: check
+ run: echo "has-token=${{ secrets.RELEASE_PLEASE_TOKEN_PROVIDER_PEM != '' }}" >> $GITHUB_OUTPUT
release:
+ needs: check-secret
+ if: needs.check-secret.outputs.has-token == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
@@ -59,7 +68,7 @@ jobs:
private-key: ${{ secrets.RELEASE_PLEASE_TOKEN_PROVIDER_PEM }}
- name: Release Please
- uses: googleapis/release-please-action@v4
+ uses: googleapis/release-please-action@v5
with:
token: ${{ steps.app-token.outputs.token }}
config-file: release-please-config.json
diff --git a/.github/workflows/sonarcloud.yml b/.github/workflows/sonarcloud.yml
index 7cd77844c..fe5afd83a 100644
--- a/.github/workflows/sonarcloud.yml
+++ b/.github/workflows/sonarcloud.yml
@@ -39,10 +39,14 @@ jobs:
with:
distribution: 'adopt'
java-version: 17
- - name: Setup .NET
+ - name: Setup .NET 8
uses: actions/setup-dotnet@v5
with:
dotnet-version: 8.0.x
+ - name: Setup .NET 10
+ uses: actions/setup-dotnet@v5
+ with:
+ dotnet-version: 10.0.x
- uses: actions/checkout@v6
with:
fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis
@@ -64,5 +68,5 @@ jobs:
dotnet tool run dotnet-sonarscanner begin /k:"microsoft_OpenAPI.NET" /o:"microsoft" /d:sonar.token="${{ secrets.SONAR_TOKEN }}" /d:sonar.host.url="https://sonarcloud.io" /d:sonar.cs.opencover.reportsPaths="test/**/coverage.opencover.xml"
dotnet workload restore
dotnet build
- dotnet test Microsoft.OpenApi.sln --no-build --verbosity normal /p:CollectCoverage=true /p:CoverletOutputFormat=opencover
+ dotnet test Microsoft.OpenApi.slnx --no-build --verbosity normal /p:CollectCoverage=true /p:CoverletOutputFormat=opencover
dotnet tool run dotnet-sonarscanner end /d:sonar.token="${{ secrets.SONAR_TOKEN }}"
\ No newline at end of file
diff --git a/.release-please-manifest.json b/.release-please-manifest.json
index c6a6955ca..751389532 100644
--- a/.release-please-manifest.json
+++ b/.release-please-manifest.json
@@ -1,3 +1,3 @@
{
- ".": "3.5.2"
+ ".": "3.5.3"
}
\ No newline at end of file
diff --git a/.vscode/tasks.json b/.vscode/tasks.json
index d2bd5722f..e68597ddc 100644
--- a/.vscode/tasks.json
+++ b/.vscode/tasks.json
@@ -10,7 +10,7 @@
"group": "build",
"args": [
"build",
- "${workspaceFolder}/Microsoft.OpenApi.sln",
+ "${workspaceFolder}/Microsoft.OpenApi.slnx",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
],
@@ -23,7 +23,7 @@
"group": "test",
"args": [
"test",
- "${workspaceFolder}/Microsoft.OpenApi.sln",
+ "${workspaceFolder}/Microsoft.OpenApi.slnx",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary",
"--collect:\"XPlat Code Coverage\""
diff --git a/CHANGELOG.md b/CHANGELOG.md
index a0a645e45..486479069 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,19 @@
# Changelog
+## [3.5.3](https://github.com/microsoft/OpenAPI.NET/compare/v3.5.2...v3.5.3) (2026-04-27)
+
+
+### Bug Fixes
+
+* null reference exception for boolean component schemas ([f97f91a](https://github.com/microsoft/OpenAPI.NET/commit/f97f91a9f785b88039968024d5d1f4af51332631))
+* null reference exception for boolean component schemas ([fe0b50a](https://github.com/microsoft/OpenAPI.NET/commit/fe0b50aae372e5b08625f888ae424f22da759cdf))
+* **schema:** support boolean schemas in deserializer for OpenAPI 3.1/3.2 ([05b44be](https://github.com/microsoft/OpenAPI.NET/commit/05b44beb9783f5bab1d8988b3cb916d0278d42ce))
+
+
+### Performance Improvements
+
+* **schema:** optimize boolean schema deserialization ([7316e3f](https://github.com/microsoft/OpenAPI.NET/commit/7316e3f0e7b9acc11bc1e0255d7ca33837a1bbdc))
+
## [3.5.2](https://github.com/microsoft/OpenAPI.NET/compare/v3.5.1...v3.5.2) (2026-04-14)
diff --git a/Directory.Build.props b/Directory.Build.props
index 63a6cf699..86e5a6bf9 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -12,13 +12,13 @@
https://github.com/Microsoft/OpenAPI.NET
© Microsoft Corporation. All rights reserved.
OpenAPI .NET
- 3.5.2
+ 3.5.3
true
-
+
\ No newline at end of file
diff --git a/Microsoft.OpenApi.sln b/Microsoft.OpenApi.sln
deleted file mode 100644
index b91029eca..000000000
--- a/Microsoft.OpenApi.sln
+++ /dev/null
@@ -1,103 +0,0 @@
-
-Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio Version 17
-VisualStudioVersion = 17.1.32210.238
-MinimumVisualStudioVersion = 10.0.40219.1
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.OpenApi", "src\Microsoft.OpenApi\Microsoft.OpenApi.csproj", "{A8E50143-69B2-472A-9D45-3F9A05D13202}"
-EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{4AEDAD90-F854-4940-BFEE-6374CC92CAB0}"
- ProjectSection(SolutionItems) = preProject
- .editorconfig = .editorconfig
- build.cmd = build.cmd
- readme.md = readme.md
- EndProjectSection
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.OpenApi.Workbench", "src\Microsoft.OpenApi.Workbench\Microsoft.OpenApi.Workbench.csproj", "{6A5E91E5-0441-46EE-AEB9-8334981B7F08}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.OpenApi.YamlReader", "src\Microsoft.OpenApi.YamlReader\Microsoft.OpenApi.YamlReader.csproj", "{79933258-0126-4382-8755-D50820ECC483}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.OpenApi.Tests", "test\Microsoft.OpenApi.Tests\Microsoft.OpenApi.Tests.csproj", "{AD83F991-DBF3-4251-8613-9CC54C826964}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.OpenApi.Readers.Tests", "test\Microsoft.OpenApi.Readers.Tests\Microsoft.OpenApi.Readers.Tests.csproj", "{1ED3C2C1-E1E7-4925-B4E6-2D969C3F5237}"
-EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{E546B92F-20A8-49C3-8323-4B25BB78F3E1}"
-EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{6357D7FD-2DE4-4900-ADB9-ABC37052040A}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.OpenApi.Trimming.Tests", "test\Microsoft.OpenApi.Trimming.Tests\Microsoft.OpenApi.Trimming.Tests.csproj", "{1D2E0C6E-B103-4CB6-912E-D56FA1501296}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.OpenApi.Hidi", "src\Microsoft.OpenApi.Hidi\Microsoft.OpenApi.Hidi.csproj", "{538936B4-5E14-4EA3-9FD0-F43E2DD014FB}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.OpenApi.Hidi.Tests", "test\Microsoft.OpenApi.Hidi.Tests\Microsoft.OpenApi.Hidi.Tests.csproj", "{6ADC5D41-EDD2-4206-B815-5DFF739C6832}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PerformanceTests", "performance\benchmark\PerformanceTests.csproj", "{537E49E3-325E-40EE-A90E-7556D4D333AA}"
-EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "performance", "performance", "{4BB7E3F7-CA7E-45D3-B5AC-5DBB510FD528}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "resultsComparer", "performance\resultsComparer\resultsComparer.csproj", "{5EEA836B-3E08-4BE1-82B8-5236D031B497}"
-EndProject
-Global
- GlobalSection(SolutionConfigurationPlatforms) = preSolution
- Debug|Any CPU = Debug|Any CPU
- Release|Any CPU = Release|Any CPU
- EndGlobalSection
- GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {A8E50143-69B2-472A-9D45-3F9A05D13202}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {A8E50143-69B2-472A-9D45-3F9A05D13202}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {A8E50143-69B2-472A-9D45-3F9A05D13202}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {A8E50143-69B2-472A-9D45-3F9A05D13202}.Release|Any CPU.Build.0 = Release|Any CPU
- {6A5E91E5-0441-46EE-AEB9-8334981B7F08}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {6A5E91E5-0441-46EE-AEB9-8334981B7F08}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {6A5E91E5-0441-46EE-AEB9-8334981B7F08}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {6A5E91E5-0441-46EE-AEB9-8334981B7F08}.Release|Any CPU.Build.0 = Release|Any CPU
- {79933258-0126-4382-8755-D50820ECC483}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {79933258-0126-4382-8755-D50820ECC483}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {79933258-0126-4382-8755-D50820ECC483}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {79933258-0126-4382-8755-D50820ECC483}.Release|Any CPU.Build.0 = Release|Any CPU
- {AD83F991-DBF3-4251-8613-9CC54C826964}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {AD83F991-DBF3-4251-8613-9CC54C826964}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {AD83F991-DBF3-4251-8613-9CC54C826964}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {AD83F991-DBF3-4251-8613-9CC54C826964}.Release|Any CPU.Build.0 = Release|Any CPU
- {1ED3C2C1-E1E7-4925-B4E6-2D969C3F5237}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {1ED3C2C1-E1E7-4925-B4E6-2D969C3F5237}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {1ED3C2C1-E1E7-4925-B4E6-2D969C3F5237}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {1ED3C2C1-E1E7-4925-B4E6-2D969C3F5237}.Release|Any CPU.Build.0 = Release|Any CPU
- {1D2E0C6E-B103-4CB6-912E-D56FA1501296}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {1D2E0C6E-B103-4CB6-912E-D56FA1501296}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {1D2E0C6E-B103-4CB6-912E-D56FA1501296}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {1D2E0C6E-B103-4CB6-912E-D56FA1501296}.Release|Any CPU.Build.0 = Release|Any CPU
- {538936B4-5E14-4EA3-9FD0-F43E2DD014FB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {538936B4-5E14-4EA3-9FD0-F43E2DD014FB}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {538936B4-5E14-4EA3-9FD0-F43E2DD014FB}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {538936B4-5E14-4EA3-9FD0-F43E2DD014FB}.Release|Any CPU.Build.0 = Release|Any CPU
- {6ADC5D41-EDD2-4206-B815-5DFF739C6832}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {6ADC5D41-EDD2-4206-B815-5DFF739C6832}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {6ADC5D41-EDD2-4206-B815-5DFF739C6832}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {6ADC5D41-EDD2-4206-B815-5DFF739C6832}.Release|Any CPU.Build.0 = Release|Any CPU
- {537E49E3-325E-40EE-A90E-7556D4D333AA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {537E49E3-325E-40EE-A90E-7556D4D333AA}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {537E49E3-325E-40EE-A90E-7556D4D333AA}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {537E49E3-325E-40EE-A90E-7556D4D333AA}.Release|Any CPU.Build.0 = Release|Any CPU
- {5EEA836B-3E08-4BE1-82B8-5236D031B497}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {5EEA836B-3E08-4BE1-82B8-5236D031B497}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {5EEA836B-3E08-4BE1-82B8-5236D031B497}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {5EEA836B-3E08-4BE1-82B8-5236D031B497}.Release|Any CPU.Build.0 = Release|Any CPU
- EndGlobalSection
- GlobalSection(SolutionProperties) = preSolution
- HideSolutionNode = FALSE
- EndGlobalSection
- GlobalSection(NestedProjects) = preSolution
- {A8E50143-69B2-472A-9D45-3F9A05D13202} = {E546B92F-20A8-49C3-8323-4B25BB78F3E1}
- {6A5E91E5-0441-46EE-AEB9-8334981B7F08} = {E546B92F-20A8-49C3-8323-4B25BB78F3E1}
- {79933258-0126-4382-8755-D50820ECC483} = {E546B92F-20A8-49C3-8323-4B25BB78F3E1}
- {AD83F991-DBF3-4251-8613-9CC54C826964} = {6357D7FD-2DE4-4900-ADB9-ABC37052040A}
- {1ED3C2C1-E1E7-4925-B4E6-2D969C3F5237} = {6357D7FD-2DE4-4900-ADB9-ABC37052040A}
- {1D2E0C6E-B103-4CB6-912E-D56FA1501296} = {6357D7FD-2DE4-4900-ADB9-ABC37052040A}
- {538936B4-5E14-4EA3-9FD0-F43E2DD014FB} = {E546B92F-20A8-49C3-8323-4B25BB78F3E1}
- {6ADC5D41-EDD2-4206-B815-5DFF739C6832} = {6357D7FD-2DE4-4900-ADB9-ABC37052040A}
- {5EEA836B-3E08-4BE1-82B8-5236D031B497} = {4BB7E3F7-CA7E-45D3-B5AC-5DBB510FD528}
- EndGlobalSection
- GlobalSection(ExtensibilityGlobals) = postSolution
- SolutionGuid = {9F171EFC-0DB5-4B10-ABFA-AF48D52CC565}
- EndGlobalSection
-EndGlobal
diff --git a/Microsoft.OpenApi.slnx b/Microsoft.OpenApi.slnx
new file mode 100644
index 000000000..f764776bf
--- /dev/null
+++ b/Microsoft.OpenApi.slnx
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/README.md b/README.md
index 6a7f787f8..3c77f909f 100644
--- a/README.md
+++ b/README.md
@@ -98,7 +98,7 @@ In order to test the validity of an OpenApi document, we avail the following too
#### Installation guidelines:
1. Clone the repo locally by running this command:
`git clone https://github.com/microsoft/OpenAPI.NET.git`
- 2. Open the solution file `(.sln)` in the root of the project with Visual Studio
+ 2. Open the solution file `(.slnx)` in the root of the project with Visual Studio
3. Navigate to the `src/Microsoft.OpenApi.Workbench` directory and set it as the startup project
4. Run the project and you'll see a GUI pop up resembling the one below:
diff --git a/global.json b/global.json
index d72c3f669..f234262d9 100644
--- a/global.json
+++ b/global.json
@@ -1,5 +1,5 @@
{
"sdk": {
- "version": "8.0.419"
+ "version": "10.0.203"
}
}
\ No newline at end of file
diff --git a/performance/resultsComparer/resultsComparer.csproj b/performance/resultsComparer/resultsComparer.csproj
index 847f570d5..eeeb0c683 100644
--- a/performance/resultsComparer/resultsComparer.csproj
+++ b/performance/resultsComparer/resultsComparer.csproj
@@ -8,12 +8,12 @@
-
-
-
-
+
+
+
+
-
+
diff --git a/src/Microsoft.OpenApi.Hidi/Microsoft.OpenApi.Hidi.csproj b/src/Microsoft.OpenApi.Hidi/Microsoft.OpenApi.Hidi.csproj
index d856e9487..eb6a089b6 100644
--- a/src/Microsoft.OpenApi.Hidi/Microsoft.OpenApi.Hidi.csproj
+++ b/src/Microsoft.OpenApi.Hidi/Microsoft.OpenApi.Hidi.csproj
@@ -29,10 +29,10 @@
-
-
-
-
+
+
+
+
runtime; build; native; contentfiles; analyzers; buildtransitive
all
diff --git a/src/Microsoft.OpenApi/Models/OpenApiSchema.cs b/src/Microsoft.OpenApi/Models/OpenApiSchema.cs
index 11252d3c4..40f24bdd0 100644
--- a/src/Microsoft.OpenApi/Models/OpenApiSchema.cs
+++ b/src/Microsoft.OpenApi/Models/OpenApiSchema.cs
@@ -11,6 +11,12 @@ namespace Microsoft.OpenApi
{
///
/// The Schema Object allows the definition of input and output data types.
+ ///
+ /// For OpenAPI 3.1+ (JSON Schema 2020-12), this class supports boolean schemas:
+ /// - Deserialization: The boolean literal true deserializes to an empty schema (allows any value).
+ /// The boolean literal false deserializes to a schema with set to an empty schema (disallows any value).
+ /// - Serialization: To produce something functionally equivalent to boolean schemas, create an empty
+ /// for "true" behavior, or create a schema with only set to an empty schema for "false" behavior.
///
public class OpenApiSchema : IOpenApiExtensible, IOpenApiSchema, IOpenApiSchemaWithUnevaluatedProperties, IMetadataContainer
{
diff --git a/src/Microsoft.OpenApi/Reader/ParseNodes/ValueNode.cs b/src/Microsoft.OpenApi/Reader/ParseNodes/ValueNode.cs
index a25a6275e..bf207d741 100644
--- a/src/Microsoft.OpenApi/Reader/ParseNodes/ValueNode.cs
+++ b/src/Microsoft.OpenApi/Reader/ParseNodes/ValueNode.cs
@@ -28,6 +28,17 @@ public override string GetScalarValue()
?? throw new OpenApiReaderException($"Expected a value at {Context.GetLocation()}.");
}
+ ///
+ /// Attempts to get the underlying value directly as the specified type without string conversion.
+ ///
+ /// The type to retrieve the value as.
+ /// The retrieved value if successful.
+ /// True if the value was successfully converted to the specified type; otherwise, false.
+ public bool TryGetValue(out T? value)
+ {
+ return _node.TryGetValue(out value);
+ }
+
///
/// Create a
///
diff --git a/src/Microsoft.OpenApi/Reader/V31/OpenApiSchemaDeserializer.cs b/src/Microsoft.OpenApi/Reader/V31/OpenApiSchemaDeserializer.cs
index ee1ca1832..39c32c979 100644
--- a/src/Microsoft.OpenApi/Reader/V31/OpenApiSchemaDeserializer.cs
+++ b/src/Microsoft.OpenApi/Reader/V31/OpenApiSchemaDeserializer.cs
@@ -362,6 +362,19 @@ internal static partial class OpenApiV31Deserializer
public static IOpenApiSchema LoadSchema(ParseNode node, OpenApiDocument hostDocument)
{
+ // Handle boolean schemas (true/false) for JSON Schema 2020-12 compatibility
+ if (node is ValueNode valueNode && valueNode.TryGetValue(out var boolValue))
+ {
+ var boolSchema = new OpenApiSchema();
+ if (!boolValue)
+ {
+ // false schema: represents "not valid" -> convert to "not: {}"
+ boolSchema.Not = new OpenApiSchema();
+ }
+ // true schema: represents "always valid" -> return empty schema (default)
+ return boolSchema;
+ }
+
var mapNode = node.CheckMapNode(OpenApiConstants.Schema);
var pointer = mapNode.GetReferencePointer();
diff --git a/src/Microsoft.OpenApi/Reader/V32/OpenApiSchemaDeserializer.cs b/src/Microsoft.OpenApi/Reader/V32/OpenApiSchemaDeserializer.cs
index 5dd32b413..c18c48462 100644
--- a/src/Microsoft.OpenApi/Reader/V32/OpenApiSchemaDeserializer.cs
+++ b/src/Microsoft.OpenApi/Reader/V32/OpenApiSchemaDeserializer.cs
@@ -362,6 +362,19 @@ internal static partial class OpenApiV32Deserializer
public static IOpenApiSchema LoadSchema(ParseNode node, OpenApiDocument hostDocument)
{
+ // Handle boolean schemas (true/false) for JSON Schema 2020-12 compatibility
+ if (node is ValueNode valueNode && valueNode.TryGetValue(out var boolValue))
+ {
+ var boolSchema = new OpenApiSchema();
+ if (!boolValue)
+ {
+ // false schema: represents "not valid" -> convert to "not: {}"
+ boolSchema.Not = new OpenApiSchema();
+ }
+ // true schema: represents "always valid" -> return empty schema (default)
+ return boolSchema;
+ }
+
var mapNode = node.CheckMapNode(OpenApiConstants.Schema);
var pointer = mapNode.GetReferencePointer();
diff --git a/src/Microsoft.OpenApi/Services/OpenApiWorkspace.cs b/src/Microsoft.OpenApi/Services/OpenApiWorkspace.cs
index 14c8d2ee4..22128adc2 100644
--- a/src/Microsoft.OpenApi/Services/OpenApiWorkspace.cs
+++ b/src/Microsoft.OpenApi/Services/OpenApiWorkspace.cs
@@ -93,6 +93,7 @@ public void RegisterComponents(OpenApiDocument document)
{
foreach (var item in document.Components.Schemas)
{
+ if (item.Value == null) continue;
location = item.Value.Id ?? baseUri + ReferenceType.Schema.GetDisplayName() + ComponentSegmentSeparator + item.Key;
RegisterComponent(location, item.Value);
}
@@ -103,6 +104,7 @@ public void RegisterComponents(OpenApiDocument document)
{
foreach (var item in document.Components.Parameters)
{
+ if (item.Value == null) continue;
location = baseUri + ReferenceType.Parameter.GetDisplayName() + ComponentSegmentSeparator + item.Key;
RegisterComponent(location, item.Value);
}
@@ -113,6 +115,7 @@ public void RegisterComponents(OpenApiDocument document)
{
foreach (var item in document.Components.Responses)
{
+ if (item.Value == null) continue;
location = baseUri + ReferenceType.Response.GetDisplayName() + ComponentSegmentSeparator + item.Key;
RegisterComponent(location, item.Value);
}
@@ -123,6 +126,7 @@ public void RegisterComponents(OpenApiDocument document)
{
foreach (var item in document.Components.RequestBodies)
{
+ if (item.Value == null) continue;
location = baseUri + ReferenceType.RequestBody.GetDisplayName() + ComponentSegmentSeparator + item.Key;
RegisterComponent(location, item.Value);
}
@@ -133,6 +137,7 @@ public void RegisterComponents(OpenApiDocument document)
{
foreach (var item in document.Components.Links)
{
+ if (item.Value == null) continue;
location = baseUri + ReferenceType.Link.GetDisplayName() + ComponentSegmentSeparator + item.Key;
RegisterComponent(location, item.Value);
}
@@ -143,6 +148,7 @@ public void RegisterComponents(OpenApiDocument document)
{
foreach (var item in document.Components.Callbacks)
{
+ if (item.Value == null) continue;
location = baseUri + ReferenceType.Callback.GetDisplayName() + ComponentSegmentSeparator + item.Key;
RegisterComponent(location, item.Value);
}
@@ -153,6 +159,7 @@ public void RegisterComponents(OpenApiDocument document)
{
foreach (var item in document.Components.PathItems)
{
+ if (item.Value == null) continue;
location = baseUri + ReferenceType.PathItem.GetDisplayName() + ComponentSegmentSeparator + item.Key;
RegisterComponent(location, item.Value);
}
@@ -163,6 +170,7 @@ public void RegisterComponents(OpenApiDocument document)
{
foreach (var item in document.Components.Examples)
{
+ if (item.Value == null) continue;
location = baseUri + ReferenceType.Example.GetDisplayName() + ComponentSegmentSeparator + item.Key;
RegisterComponent(location, item.Value);
}
@@ -173,6 +181,7 @@ public void RegisterComponents(OpenApiDocument document)
{
foreach (var item in document.Components.Headers)
{
+ if (item.Value == null) continue;
location = baseUri + ReferenceType.Header.GetDisplayName() + ComponentSegmentSeparator + item.Key;
RegisterComponent(location, item.Value);
}
@@ -183,6 +192,7 @@ public void RegisterComponents(OpenApiDocument document)
{
foreach (var item in document.Components.SecuritySchemes)
{
+ if (item.Value == null) continue;
location = baseUri + ReferenceType.SecurityScheme.GetDisplayName() + ComponentSegmentSeparator + item.Key;
RegisterComponent(location, item.Value);
}
@@ -193,6 +203,7 @@ public void RegisterComponents(OpenApiDocument document)
{
foreach (var item in document.Components.MediaTypes)
{
+ if (item.Value == null) continue;
location = baseUri + ReferenceType.MediaType.GetDisplayName() + ComponentSegmentSeparator + item.Key;
RegisterComponent(location, item.Value);
}
diff --git a/test/Microsoft.OpenApi.Hidi.Tests/Microsoft.OpenApi.Hidi.Tests.csproj b/test/Microsoft.OpenApi.Hidi.Tests/Microsoft.OpenApi.Hidi.Tests.csproj
index 76afffd97..2ff261d63 100644
--- a/test/Microsoft.OpenApi.Hidi.Tests/Microsoft.OpenApi.Hidi.Tests.csproj
+++ b/test/Microsoft.OpenApi.Hidi.Tests/Microsoft.OpenApi.Hidi.Tests.csproj
@@ -1,7 +1,7 @@
- net8.0
+ net10.0
enable
enable
@@ -13,12 +13,12 @@
-
+
-
+
diff --git a/test/Microsoft.OpenApi.Hidi.Tests/UtilityFiles/OpenApiDocumentMock.cs b/test/Microsoft.OpenApi.Hidi.Tests/UtilityFiles/OpenApiDocumentMock.cs
index 4654b43b6..535b43a80 100644
--- a/test/Microsoft.OpenApi.Hidi.Tests/UtilityFiles/OpenApiDocumentMock.cs
+++ b/test/Microsoft.OpenApi.Hidi.Tests/UtilityFiles/OpenApiDocumentMock.cs
@@ -6,7 +6,7 @@ namespace Microsoft.OpenApi.Tests.UtilityFiles
///
/// Mock class that creates a sample OpenAPI document.
///
- public static class OpenApiDocumentMock
+ internal static class OpenApiDocumentMock
{
///
/// Creates an OpenAPI document.
diff --git a/test/Microsoft.OpenApi.Readers.Tests/Microsoft.OpenApi.Readers.Tests.csproj b/test/Microsoft.OpenApi.Readers.Tests/Microsoft.OpenApi.Readers.Tests.csproj
index 7bac31d10..370e1b372 100644
--- a/test/Microsoft.OpenApi.Readers.Tests/Microsoft.OpenApi.Readers.Tests.csproj
+++ b/test/Microsoft.OpenApi.Readers.Tests/Microsoft.OpenApi.Readers.Tests.csproj
@@ -1,6 +1,6 @@
- net8.0
+ net10.0
false
true
..\..\src\Microsoft.OpenApi.snk
@@ -15,11 +15,10 @@
-
-
+
+
-
@@ -30,4 +29,4 @@
-
\ No newline at end of file
+
diff --git a/test/Microsoft.OpenApi.Readers.Tests/V31Tests/OpenApiDocumentTests.cs b/test/Microsoft.OpenApi.Readers.Tests/V31Tests/OpenApiDocumentTests.cs
index 28085736c..8acf84d09 100644
--- a/test/Microsoft.OpenApi.Readers.Tests/V31Tests/OpenApiDocumentTests.cs
+++ b/test/Microsoft.OpenApi.Readers.Tests/V31Tests/OpenApiDocumentTests.cs
@@ -630,5 +630,23 @@ public async Task ParseDocumentWithSelfExtensionWorks()
Assert.Empty(result.Diagnostic.Errors);
Assert.Empty(result.Diagnostic.Warnings);
}
+
+ [Fact]
+ public void LoadDocumentWithBooleanSchemaShouldNotThrowNullReferenceException()
+ {
+ // Arrange - OpenAPI 3.1 with a boolean schema in components/schemas (spec-valid per JSON Schema 2020-12)
+ var bytes = "{\"openapi\":\"3.1.0\",\"components\":{\"schemas\":{\"X\":true}}}"u8.ToArray();
+ using var ms = new MemoryStream(bytes);
+
+ // Act & Assert - should not throw NullReferenceException
+ var exception = Record.Exception(() => OpenApiDocument.Load(ms, format: null, new OpenApiReaderSettings()));
+
+ // The parser should handle the boolean schema gracefully
+ // Either accepting it or surfacing a structured diagnostic, but not throwing NullReferenceException
+ if (exception != null)
+ {
+ Assert.IsNotType(exception);
+ }
+ }
}
}
diff --git a/test/Microsoft.OpenApi.Readers.Tests/V31Tests/OpenApiSchemaTests.cs b/test/Microsoft.OpenApi.Readers.Tests/V31Tests/OpenApiSchemaTests.cs
index 7ed518b95..b65b2d8c3 100644
--- a/test/Microsoft.OpenApi.Readers.Tests/V31Tests/OpenApiSchemaTests.cs
+++ b/test/Microsoft.OpenApi.Readers.Tests/V31Tests/OpenApiSchemaTests.cs
@@ -844,5 +844,34 @@ public void ParseSchemaWithoutUnevaluatedPropertiesDefaultsToTrue()
Assert.Equivalent(expected, actual);
Assert.True(actual.UnevaluatedProperties); // Explicitly verify the default
}
+
+ [Theory]
+ [InlineData("{}")]
+ [InlineData("true")]
+ public void DeserializeTrueSchemaParsesAsEmptySchema(string schemaSource)
+ {
+ // Arrange & Act
+ var schema = OpenApiModelFactory.Parse(schemaSource, OpenApiSpecVersion.OpenApi3_1, new(), out _, OpenApiConstants.Json);
+
+ // Assert - schema should deserialize without error
+ Assert.NotNull(schema);
+ }
+
+ [Fact]
+ public void DeserializeFalseSchemaParsesAsNotEmptySchema()
+ {
+ // Arrange
+ var schemaSource = "false";
+
+ // Act
+ var schema = OpenApiModelFactory.Parse(schemaSource, OpenApiSpecVersion.OpenApi3_1, new(), out _, OpenApiConstants.Json);
+
+ // Assert - false schema should deserialize to not: {}
+ Assert.NotNull(schema);
+ Assert.NotNull(schema.Not);
+ Assert.Empty(schema.Not.AnyOf ?? []);
+ Assert.Empty(schema.Not.AllOf ?? []);
+ Assert.Empty(schema.Not.OneOf ?? []);
+ }
}
}
diff --git a/test/Microsoft.OpenApi.Readers.Tests/V32Tests/OpenApiDocumentTests.cs b/test/Microsoft.OpenApi.Readers.Tests/V32Tests/OpenApiDocumentTests.cs
index ed868a913..80051f05c 100644
--- a/test/Microsoft.OpenApi.Readers.Tests/V32Tests/OpenApiDocumentTests.cs
+++ b/test/Microsoft.OpenApi.Readers.Tests/V32Tests/OpenApiDocumentTests.cs
@@ -645,6 +645,24 @@ public async Task ParseDocumentWithSelfPropertyFromJsonWorks()
Assert.Empty(result.Diagnostic.Errors);
Assert.Empty(result.Diagnostic.Warnings);
}
+
+ [Fact]
+ public void LoadDocumentWithBooleanSchemaShouldNotThrowNullReferenceException()
+ {
+ // Arrange - OpenAPI 3.2 with a boolean schema in components/schemas (spec-valid per JSON Schema 2020-12)
+ var bytes = "{\"openapi\":\"3.2.0\",\"components\":{\"schemas\":{\"X\":true}}}"u8.ToArray();
+ using var ms = new MemoryStream(bytes);
+
+ // Act & Assert - should not throw NullReferenceException
+ var exception = Record.Exception(() => OpenApiDocument.Load(ms, format: null, new OpenApiReaderSettings()));
+
+ // The parser should handle the boolean schema gracefully
+ // Either accepting it or surfacing a structured diagnostic, but not throwing NullReferenceException
+ if (exception != null)
+ {
+ Assert.IsNotType(exception);
+ }
+ }
}
}
diff --git a/test/Microsoft.OpenApi.Readers.Tests/V32Tests/OpenApiSchemaTests.cs b/test/Microsoft.OpenApi.Readers.Tests/V32Tests/OpenApiSchemaTests.cs
index 286a0baf8..7f91b0327 100644
--- a/test/Microsoft.OpenApi.Readers.Tests/V32Tests/OpenApiSchemaTests.cs
+++ b/test/Microsoft.OpenApi.Readers.Tests/V32Tests/OpenApiSchemaTests.cs
@@ -1,4 +1,4 @@
-// Copyright (c) Microsoft Corporation. All rights reserved.
+// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
using System.Collections.Generic;
@@ -695,6 +695,35 @@ public void ParseSchemaWithUnevaluatedPropertiesComplexSchema()
// Assert
Assert.Equivalent(expected, actual);
}
+
+ [Theory]
+ [InlineData("{}")]
+ [InlineData("true")]
+ public void DeserializeTrueSchemaParsesAsEmptySchema(string schemaSource)
+ {
+ // Arrange & Act
+ var schema = OpenApiModelFactory.Parse(schemaSource, OpenApiSpecVersion.OpenApi3_2, new(), out _, OpenApiConstants.Json);
+
+ // Assert - schema should deserialize without error
+ Assert.NotNull(schema);
+ }
+
+ [Fact]
+ public void DeserializeFalseSchemaParsesAsNotEmptySchema()
+ {
+ // Arrange
+ var schemaSource = "false";
+
+ // Act
+ var schema = OpenApiModelFactory.Parse(schemaSource, OpenApiSpecVersion.OpenApi3_2, new(), out _, OpenApiConstants.Json);
+
+ // Assert - false schema should deserialize to not: {}
+ Assert.NotNull(schema);
+ Assert.NotNull(schema.Not);
+ Assert.Empty(schema.Not.AnyOf ?? []);
+ Assert.Empty(schema.Not.AllOf ?? []);
+ Assert.Empty(schema.Not.OneOf ?? []);
+ }
}
}
diff --git a/test/Microsoft.OpenApi.Tests/Microsoft.OpenApi.Tests.csproj b/test/Microsoft.OpenApi.Tests/Microsoft.OpenApi.Tests.csproj
index c4d967bde..41666803a 100644
--- a/test/Microsoft.OpenApi.Tests/Microsoft.OpenApi.Tests.csproj
+++ b/test/Microsoft.OpenApi.Tests/Microsoft.OpenApi.Tests.csproj
@@ -1,6 +1,6 @@
- net8.0
+ net10.0
false
true
Library
@@ -8,16 +8,14 @@
-
-
+
+
-
-
@@ -67,4 +65,4 @@
-
\ No newline at end of file
+
diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiSchemaTests.cs b/test/Microsoft.OpenApi.Tests/Models/OpenApiSchemaTests.cs
index 64874fb37..40034eded 100644
--- a/test/Microsoft.OpenApi.Tests/Models/OpenApiSchemaTests.cs
+++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiSchemaTests.cs
@@ -204,14 +204,18 @@ public class OpenApiSchemaTests
}
};
- [Fact]
- public async Task SerializeBasicSchemaAsV3JsonWorks()
+ [Theory]
+ [InlineData(OpenApiSpecVersion.OpenApi2_0)]
+ [InlineData(OpenApiSpecVersion.OpenApi3_0)]
+ [InlineData(OpenApiSpecVersion.OpenApi3_1)]
+ [InlineData(OpenApiSpecVersion.OpenApi3_2)]
+ public async Task SerializeBasicSchemaAsJsonWorks(OpenApiSpecVersion version)
{
// Arrange
var expected = @"{ }";
// Act
- var actual = await BasicSchema.SerializeAsJsonAsync(OpenApiSpecVersion.OpenApi3_0);
+ var actual = await BasicSchema.SerializeAsJsonAsync(version);
// Assert
actual = actual.MakeLineBreaksEnvironmentNeutral();
diff --git a/test/Microsoft.OpenApi.Trimming.Tests/Microsoft.OpenApi.Trimming.Tests.csproj b/test/Microsoft.OpenApi.Trimming.Tests/Microsoft.OpenApi.Trimming.Tests.csproj
index 20d8d8d70..a6a858526 100644
--- a/test/Microsoft.OpenApi.Trimming.Tests/Microsoft.OpenApi.Trimming.Tests.csproj
+++ b/test/Microsoft.OpenApi.Trimming.Tests/Microsoft.OpenApi.Trimming.Tests.csproj
@@ -1,7 +1,7 @@
Exe
- net8.0
+ net10.0
enable
enable
true