diff --git a/docs/plans/2026-04-03-migrate-nswag-to-stj-implementation.md b/docs/plans/2026-04-03-migrate-nswag-to-stj-implementation.md new file mode 100644 index 0000000000..f1a9b0d0ce --- /dev/null +++ b/docs/plans/2026-04-03-migrate-nswag-to-stj-implementation.md @@ -0,0 +1,1432 @@ +# Migrate NSwag to System.Text.Json — Implementation Plan + +> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. + +**Goal:** Remove Newtonsoft.Json from all NSwag projects except `NSwag.Generation.NewtonsoftJson` (new) and `NSwag.AspNet.WebApi` (legacy). Add a global `UseLocalNJsonSchemaProjects` switch to toggle between local project references and NuGet packages. + +**Architecture:** NSwag.Core has 28 files with Newtonsoft attributes (`[JsonProperty]`, `[JsonIgnore]`, etc.) that get mechanically swapped to STJ equivalents. One custom converter (`OpenApiPathItemConverter`) is rewritten as STJ `JsonConverter`. `NSwag.Commands` migrates from `JObject`/`JsonConvert` to `JsonNode`/`JsonSerializer`. A new `NSwag.Generation.NewtonsoftJson` project isolates all Newtonsoft schema generation for opt-in use. Code generation templates (Liquid) that generate Newtonsoft user code are NOT changed — they already support both JSON libraries. + +**Tech Stack:** C#, System.Text.Json, XUnit v3, Verify (snapshot testing) + +**Design doc:** `docs/plans/2026-04-03-migrate-nswag-to-stj.md` + +**Prerequisites:** NJsonSchema `feature/migrate-core-to-stj` branch must be available locally at `../NJsonSchema` (sibling directory). + +--- + +## Attribute Migration Reference + +These mechanical swaps apply across all NSwag.Core files (28 files): + +| Newtonsoft | System.Text.Json | +|---|---| +| `using Newtonsoft.Json;` | `using System.Text.Json.Serialization;` | +| `using Newtonsoft.Json.Converters;` | _(remove)_ | +| `[JsonProperty(PropertyName = "x")]` | `[JsonPropertyName("x")]` | +| `[JsonProperty(PropertyName = "x", Order = N)]` | `[JsonPropertyName("x")] [JsonPropertyOrder(N)]` | +| `[JsonProperty(..., DefaultValueHandling = DefaultValueHandling.Ignore)]` | Add `[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]` | +| `[JsonProperty(..., DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate)]` | Add `[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]` | +| `[JsonProperty(..., NullValueHandling = NullValueHandling.Ignore)]` | Add `[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]` | +| `[JsonProperty(..., Required = Required.Always)]` | Add `[JsonRequired]` | +| `[JsonProperty(..., ItemConverterType = typeof(StringEnumConverter))]` | Apply `[JsonConverter(typeof(JsonStringEnumConverter))]` on the enum type instead | +| `[JsonIgnore]` | `[JsonIgnore]` (same name, different namespace) | +| `[JsonExtensionData]` | `[JsonExtensionData]` (same name, different namespace; backing type `JToken` → `JsonNode`) | +| `[JsonConverter(typeof(StringEnumConverter))]` | `[JsonConverter(typeof(JsonStringEnumConverter))]` | +| `[JsonConverter(typeof(OpenApiPathItemConverter))]` | `[JsonConverter(typeof(OpenApiPathItemConverter))]` (rewrite converter for STJ) | + +--- + +## Phase 0: Setup — Local Reference Switch + +### Task 0.1: Add UseLocalNJsonSchemaProjects to Directory.Build.props + +**Files:** +- Modify: `Directory.Build.props` + +**Step 1: Add the property** + +Add inside the existing ``, after `true`: + +```xml + +false +``` + +**Step 2: Verify build still works** + +Run: `dotnet build src/NSwag.Core/NSwag.Core.csproj` +Expected: SUCCESS (property exists but not used yet) + +**Step 3: Commit** + +```bash +git add Directory.Build.props +git commit -m "feat: add UseLocalNJsonSchemaProjects switch to Directory.Build.props" +``` + +--- + +### Task 0.2: Add conditional references to NSwag.Core.csproj + +**Files:** +- Modify: `src/NSwag.Core/NSwag.Core.csproj` + +**Step 1: Replace the NJsonSchema PackageReference with conditional blocks** + +Find the existing `` and replace with: + +```xml + + + + + + + + +``` + +Keep the existing version number from the current PackageReference. + +**Step 2: Verify build with NuGet (default)** + +Run: `dotnet build src/NSwag.Core/NSwag.Core.csproj` +Expected: SUCCESS + +**Step 3: Verify build with local references** + +Run: `dotnet build src/NSwag.Core/NSwag.Core.csproj -p:UseLocalNJsonSchemaProjects=true` +Expected: SUCCESS (requires NJsonSchema repo at `../NJsonSchema` on the `feature/migrate-core-to-stj` branch) + +--- + +### Task 0.3: Add conditional references to remaining 5 projects + +**Files:** +- Modify: `src/NSwag.Core.Yaml/NSwag.Core.Yaml.csproj` — `NJsonSchema.Yaml` +- Modify: `src/NSwag.CodeGeneration/NSwag.CodeGeneration.csproj` — `NJsonSchema.CodeGeneration` +- Modify: `src/NSwag.CodeGeneration.CSharp/NSwag.CodeGeneration.CSharp.csproj` — `NJsonSchema.CodeGeneration.CSharp` +- Modify: `src/NSwag.CodeGeneration.TypeScript/NSwag.CodeGeneration.TypeScript.csproj` — `NJsonSchema.CodeGeneration.TypeScript` +- Modify: `src/NSwag.Generation/NSwag.Generation.csproj` — `NJsonSchema.NewtonsoftJson` (temporarily; will change in Phase 7) + +**Step 1: Apply the same conditional pattern to each .csproj** + +For each project, find its NJsonSchema `PackageReference` and replace with conditional blocks. The local project paths are: + +| NSwag .csproj | NuGet Package | Local ProjectReference path | +|---|---|---| +| `NSwag.Core.Yaml` | `NJsonSchema.Yaml` | `..\..\..\NJsonSchema\src\NJsonSchema.Yaml\NJsonSchema.Yaml.csproj` | +| `NSwag.CodeGeneration` | `NJsonSchema.CodeGeneration` | `..\..\..\NJsonSchema\src\NJsonSchema.CodeGeneration\NJsonSchema.CodeGeneration.csproj` | +| `NSwag.CodeGeneration.CSharp` | `NJsonSchema.CodeGeneration.CSharp` | `..\..\..\NJsonSchema\src\NJsonSchema.CodeGeneration.CSharp\NJsonSchema.CodeGeneration.CSharp.csproj` | +| `NSwag.CodeGeneration.TypeScript` | `NJsonSchema.CodeGeneration.TypeScript` | `..\..\..\NJsonSchema\src\NJsonSchema.CodeGeneration.TypeScript\NJsonSchema.CodeGeneration.TypeScript.csproj` | +| `NSwag.Generation` | `NJsonSchema.NewtonsoftJson` | `..\..\..\NJsonSchema\src\NJsonSchema.NewtonsoftJson\NJsonSchema.NewtonsoftJson.csproj` | + +**Step 2: Verify full solution build with local references** + +Run: `dotnet build src/NSwag.Core/NSwag.Core.csproj -p:UseLocalNJsonSchemaProjects=true && dotnet build src/NSwag.Core.Yaml/NSwag.Core.Yaml.csproj -p:UseLocalNJsonSchemaProjects=true && dotnet build src/NSwag.CodeGeneration/NSwag.CodeGeneration.csproj -p:UseLocalNJsonSchemaProjects=true && dotnet build src/NSwag.CodeGeneration.CSharp/NSwag.CodeGeneration.CSharp.csproj -p:UseLocalNJsonSchemaProjects=true && dotnet build src/NSwag.CodeGeneration.TypeScript/NSwag.CodeGeneration.TypeScript.csproj -p:UseLocalNJsonSchemaProjects=true` +Expected: SUCCESS + +**Step 3: Commit** + +```bash +git add src/NSwag.Core/NSwag.Core.csproj src/NSwag.Core.Yaml/NSwag.Core.Yaml.csproj src/NSwag.CodeGeneration/NSwag.CodeGeneration.csproj src/NSwag.CodeGeneration.CSharp/NSwag.CodeGeneration.CSharp.csproj src/NSwag.CodeGeneration.TypeScript/NSwag.CodeGeneration.TypeScript.csproj src/NSwag.Generation/NSwag.Generation.csproj +git commit -m "feat: add conditional NJsonSchema project/NuGet references to all projects" +``` + +--- + +## Phase 1: Snapshot Tests (Before Migration) + +Add Verify snapshot tests for OpenAPI document serialization to lock down current behavior before changing anything. + +### Task 1.1: Add serialization snapshot tests for NSwag.Core + +**Files:** +- Create: `src/NSwag.Core.Tests/Serialization/OpenApiDocumentSnapshotTests.cs` + +**Step 1: Write snapshot tests for representative OpenAPI documents** + +These tests serialize OpenAPI documents in both Swagger2 and OpenApi3 formats and snapshot the output. Any serialization drift during migration will be caught. + +```csharp +namespace NSwag.Core.Tests.Serialization; + +[UsesVerify] +public class OpenApiDocumentSnapshotTests +{ + [Fact] + public async Task Snapshot_MinimalDocument_Swagger2() + { + // Arrange + var document = new OpenApiDocument(); + document.Info = new OpenApiInfo { Title = "Test API", Version = "1.0.0" }; + + // Act + var json = document.ToJson(SchemaType.Swagger2, Formatting.Indented); + + // Assert + await Verify(json); + } + + [Fact] + public async Task Snapshot_MinimalDocument_OpenApi3() + { + // Arrange + var document = new OpenApiDocument(); + document.Info = new OpenApiInfo { Title = "Test API", Version = "1.0.0" }; + + // Act + var json = document.ToJson(SchemaType.OpenApi3, Formatting.Indented); + + // Assert + await Verify(json); + } + + [Fact] + public async Task Snapshot_FullDocument_OpenApi3() + { + // Arrange + var document = new OpenApiDocument(); + document.Info = new OpenApiInfo + { + Title = "Pet Store", + Version = "1.0.0", + Description = "A sample API", + Contact = new OpenApiContact { Name = "Test", Email = "test@test.com" }, + License = new OpenApiLicense { Name = "MIT" } + }; + document.Servers.Add(new OpenApiServer { Url = "https://api.example.com/v1" }); + + var pathItem = new OpenApiPathItem(); + var operation = new OpenApiOperation + { + Summary = "List pets", + OperationId = "listPets", + Tags = { "pets" } + }; + operation.Responses["200"] = new OpenApiResponse { Description = "A list of pets" }; + pathItem["get"] = operation; + document.Paths["/pets"] = pathItem; + + var postOperation = new OpenApiOperation + { + Summary = "Create a pet", + OperationId = "createPet" + }; + postOperation.RequestBody = new OpenApiRequestBody + { + Description = "Pet to add", + IsRequired = true + }; + postOperation.Responses["201"] = new OpenApiResponse { Description = "Pet created" }; + pathItem["post"] = postOperation; + + // Act + var json = document.ToJson(SchemaType.OpenApi3, Formatting.Indented); + + // Assert + await Verify(json); + } + + [Fact] + public async Task Snapshot_DocumentWithSecurity_OpenApi3() + { + // Arrange + var document = new OpenApiDocument(); + document.Info = new OpenApiInfo { Title = "Secure API", Version = "1.0.0" }; + document.Components.SecuritySchemes["bearer"] = new OpenApiSecurityScheme + { + Type = OpenApiSecuritySchemeType.Http, + Scheme = "bearer", + BearerFormat = "JWT", + Description = "JWT Bearer token" + }; + + // Act + var json = document.ToJson(SchemaType.OpenApi3, Formatting.Indented); + + // Assert + await Verify(json); + } + + [Fact] + public async Task Snapshot_RoundTrip_ComplexDocument() + { + // Arrange — load a real-world-ish spec + var json = @"{ + ""openapi"": ""3.0.0"", + ""info"": { ""title"": ""Test"", ""version"": ""1.0"" }, + ""paths"": { + ""/items"": { + ""get"": { + ""operationId"": ""getItems"", + ""parameters"": [ + { ""name"": ""limit"", ""in"": ""query"", ""schema"": { ""type"": ""integer"" } } + ], + ""responses"": { + ""200"": { ""description"": ""OK"" } + } + } + } + } + }"; + + // Act + var document = await OpenApiDocument.FromJsonAsync(json); + var roundTripped = document.ToJson(SchemaType.OpenApi3, Formatting.Indented); + + // Assert + await Verify(roundTripped); + } +} +``` + +**Step 2: Run tests to generate initial snapshots** + +Run: `dotnet test src/NSwag.Core.Tests/NSwag.Core.Tests.csproj --filter "OpenApiDocumentSnapshotTests"` +Expected: FAIL (no verified snapshots yet) + +**Step 3: Accept the snapshots** + +Review the generated `.received.txt` files. If they look correct, rename to `.verified.txt`. + +**Step 4: Run tests again to confirm they pass** + +Run: `dotnet test src/NSwag.Core.Tests/NSwag.Core.Tests.csproj --filter "OpenApiDocumentSnapshotTests"` +Expected: PASS + +**Step 5: Commit** + +```bash +git add src/NSwag.Core.Tests/Serialization/OpenApiDocumentSnapshotTests.cs src/NSwag.Core.Tests/Snapshots/ +git commit -m "test: add OpenAPI document serialization snapshot tests" +``` + +--- + +## Phase 2: NSwag.Core Attribute Migration (Mechanical) + +Migrate all 28 files from Newtonsoft attributes to STJ attributes. This is mechanical work following the attribute migration reference table above. + +### Task 2.1: Migrate simple model files (no custom converters, no extension data) + +**Files to modify** (one `using` swap + attribute swaps each): +- `src/NSwag.Core/OpenApiTag.cs` +- `src/NSwag.Core/OpenApiServerVariable.cs` +- `src/NSwag.Core/OpenApiServer.cs` +- `src/NSwag.Core/OpenApiOAuthFlows.cs` +- `src/NSwag.Core/OpenApiOAuthFlow.cs` +- `src/NSwag.Core/OpenApiMediaType.cs` +- `src/NSwag.Core/OpenApiLink.cs` +- `src/NSwag.Core/OpenApiLicense.cs` +- `src/NSwag.Core/OpenApiInfo.cs` +- `src/NSwag.Core/OpenApiExternalDocumentation.cs` +- `src/NSwag.Core/OpenApiExample.cs` +- `src/NSwag.Core/OpenApiEncoding.cs` +- `src/NSwag.Core/OpenApiContact.cs` +- `src/NSwag.Core/JsonExpectedSchema.cs` + +**Step 1: For each file, apply the mechanical attribute swap** + +Example transformation for `OpenApiTag.cs`: + +Before: +```csharp +using Newtonsoft.Json; + +// ... +[JsonProperty(PropertyName = "name", DefaultValueHandling = DefaultValueHandling.Ignore)] +public string Name { get; set; } +``` + +After: +```csharp +using System.Text.Json.Serialization; + +// ... +[JsonPropertyName("name")] +[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] +public string Name { get; set; } +``` + +Apply the same pattern to all 14 files listed above. + +**Step 2: Build to verify** + +Run: `dotnet build src/NSwag.Core/NSwag.Core.csproj -p:UseLocalNJsonSchemaProjects=true` +Expected: SUCCESS + +**Step 3: Run snapshot tests** + +Run: `dotnet test src/NSwag.Core.Tests/NSwag.Core.Tests.csproj --filter "OpenApiDocumentSnapshotTests"` +Expected: Check for drift. Update snapshots if the output is semantically equivalent. + +**Step 4: Commit** + +```bash +git add src/NSwag.Core/ +git commit -m "refactor: migrate simple NSwag.Core model files from Newtonsoft to STJ attributes" +``` + +--- + +### Task 2.2: Migrate enum types with StringEnumConverter + +**Files:** +- Modify: `src/NSwag.Core/OpenApiParameterStyle.cs` +- Modify: `src/NSwag.Core/OpenApiParameterKind.cs` +- Modify: `src/NSwag.Core/OpenApiParameterCollectionFormat.cs` +- Modify: `src/NSwag.Core/OpenApiOAuth2Flow.cs` + +**Step 1: Swap converters** + +Before: +```csharp +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; + +[JsonConverter(typeof(StringEnumConverter))] +public enum OpenApiParameterStyle { ... } +``` + +After: +```csharp +using System.Text.Json.Serialization; + +[JsonConverter(typeof(JsonStringEnumConverter))] +public enum OpenApiParameterStyle { ... } +``` + +Apply to all 4 enum files. + +**Step 2: Build and test** + +Run: `dotnet build src/NSwag.Core/NSwag.Core.csproj -p:UseLocalNJsonSchemaProjects=true` +Expected: SUCCESS + +**Step 3: Commit** + +```bash +git add src/NSwag.Core/ +git commit -m "refactor: migrate NSwag.Core enum types to STJ JsonStringEnumConverter" +``` + +--- + +### Task 2.3: Migrate complex model files (with Order, Required, ItemConverterType) + +**Files:** +- Modify: `src/NSwag.Core/OpenApiDocument.cs` +- Modify: `src/NSwag.Core/OpenApiDocument.Serialization.cs` +- Modify: `src/NSwag.Core/OpenApiComponents.cs` +- Modify: `src/NSwag.Core/OpenApiOperation.cs` +- Modify: `src/NSwag.Core/OpenApiParameter.cs` +- Modify: `src/NSwag.Core/OpenApiRequestBody.cs` +- Modify: `src/NSwag.Core/OpenApiResponse.cs` +- Modify: `src/NSwag.Core/OpenApiSecurityScheme.cs` +- Modify: `src/NSwag.Core/OpenApiCallback.cs` + +**Step 1: Apply attribute swaps with Order and Required** + +Example for `OpenApiDocument.cs`: + +Before: +```csharp +[JsonProperty(PropertyName = "info", Order = 4, DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate)] +public OpenApiInfo Info { get; set; } +``` + +After: +```csharp +[JsonPropertyName("info")] +[JsonPropertyOrder(4)] +[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] +public OpenApiInfo Info { get; set; } +``` + +For `Required.Always`: +```csharp +// Before +[JsonProperty(PropertyName = "title", Required = Required.Always, DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate)] + +// After +[JsonPropertyName("title")] +[JsonRequired] +[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] +``` + +For `ItemConverterType = typeof(StringEnumConverter)` (in `OpenApiDocument.Serialization.cs` and `OpenApiOperation.cs`): +The enum types themselves already have `[JsonConverter(typeof(JsonStringEnumConverter))]` from Task 2.2, so just remove the `ItemConverterType` parameter from the property attribute. + +**Step 2: Handle OpenApiDocument.cs Formatting reference** + +`OpenApiDocument.cs` line 123 uses `Formatting.Indented` (Newtonsoft). Replace the `ToJson()` method signature and internals to use STJ. Check how NJsonSchema's `ToJson()` works after its migration and follow the same pattern. + +**Step 3: Handle extension data type changes** + +In `OpenApiResponse.cs` and `OpenApiPathItem.cs` (pathitem done in Task 2.4), change: +```csharp +// Before +[JsonExtensionData] +public IDictionary ExtensionData { get; set; } + +// After +[JsonExtensionData] +public IDictionary ExtensionData { get; set; } +``` + +Add `using System.Text.Json.Nodes;` where needed. + +**Step 4: Build and test** + +Run: `dotnet build src/NSwag.Core/NSwag.Core.csproj -p:UseLocalNJsonSchemaProjects=true` +Expected: SUCCESS + +Run: `dotnet test src/NSwag.Core.Tests/NSwag.Core.Tests.csproj` +Expected: Check for failures and fix + +**Step 5: Commit** + +```bash +git add src/NSwag.Core/ +git commit -m "refactor: migrate complex NSwag.Core model files to STJ attributes" +``` + +--- + +### Task 2.4: Rewrite OpenApiPathItemConverter for STJ + +**Files:** +- Modify: `src/NSwag.Core/OpenApiPathItem.cs` + +This is the most complex single file. It has a custom `JsonConverter` that manually serializes/deserializes path items with HTTP operations, extension data, and `$ref` handling. + +**Step 1: Migrate the OpenApiPathItem attributes** + +Same as other files — swap `[JsonProperty]` → `[JsonPropertyName]`, `[JsonIgnore]` → `[JsonIgnore]`, etc. + +Change `[JsonExtensionData]` backing type from `JToken` to `JsonNode`. + +**Step 2: Rewrite OpenApiPathItemConverter as STJ JsonConverter** + +The converter needs to handle: +- Writing: summary, description, servers, parameters, extension data, and HTTP operation methods (get, put, post, delete, options, head, patch, trace) as lowercase keys +- Reading: parsing property names, detecting `$ref`, deserializing nested objects, collecting extension data (`x-*` properties) + +```csharp +using System.Text.Json; +using System.Text.Json.Nodes; +using System.Text.Json.Serialization; + +public class OpenApiPathItemConverter : JsonConverter +{ + public override OpenApiPathItem Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + if (reader.TokenType == JsonTokenType.Null) + return null; + + var pathItem = new OpenApiPathItem(); + + if (reader.TokenType != JsonTokenType.StartObject) + throw new JsonException("Expected StartObject"); + + while (reader.Read() && reader.TokenType == JsonTokenType.PropertyName) + { + var propertyName = reader.GetString(); + reader.Read(); + + switch (propertyName) + { + case "$ref": + // Handle reference — read as string, set on pathItem + break; + case "summary": + pathItem.Summary = reader.GetString(); + break; + case "description": + pathItem.Description = reader.GetString(); + break; + case "servers": + pathItem.Servers = JsonSerializer.Deserialize>(ref reader, options); + break; + case "parameters": + pathItem.Parameters = JsonSerializer.Deserialize>(ref reader, options); + break; + default: + if (Enum.TryParse(propertyName, true, out var method)) + { + var operation = JsonSerializer.Deserialize(ref reader, options); + pathItem[method] = operation; + } + else if (propertyName.StartsWith("x-")) + { + pathItem.ExtensionData ??= new Dictionary(); + pathItem.ExtensionData[propertyName] = JsonNode.Parse(JsonSerializer.Serialize(JsonSerializer.Deserialize(ref reader, options))); + } + else + { + reader.Skip(); + } + break; + } + } + + return pathItem; + } + + public override void Write(Utf8JsonWriter writer, OpenApiPathItem value, JsonSerializerOptions options) + { + writer.WriteStartObject(); + + // Write known properties + if (!string.IsNullOrEmpty(value.Summary)) + { + writer.WriteString("summary", value.Summary); + } + if (!string.IsNullOrEmpty(value.Description)) + { + writer.WriteString("description", value.Description); + } + + // Write operations with lowercase keys + foreach (var pair in value) + { + writer.WritePropertyName(pair.Key.ToString().ToLowerInvariant()); + JsonSerializer.Serialize(writer, pair.Value, options); + } + + // Write parameters, servers, extension data + if (value.Parameters?.Count > 0) + { + writer.WritePropertyName("parameters"); + JsonSerializer.Serialize(writer, value.Parameters, options); + } + if (value.Servers?.Count > 0) + { + writer.WritePropertyName("servers"); + JsonSerializer.Serialize(writer, value.Servers, options); + } + if (value.ExtensionData != null) + { + foreach (var ext in value.ExtensionData) + { + writer.WritePropertyName(ext.Key); + if (ext.Value != null) + ext.Value.WriteTo(writer); + else + writer.WriteNullValue(); + } + } + + writer.WriteEndObject(); + } +} +``` + +**Note:** This is a starting point. The exact implementation must match the current Newtonsoft converter's behavior. Compare carefully with the existing `OpenApiPathItemConverter` (lines 111-212 of `OpenApiPathItem.cs`) and port all edge cases, especially `$ref` handling. + +**Step 3: Build and test** + +Run: `dotnet build src/NSwag.Core/NSwag.Core.csproj -p:UseLocalNJsonSchemaProjects=true` +Expected: SUCCESS + +Run: `dotnet test src/NSwag.Core.Tests/NSwag.Core.Tests.csproj` +Expected: PASS (check snapshot tests for drift) + +**Step 4: Commit** + +```bash +git add src/NSwag.Core/OpenApiPathItem.cs +git commit -m "refactor: rewrite OpenApiPathItemConverter for System.Text.Json" +``` + +--- + +### Task 2.5: Migrate OpenApiSecurityScheme enum converters + +**Files:** +- Modify: `src/NSwag.Core/OpenApiSecurityScheme.cs` + +This file has `[JsonConverter(typeof(StringEnumConverter))]` on the `In` property (of type `OpenApiSecurityApiKeyLocation`) and the `Type` property (of type `OpenApiSecuritySchemeType`). The enum types themselves may not have the converter attribute, so: + +**Step 1: Check if the enum types have their own converter attributes** + +If `OpenApiSecuritySchemeType` and `OpenApiSecurityApiKeyLocation` don't already have `[JsonConverter]`, add `[JsonConverter(typeof(JsonStringEnumConverter))]` to the enum declarations. + +**Step 2: Remove the per-property `[JsonConverter]` attributes** + +Since the enums now have their own converter, the property-level converter is redundant. + +**Step 3: Swap remaining attributes as per the reference table** + +**Step 4: Build and test** + +Run: `dotnet build src/NSwag.Core/NSwag.Core.csproj -p:UseLocalNJsonSchemaProjects=true && dotnet test src/NSwag.Core.Tests/NSwag.Core.Tests.csproj` +Expected: SUCCESS + +**Step 5: Commit** + +```bash +git add src/NSwag.Core/ +git commit -m "refactor: migrate OpenApiSecurityScheme to STJ enum converters" +``` + +--- + +### Task 2.6: Update NSwag.Core.csproj — remove Newtonsoft.Json dependency + +**Files:** +- Modify: `src/NSwag.Core/NSwag.Core.csproj` + +**Step 1: Remove Newtonsoft.Json PackageReference** + +Find and remove: +```xml + +``` + +The project should now only depend on NJsonSchema (which itself uses STJ). + +**Step 2: Add System.Text.Json PackageReference if not already present** + +For `netstandard2.0` and `net462` targets, System.Text.Json needs to come from the NuGet package. Check if it's already pulled transitively via NJsonSchema. If not, add: + +```xml + +``` + +**Step 3: Build on all TFMs** + +Run: `dotnet build src/NSwag.Core/NSwag.Core.csproj -p:UseLocalNJsonSchemaProjects=true` +Expected: SUCCESS on all target frameworks + +**Step 4: Run all NSwag.Core tests** + +Run: `dotnet test src/NSwag.Core.Tests/NSwag.Core.Tests.csproj` +Expected: PASS + +**Step 5: Commit** + +```bash +git add src/NSwag.Core/NSwag.Core.csproj +git commit -m "refactor: remove Newtonsoft.Json dependency from NSwag.Core" +``` + +--- + +## Phase 3: NSwag.Commands Migration + +### Task 3.1: Migrate NSwagDocumentBase.cs + +**Files:** +- Modify: `src/NSwag.Commands/NSwagDocumentBase.cs` + +This is the most complex file in NSwag.Commands. It uses `JObject`, `JsonConvert`, `JsonSerializerSettings`, and `CamelCasePropertyNamesContractResolver`. + +**Step 1: Replace using statements** + +```csharp +// Remove: +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; +using Newtonsoft.Json.Linq; +using Newtonsoft.Json.Serialization; + +// Add: +using System.Text.Json; +using System.Text.Json.Nodes; +using System.Text.Json.Serialization; +``` + +**Step 2: Replace GetSerializerSettings() with GetSerializerOptions()** + +Before: +```csharp +private static JsonSerializerSettings GetSerializerSettings() +{ + return new JsonSerializerSettings + { + DefaultValueHandling = DefaultValueHandling.Include, + NullValueHandling = NullValueHandling.Include, + ContractResolver = new CamelCasePropertyNamesContractResolver(), + Converters = [new StringEnumConverter()] + }; +} +``` + +After: +```csharp +private static JsonSerializerOptions GetSerializerOptions() +{ + return new JsonSerializerOptions + { + DefaultIgnoreCondition = JsonIgnoreCondition.Never, + PropertyNamingPolicy = JsonNamingPolicy.CamelCase, + Converters = { new JsonStringEnumConverter() }, + WriteIndented = true + }; +} +``` + +**Step 3: Replace all JsonConvert calls** + +- `JsonConvert.SerializeObject(obj, Formatting.Indented, settings)` → `JsonSerializer.Serialize(obj, options)` (WriteIndented is on options) +- `JsonConvert.DeserializeObject(json, settings)` → `JsonSerializer.Deserialize(json, options)` +- `JsonConvert.ToString(value)` → `JsonSerializer.Serialize(value)` or manual escaping + +**Step 4: Replace JObject usage** + +- `JObject.FromObject(dict)` → `JsonSerializer.SerializeToNode(dict)` to get a `JsonNode` +- `JObject.Parse(data)` → `JsonNode.Parse(data)` +- `obj["key"].Value()` → `node["key"].GetValue()` + +**Step 5: Replace [JsonProperty] and [JsonIgnore] attributes** + +Same mechanical swap as NSwag.Core files. + +**Step 6: Build and test** + +Run: `dotnet build src/NSwag.Commands/NSwag.Commands.csproj -p:UseLocalNJsonSchemaProjects=true` +Expected: SUCCESS + +**Step 7: Commit** + +```bash +git add src/NSwag.Commands/NSwagDocumentBase.cs +git commit -m "refactor: migrate NSwagDocumentBase from Newtonsoft to STJ" +``` + +--- + +### Task 3.2: Migrate remaining NSwag.Commands files + +**Files:** +- Modify: `src/NSwag.Commands/OpenApiGeneratorCollection.cs` — `[JsonIgnore]` swap only +- Modify: `src/NSwag.Commands/CodeGeneratorCollection.cs` — `[JsonProperty]` + `[JsonIgnore]` swap +- Modify: `src/NSwag.Commands/Commands/OutputCommandBase.cs` — `[JsonProperty]` swap +- Modify: `src/NSwag.Commands/Commands/InputOutputCommandBase.cs` — `[JsonIgnore]` swap +- Modify: `src/NSwag.Commands/Commands/Generation/FromDocumentCommand.cs` — `[JsonProperty]` swap +- Modify: `src/NSwag.Commands/Commands/CodeGeneration/JsonSchemaToCSharpCommand.cs` — `[JsonIgnore]` swap +- Modify: `src/NSwag.Commands/Commands/CodeGeneration/CodeGeneratorCommandBase.cs` — `[JsonIgnore]` swap +- Modify: `src/NSwag.Commands/Commands/CodeGeneration/JsonSchemaToOpenApiCommand.cs` — `[JsonProperty]` swap +- Modify: `src/NSwag.Commands/Commands/Generation/AspNetCore/AspNetCoreToOpenApiGeneratorCommandEntryPoint.cs` — `JsonConvert.DeserializeObject` → `JsonSerializer.Deserialize` +- Modify: `src/NSwag.Commands/Commands/Generation/AspNetCore/AspNetCoreToOpenApiCommand.cs` — `JsonConvert.SerializeObject` → `JsonSerializer.Serialize` + +**Step 1: Apply mechanical swaps to all files** + +These are all straightforward — attribute namespace changes and `JsonConvert` → `JsonSerializer` calls. + +For `CodeGeneratorCollection.cs`, `NullValueHandling.Ignore`: +```csharp +// Before +[JsonProperty("OpenApiToTypeScriptClient", NullValueHandling = NullValueHandling.Ignore)] + +// After +[JsonPropertyName("OpenApiToTypeScriptClient")] +[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] +``` + +**Step 2: Build and test** + +Run: `dotnet build src/NSwag.Commands/NSwag.Commands.csproj -p:UseLocalNJsonSchemaProjects=true` +Expected: SUCCESS + +**Step 3: Commit** + +```bash +git add src/NSwag.Commands/ +git commit -m "refactor: migrate remaining NSwag.Commands files to STJ" +``` + +--- + +## Phase 4: NSwag.Core.Yaml Migration + +### Task 4.1: Migrate OpenApiYamlDocument.cs + +**Files:** +- Modify: `src/NSwag.Core.Yaml/OpenApiYamlDocument.cs` + +**Step 1: Replace ExpandoObject conversion** + +Before: +```csharp +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; +// ... +var expConverter = new ExpandoObjectConverter(); +dynamic expandoObject = JsonConvert.DeserializeObject(json, expConverter); +``` + +After: +```csharp +using System.Text.Json; +using System.Text.Json.Nodes; +// ... +// Convert JSON string to a dynamic-friendly structure for YamlDotNet +var jsonNode = JsonNode.Parse(json); +var expandoObject = ConvertJsonNodeToExpandoObject(jsonNode); +``` + +Write a helper method `ConvertJsonNodeToExpandoObject` that recursively converts `JsonNode` to `ExpandoObject`/`List`/primitives for YamlDotNet compatibility: + +```csharp +private static object ConvertJsonNodeToExpandoObject(JsonNode node) +{ + if (node is JsonObject jsonObject) + { + var expando = new ExpandoObject(); + var dict = (IDictionary)expando; + foreach (var property in jsonObject) + { + dict[property.Key] = property.Value != null ? ConvertJsonNodeToExpandoObject(property.Value) : null; + } + return expando; + } + else if (node is JsonArray jsonArray) + { + return jsonArray.Select(item => item != null ? ConvertJsonNodeToExpandoObject(item) : null).ToList(); + } + else if (node is JsonValue jsonValue) + { + if (jsonValue.TryGetValue(out var boolValue)) return boolValue; + if (jsonValue.TryGetValue(out var longValue)) return longValue; + if (jsonValue.TryGetValue(out var doubleValue)) return doubleValue; + if (jsonValue.TryGetValue(out var stringValue)) return stringValue; + return node.ToJsonString(); + } + return null; +} +``` + +**Step 2: Build and test** + +Run: `dotnet build src/NSwag.Core.Yaml/NSwag.Core.Yaml.csproj -p:UseLocalNJsonSchemaProjects=true && dotnet test src/NSwag.Core.Yaml.Tests/NSwag.Core.Yaml.Tests.csproj` +Expected: PASS + +**Step 3: Commit** + +```bash +git add src/NSwag.Core.Yaml/ +git commit -m "refactor: migrate NSwag.Core.Yaml from Newtonsoft to STJ" +``` + +--- + +## Phase 5: NSwag.AspNetCore Migration + +### Task 5.1: Migrate SwaggerUiSettings and SwaggerUiSettingsBase + +**Files:** +- Modify: `src/NSwag.AspNetCore/SwaggerUiSettings.cs` +- Modify: `src/NSwag.AspNetCore/SwaggerUiSettingsBase.cs` + +**Step 1: Replace JsonConvert calls** + +In `SwaggerUiSettings.cs`: +```csharp +// Before +JsonConvert.SerializeObject(collection) +JsonConvert.SerializeObject(swaggerRoutes) + +// After +JsonSerializer.Serialize(collection) +JsonSerializer.Serialize(swaggerRoutes) +``` + +In `SwaggerUiSettingsBase.cs`: +```csharp +// Before +JsonConvert.SerializeObject(pair.Value) + +// After +JsonSerializer.Serialize(pair.Value) +``` + +In `SwaggerUiSettings.cs` inner class `SwaggerUiRoute`: +```csharp +// Before +[JsonProperty("url")] +public string Url { get; set; } +[JsonProperty("name")] +public string Name { get; set; } + +// After +[JsonPropertyName("url")] +public string Url { get; set; } +[JsonPropertyName("name")] +public string Name { get; set; } +``` + +**Step 2: Replace using statements** + +```csharp +// Remove: using Newtonsoft.Json; +// Add: using System.Text.Json; using System.Text.Json.Serialization; +``` + +**Step 3: Build** + +Run: `dotnet build src/NSwag.AspNetCore/NSwag.AspNetCore.csproj` +Expected: SUCCESS + +**Step 4: Commit** + +```bash +git add src/NSwag.AspNetCore/SwaggerUiSettings.cs src/NSwag.AspNetCore/SwaggerUiSettingsBase.cs +git commit -m "refactor: migrate SwaggerUiSettings to STJ" +``` + +--- + +### Task 5.2: Migrate JsonExceptionFilterAttribute (AspNetCore) + +**Files:** +- Modify: `src/NSwag.AspNetCore/JsonExceptionFilterAttribute.cs` + +**Step 1: Replace JsonConvert and JsonSerializerSettings** + +This file uses `JsonConvert.SerializeObject(exception, settings)` with custom settings and a `JsonExceptionConverter`. The `JsonExceptionConverter` is from NJsonSchema. + +After the NJsonSchema STJ migration, check if there's a STJ equivalent of `JsonExceptionConverter`. If so, use it. If not, serialize exceptions with a simple STJ approach: + +```csharp +// Before +var settings = GetSerializerSettings(context); +settings.Converters.Add(new JsonExceptionConverter(...)); +var json = JsonConvert.SerializeObject(context.Exception, settings); + +// After +var options = new JsonSerializerOptions { WriteIndented = true }; +// Use NJsonSchema's STJ exception converter if available, otherwise basic serialization +var json = JsonSerializer.Serialize(context.Exception, options); +``` + +**Step 2: Remove CopySettings method** (no longer needed with STJ) + +**Step 3: Build and test** + +Run: `dotnet build src/NSwag.AspNetCore/NSwag.AspNetCore.csproj` +Expected: SUCCESS + +**Step 4: Commit** + +```bash +git add src/NSwag.AspNetCore/JsonExceptionFilterAttribute.cs +git commit -m "refactor: migrate JsonExceptionFilterAttribute to STJ" +``` + +--- + +### Task 5.3: Migrate NSwagServiceCollectionExtensions + +**Files:** +- Modify: `src/NSwag.AspNetCore/Extensions/NSwagServiceCollectionExtensions.cs` + +**Step 1: Update GetJsonSerializerSettings references** + +This file calls `AspNetCoreOpenApiDocumentGenerator.GetJsonSerializerSettings(services)`. After Phase 6 migrates the generation layer, this will need to use the STJ equivalent. For now, note the dependency and update when Phase 6 is complete. + +--- + +## Phase 6: NSwag.Generation Migration + +### Task 6.1: Migrate OpenApiDocumentGeneratorSettings.cs + +**Files:** +- Modify: `src/NSwag.Generation/OpenApiDocumentGeneratorSettings.cs` + +**Step 1: Swap [JsonIgnore] namespace** + +```csharp +// Before: using Newtonsoft.Json; +// After: using System.Text.Json.Serialization; +``` + +The `[JsonIgnore]` attributes don't change — just the using statement. + +**Step 2: Commit** + +```bash +git add src/NSwag.Generation/OpenApiDocumentGeneratorSettings.cs +git commit -m "refactor: migrate OpenApiDocumentGeneratorSettings to STJ" +``` + +--- + +### Task 6.2: Migrate OpenApiDocumentGenerator.cs + +**Files:** +- Modify: `src/NSwag.Generation/OpenApiDocumentGenerator.cs` + +**Step 1: Replace schema annotation check** + +Before: +```csharp +JsonConvert.SerializeObject(operationParameter.Schema) != "{}" +``` + +After: +```csharp +JsonSerializer.Serialize(operationParameter.Schema) != "{}" +``` + +Or better, check schema properties directly without serialization if possible. + +**Step 2: Update using statements** + +```csharp +// Before: using Newtonsoft.Json; +// After: using System.Text.Json; +``` + +**Step 3: Commit** + +```bash +git add src/NSwag.Generation/OpenApiDocumentGenerator.cs +git commit -m "refactor: migrate OpenApiDocumentGenerator to STJ" +``` + +--- + +### Task 6.3: Migrate AspNetCoreOpenApiDocumentGenerator.cs + +**Files:** +- Modify: `src/NSwag.Generation.AspNetCore/AspNetCoreOpenApiDocumentGenerator.cs` + +This file uses reflection to load `MvcNewtonsoftJsonOptions` from ASP.NET Core. After migration, the default path should use STJ's `JsonOptions` (which is the default in modern ASP.NET Core). + +**Step 1: Update GetJsonSerializerSettings to GetJsonSerializerOptions** + +The method should return `JsonSerializerOptions` instead of `JsonSerializerSettings`. It should look for ASP.NET Core's `Microsoft.AspNetCore.Http.Json.JsonOptions` from DI (the default STJ configuration). + +The Newtonsoft reflection path (loading `MvcNewtonsoftJsonOptions`) moves to `NSwag.Generation.NewtonsoftJson` in Phase 7. + +**Step 2: Replace JsonConvert.DeserializeObject** + +```csharp +// Before +JsonConvert.DeserializeObject(stringBuilder.ToString()) + +// After +JsonSerializer.Deserialize(stringBuilder.ToString()) +``` + +**Step 3: Build and test** + +Run: `dotnet build src/NSwag.Generation.AspNetCore/NSwag.Generation.AspNetCore.csproj -p:UseLocalNJsonSchemaProjects=true` +Expected: SUCCESS + +**Step 4: Commit** + +```bash +git add src/NSwag.Generation.AspNetCore/ +git commit -m "refactor: migrate AspNetCoreOpenApiDocumentGenerator to STJ" +``` + +--- + +## Phase 7: Create NSwag.Generation.NewtonsoftJson + +### Task 7.1: Create the new project + +**Files:** +- Create: `src/NSwag.Generation.NewtonsoftJson/NSwag.Generation.NewtonsoftJson.csproj` + +**Step 1: Create the .csproj** + +```xml + + + + netstandard2.0;net462;net8.0 + enable + NSwag Newtonsoft.Json integration for OpenAPI schema generation from Newtonsoft-annotated types + + + + + + + + + + + + + + + + +``` + +**Step 2: Create the Newtonsoft-aware generator extension** + +Create: `src/NSwag.Generation.NewtonsoftJson/NewtonsoftJsonOpenApiSchemaGeneratorExtensions.cs` + +This class provides the opt-in for Newtonsoft-aware schema generation. Move the reflection-based `MvcNewtonsoftJsonOptions` detection from `AspNetCoreOpenApiDocumentGenerator` here. + +**Step 3: Build** + +Run: `dotnet build src/NSwag.Generation.NewtonsoftJson/NSwag.Generation.NewtonsoftJson.csproj -p:UseLocalNJsonSchemaProjects=true` +Expected: SUCCESS + +**Step 4: Commit** + +```bash +git add src/NSwag.Generation.NewtonsoftJson/ +git commit -m "feat: create NSwag.Generation.NewtonsoftJson project" +``` + +--- + +### Task 7.2: Update NSwag.Generation to drop NJsonSchema.NewtonsoftJson + +**Files:** +- Modify: `src/NSwag.Generation/NSwag.Generation.csproj` + +**Step 1: Change NJsonSchema.NewtonsoftJson reference to NJsonSchema** + +The generation project should now reference just `NJsonSchema` (STJ-based), not `NJsonSchema.NewtonsoftJson`. + +```xml + + + + + + + + +``` + +**Step 2: Build and verify no Newtonsoft references remain** + +Run: `dotnet build src/NSwag.Generation/NSwag.Generation.csproj -p:UseLocalNJsonSchemaProjects=true` +Expected: SUCCESS (no Newtonsoft types should be used in this project after Phase 6 migration) + +**Step 3: Commit** + +```bash +git add src/NSwag.Generation/NSwag.Generation.csproj +git commit -m "refactor: drop NJsonSchema.NewtonsoftJson dependency from NSwag.Generation" +``` + +--- + +### Task 7.3: Update NSwag.AspNet.WebApi to reference NSwag.Generation.NewtonsoftJson + +**Files:** +- Modify: `src/NSwag.AspNet.WebApi/NSwag.AspNet.WebApi.csproj` + +**Step 1: Add project reference to NSwag.Generation.NewtonsoftJson** + +This project stays Newtonsoft-based (old ASP.NET). It should reference `NSwag.Generation.NewtonsoftJson` instead of directly referencing `NJsonSchema.NewtonsoftJson`. + +**Step 2: Build** + +Run: `dotnet build src/NSwag.AspNet.WebApi/NSwag.AspNet.WebApi.csproj -p:UseLocalNJsonSchemaProjects=true` +Expected: SUCCESS + +**Step 3: Commit** + +```bash +git add src/NSwag.AspNet.WebApi/NSwag.AspNet.WebApi.csproj +git commit -m "refactor: NSwag.AspNet.WebApi references NSwag.Generation.NewtonsoftJson" +``` + +--- + +## Phase 8: Peripheral Projects Cleanup + +### Task 8.1: Migrate NSwag.CodeGeneration + +**Files:** +- Modify: `src/NSwag.CodeGeneration/ClientGeneratorBase.cs` + +**Step 1: Remove Newtonsoft.Json.Linq using statement** + +```csharp +// Remove: using Newtonsoft.Json.Linq; +``` + +If any JObject/JToken usage exists, replace with JsonNode equivalents. + +**Step 2: Build** + +Run: `dotnet build src/NSwag.CodeGeneration/NSwag.CodeGeneration.csproj -p:UseLocalNJsonSchemaProjects=true` +Expected: SUCCESS + +**Step 3: Commit** + +```bash +git add src/NSwag.CodeGeneration/ +git commit -m "refactor: remove Newtonsoft from NSwag.CodeGeneration" +``` + +--- + +### Task 8.2: Migrate NSwag.CodeGeneration.CSharp + +**Files:** +- Modify: `src/NSwag.CodeGeneration.CSharp/CSharpGeneratorBaseSettings.cs` + +**Step 1: Swap [JsonIgnore] namespace** + +```csharp +// Before: using Newtonsoft.Json; +// After: using System.Text.Json.Serialization; +``` + +**Note:** Do NOT modify the Liquid templates (`Client.Class.liquid`, `JsonExceptionConverter.liquid`, etc.). These generate user code that supports both Newtonsoft and STJ via the `CSharpJsonLibrary` setting. They are output code, not NSwag's own dependency. + +**Step 2: Build** + +Run: `dotnet build src/NSwag.CodeGeneration.CSharp/NSwag.CodeGeneration.CSharp.csproj -p:UseLocalNJsonSchemaProjects=true` +Expected: SUCCESS + +**Step 3: Commit** + +```bash +git add src/NSwag.CodeGeneration.CSharp/CSharpGeneratorBaseSettings.cs +git commit -m "refactor: remove Newtonsoft from NSwag.CodeGeneration.CSharp" +``` + +--- + +### Task 8.3: Migrate NSwag.CodeGeneration.TypeScript + +**Files:** +- Modify: `src/NSwag.CodeGeneration.TypeScript/TypeScriptClientGeneratorSettings.cs` + +**Step 1: Swap [JsonIgnore] namespace** + +```csharp +// Before: using Newtonsoft.Json; +// After: using System.Text.Json.Serialization; +``` + +**Step 2: Build** + +Run: `dotnet build src/NSwag.CodeGeneration.TypeScript/NSwag.CodeGeneration.TypeScript.csproj -p:UseLocalNJsonSchemaProjects=true` +Expected: SUCCESS + +**Step 3: Commit** + +```bash +git add src/NSwag.CodeGeneration.TypeScript/TypeScriptClientGeneratorSettings.cs +git commit -m "refactor: remove Newtonsoft from NSwag.CodeGeneration.TypeScript" +``` + +--- + +### Task 8.4: Migrate NSwag.Generation.WebApi + +**Files:** +- Modify: `src/NSwag.Generation.WebApi/Processors/OperationParameterProcessor.cs` + +**Step 1: Check usage** + +The file checks for `JsonIgnoreAttribute` by type name string (not a direct Newtonsoft reference). This string check should work for both Newtonsoft and STJ `JsonIgnore` attributes. Verify no `using Newtonsoft.Json` exists. + +**Step 2: Build** + +Run: `dotnet build src/NSwag.Generation.WebApi/NSwag.Generation.WebApi.csproj -p:UseLocalNJsonSchemaProjects=true` +Expected: SUCCESS + +--- + +## Phase 9: Remove Newtonsoft Dependencies from .csproj Files + +### Task 9.1: Audit and remove remaining Newtonsoft PackageReferences + +**Files to check:** +- `src/NSwag.Core/NSwag.Core.csproj` (should be done in Task 2.6) +- `src/NSwag.Core.Yaml/NSwag.Core.Yaml.csproj` +- `src/NSwag.Commands/NSwag.Commands.csproj` +- `src/NSwag.AspNetCore/NSwag.AspNetCore.csproj` +- `src/NSwag.Generation/NSwag.Generation.csproj` (should be done in Task 7.2) +- `src/NSwag.Generation.AspNetCore/NSwag.Generation.AspNetCore.csproj` +- `src/NSwag.CodeGeneration/NSwag.CodeGeneration.csproj` +- `src/NSwag.CodeGeneration.CSharp/NSwag.CodeGeneration.CSharp.csproj` +- `src/NSwag.CodeGeneration.TypeScript/NSwag.CodeGeneration.TypeScript.csproj` + +**Step 1: For each .csproj, verify no direct Newtonsoft.Json PackageReference remains** + +Search for `Newtonsoft` in all .csproj files (excluding NSwag.Generation.NewtonsoftJson, NSwag.AspNet.WebApi, and test projects). + +**Step 2: Build the full solution** + +Run: `dotnet build -p:UseLocalNJsonSchemaProjects=true` (from repo root, if solution file exists) +or build each project individually. + +**Step 3: Commit** + +```bash +git add src/ +git commit -m "refactor: remove all remaining Newtonsoft.Json dependencies from core projects" +``` + +--- + +## Phase 10: Test Updates & Verification + +### Task 10.1: Update test projects + +**Files:** +- Modify: `src/NSwag.Core.Tests/NSwag.Core.Tests.csproj` — may need `System.Text.Json` reference +- Modify: `src/NSwag.CodeGeneration.Tests/NSwag.CodeGeneration.Tests.csproj` — remove `NJsonSchema.NewtonsoftJson` if no longer needed +- Modify: All test files that use Newtonsoft types for assertions + +**Step 1: Update test project references** + +Test projects may still need Newtonsoft for testing Newtonsoft-specific scenarios, but the main test paths should use STJ. + +**Step 2: Update test code** + +Any test that directly uses `JObject`, `JToken`, `JsonConvert` for assertions needs to be updated: +- `JObject.Parse(json)["property"]` → `JsonNode.Parse(json)["property"]` +- `JsonConvert.DeserializeObject(json)` → `JsonSerializer.Deserialize(json)` + +--- + +### Task 10.2: Run full test suite + +**Step 1: Run all tests with local NJsonSchema references** + +Run: `dotnet test src/NSwag.Core.Tests/NSwag.Core.Tests.csproj -p:UseLocalNJsonSchemaProjects=true` +Run: `dotnet test src/NSwag.Core.Yaml.Tests/NSwag.Core.Yaml.Tests.csproj -p:UseLocalNJsonSchemaProjects=true` +Run: `dotnet test src/NSwag.CodeGeneration.Tests/NSwag.CodeGeneration.Tests.csproj -p:UseLocalNJsonSchemaProjects=true` +Run: `dotnet test src/NSwag.CodeGeneration.CSharp.Tests/NSwag.CodeGeneration.CSharp.Tests.csproj -p:UseLocalNJsonSchemaProjects=true` +Run: `dotnet test src/NSwag.CodeGeneration.TypeScript.Tests/NSwag.CodeGeneration.TypeScript.Tests.csproj -p:UseLocalNJsonSchemaProjects=true` +Run: `dotnet test src/NSwag.Generation.Tests/NSwag.Generation.Tests.csproj -p:UseLocalNJsonSchemaProjects=true` +Run: `dotnet test src/NSwag.Generation.AspNetCore.Tests/NSwag.Generation.AspNetCore.Tests.csproj -p:UseLocalNJsonSchemaProjects=true` + +Expected: ALL PASS + +**Step 2: Verify snapshot tests haven't drifted** + +Check that all `.verified.txt` files match. If there's drift, review carefully — serialization order changes or whitespace differences may be acceptable. + +**Step 3: Verify NSwagStudio still builds** + +Run: `dotnet build src/NSwagStudio/NSwagStudio.csproj -p:UseLocalNJsonSchemaProjects=true` +Expected: SUCCESS (net462 WPF app) + +--- + +### Task 10.3: Final verification — build with NuGet packages (switch off) + +**Step 1: Verify default build still works** + +Run: `dotnet build src/NSwag.Core/NSwag.Core.csproj` +Expected: SUCCESS (using NuGet packages — will only work once NJsonSchema v11 STJ is published) + +**Note:** Until NJsonSchema publishes STJ-based NuGet packages, NuGet-mode builds will use the old Newtonsoft-based NJsonSchema. Local-reference mode is the primary development path for now. + +**Step 2: Commit all remaining changes** + +```bash +git add . +git commit -m "test: update test projects and verify full migration" +``` diff --git a/docs/plans/2026-04-03-migrate-nswag-to-stj.md b/docs/plans/2026-04-03-migrate-nswag-to-stj.md new file mode 100644 index 0000000000..b208f66950 --- /dev/null +++ b/docs/plans/2026-04-03-migrate-nswag-to-stj.md @@ -0,0 +1,186 @@ +# Migrate NSwag Core to System.Text.Json with Local NJsonSchema Project References + +## Goal + +Remove `Newtonsoft.Json` from all NSwag projects except `NSwag.Generation.NewtonsoftJson` (new) and `NSwag.AspNet.WebApi` (legacy). The core NSwag libraries use only `System.Text.Json`. A global MSBuild switch allows toggling between local NJsonSchema project references (development) and NuGet packages (production). + +This is a **major breaking version** aligned with NJsonSchema v11's STJ migration. + +## Decisions + +| Decision | Choice | Rationale | +|---|---|---| +| Local/NuGet switch | `UseLocalNJsonSchemaProjects` property in `Directory.Build.props` | Same pattern as Namotion.Interceptor. Global property, per-csproj conditional ItemGroups. Local refs are for development/testing only. | +| Newtonsoft isolation | New `NSwag.Generation.NewtonsoftJson` project | Opt-in package for users whose types use Newtonsoft attributes. Mirrors ASP.NET Core's `AddNewtonsoftJson()` pattern. | +| NSwag.AspNet.WebApi | Keeps Newtonsoft | Old ASP.NET is inherently Newtonsoft-based. References `NSwag.Generation.NewtonsoftJson`. | +| ASP.NET Core Newtonsoft opt-in | Via `NSwag.Generation.NewtonsoftJson` package + service registration | Users who call `AddNewtonsoftJson()` on MVC also add this package. | +| OpenAPI model serialization | STJ attributes directly on NSwag.Core types | Same approach as NJsonSchema core migration. Clean break. | +| .nswag config files | Migrate to STJ serialization with backwards-compatible reading | Must round-trip existing .nswag files correctly. | + +## Project Structure After Migration + +| Project | Newtonsoft? | NJsonSchema Reference | +|---|---|---| +| NSwag.Core | No | `NJsonSchema` | +| NSwag.Core.Yaml | No | `NJsonSchema.Yaml` | +| NSwag.Annotations | No | None | +| NSwag.CodeGeneration | No | `NJsonSchema.CodeGeneration` | +| NSwag.CodeGeneration.CSharp | No | `NJsonSchema.CodeGeneration.CSharp` | +| NSwag.CodeGeneration.TypeScript | No | `NJsonSchema.CodeGeneration.TypeScript` | +| NSwag.Generation | No | `NJsonSchema` (via NSwag.Core) | +| NSwag.Generation.AspNetCore | No | None (via NSwag.Generation) | +| NSwag.Generation.WebApi | No | None (via NSwag.Generation) | +| NSwag.AspNetCore | No | None (via NSwag.Core) | +| NSwag.Commands | No | None (via other projects) | +| NSwag.ConsoleCore | No | None (via NSwag.Commands) | +| NSwag.Generation.NewtonsoftJson *(new)* | **Yes** | `NJsonSchema.NewtonsoftJson` | +| NSwag.AspNet.WebApi | **Yes** | Via `NSwag.Generation.NewtonsoftJson` | + +## Local/NuGet Reference Switch + +### Directory.Build.props (NSwag root) + +```xml + + + false + +``` + +### Per-csproj conditional blocks + +Each project that references NJsonSchema gets conditional ItemGroups: + +```xml + + + + + + + + +``` + +### Reference mapping + +| NSwag project | NuGet package | Local project path (relative from src/) | +|---|---|---| +| NSwag.Core | `NJsonSchema` | `../../../NJsonSchema/src/NJsonSchema/NJsonSchema.csproj` | +| NSwag.Core.Yaml | `NJsonSchema.Yaml` | `../../../NJsonSchema/src/NJsonSchema.Yaml/NJsonSchema.Yaml.csproj` | +| NSwag.CodeGeneration | `NJsonSchema.CodeGeneration` | `../../../NJsonSchema/src/NJsonSchema.CodeGeneration/NJsonSchema.CodeGeneration.csproj` | +| NSwag.CodeGeneration.CSharp | `NJsonSchema.CodeGeneration.CSharp` | `../../../NJsonSchema/src/NJsonSchema.CodeGeneration.CSharp/NJsonSchema.CodeGeneration.CSharp.csproj` | +| NSwag.CodeGeneration.TypeScript | `NJsonSchema.CodeGeneration.TypeScript` | `../../../NJsonSchema/src/NJsonSchema.CodeGeneration.TypeScript/NJsonSchema.CodeGeneration.TypeScript.csproj` | +| NSwag.Generation.NewtonsoftJson | `NJsonSchema.NewtonsoftJson` | `../../../NJsonSchema/src/NJsonSchema.NewtonsoftJson/NJsonSchema.NewtonsoftJson.csproj` | + +Assumes `NJsonSchema` and `NSwag` repos are sibling directories. + +## Breaking Changes + +- OpenAPI model types use STJ attributes instead of Newtonsoft attributes +- Extension data dictionaries become `IDictionary` instead of `IDictionary` +- `NSwag.Generation` no longer transitively pulls in Newtonsoft.Json +- Users relying on Newtonsoft-aware schema generation must add `NSwag.Generation.NewtonsoftJson` +- `.nswag` config files are serialized with STJ (reading existing files must remain backwards-compatible) +- Custom OpenAPI converters rewritten for STJ + +## Migration Details + +### NSwag.Core (28 files, bulk of work) + +**Attribute swaps (mechanical):** +- `[JsonProperty("name")]` → `[JsonPropertyName("name")]` +- `[JsonIgnore]` (Newtonsoft) → `[JsonIgnore]` (STJ) +- `[JsonConverter(typeof(StringEnumConverter))]` → `[JsonConverter(typeof(JsonStringEnumConverter))]` +- `[JsonExtensionData]` — same name, backing type: `IDictionary` → `IDictionary` +- `DefaultValueHandling.Ignore` → `[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]` + +**Custom converter rewrite:** +- `OpenApiPathItemConverter` (in `OpenApiPathItem.cs`) — full custom Read/Write with `$ref` handling, extension data, HTTP operation mapping. Rewrite as STJ `JsonConverter`. + +**Enum converters:** +- `OpenApiParameterStyle`, `OpenApiParameterKind`, `OpenApiParameterCollectionFormat`, `OpenApiOAuth2Flow`, `OpenApiSecurityScheme.In` — all use `[JsonConverter(typeof(StringEnumConverter))]` → `[JsonConverter(typeof(JsonStringEnumConverter))]` + +**Serialization infrastructure:** +- Uses NJsonSchema's `JsonSchemaSerialization` which has already been migrated to STJ +- Migrate from `CreateJsonSerializerContractResolver()` to `ConfigureJsonSerializerOptions()` + +### NSwag.Commands + +- `NSwagDocumentBase.cs`: `JObject.FromObject()` → `JsonSerializer.SerializeToNode()`, `JToken` → `JsonNode`, `JsonConvert` → `JsonSerializer` +- Other command files: lighter mechanical swaps + +### NSwag.Core.Yaml + +- Has its own direct Newtonsoft usage (not via NJsonSchema.Yaml): `JsonConvert.DeserializeObject(json, expConverter)` +- Replace with STJ-based JSON→dynamic conversion for YamlDotNet compatibility + +### NSwag.AspNetCore + +- `SwaggerUiSettings.cs`: `JsonConvert.SerializeObject()` → `JsonSerializer.Serialize()` +- `JsonExceptionFilterAttribute.cs`: same swap + +### NSwag.Generation.NewtonsoftJson (new project) + +- Newtonsoft-aware schema generation subclass/extension +- Service registration extensions (e.g. `UseNewtonsoftJson()` on swagger options) +- References: `NSwag.Generation` + `NJsonSchema.NewtonsoftJson` + +## Risk Areas + +### OpenAPI document serialization fidelity +Switching from Newtonsoft to STJ can subtly change JSON output (property ordering, null handling, number formatting). Mitigate with snapshot tests for representative OpenAPI documents before and after migration. + +### .nswag config file backwards compatibility +Existing `.nswag` files serialized with Newtonsoft must still deserialize correctly with STJ. Mitigate with dedicated round-trip tests. + +### NSwagStudio (net462 WPF app) +Transitively depends on everything via NSwag.Commands. STJ NuGet package should work on net462 (NJsonSchema already does), but needs build verification. + +### OpenApiPathItemConverter complexity +Moderate-complexity converter with $ref handling and extension data. Needs careful porting and test coverage. + +## Implementation Phases + +### Phase 0 — Setup & test hardening +- Add `UseLocalNJsonSchemaProjects` switch to `Directory.Build.props` +- Add conditional ItemGroups to all 6 NJsonSchema-referencing .csproj files +- Verify build works with local references (switch = true) +- Add snapshot tests for OpenAPI document serialization round-trips +- Add .nswag config file round-trip tests + +### Phase 1 — NSwag.Core attribute migration +- Replace all Newtonsoft attributes with STJ equivalents across 28 files +- Change extension data types from `JToken` to `JsonNode` +- Migrate `ConfigureJsonSerializerOptions()` usage + +### Phase 2 — OpenApiPathItemConverter rewrite +- Port to STJ `JsonConverter` +- Port enum StringEnumConverter usages to `JsonStringEnumConverter` + +### Phase 3 — NSwag.Commands migration +- Migrate `NSwagDocumentBase.cs` from JObject/JsonConvert to JsonNode/JsonSerializer +- Migrate other command files + +### Phase 4 — NSwag.Core.Yaml migration +- Replace direct Newtonsoft ExpandoObject conversion with STJ equivalent + +### Phase 5 — NSwag.AspNetCore cleanup +- Migrate SwaggerUiSettings and JsonExceptionFilterAttribute to STJ + +### Phase 6 — Create NSwag.Generation.NewtonsoftJson +- New project with Newtonsoft-aware schema generation +- Service registration extensions +- Move NSwag.AspNet.WebApi to reference this + +### Phase 7 — Remove Newtonsoft dependencies +- Remove `Newtonsoft.Json` PackageReferences from all projects except NSwag.Generation.NewtonsoftJson and NSwag.AspNet.WebApi +- Remove NJsonSchema.NewtonsoftJson references from NSwag.Generation +- Verify clean build on all TFMs + +### Phase 8 — Test updates & verification +- Update all test projects +- Verify snapshot tests pass +- Verify .nswag config file compatibility +- Verify NSwagStudio builds and runs +- Full test pass diff --git a/migration.md b/migration.md new file mode 100644 index 0000000000..cf87ed4caa --- /dev/null +++ b/migration.md @@ -0,0 +1,170 @@ +# NSwag Migration Guide: Newtonsoft.Json to System.Text.Json + +## Overview + +Starting with this release, **NSwag** core packages use **System.Text.Json** instead of Newtonsoft.Json for all serialization, deserialization, and document handling. + +If you need Newtonsoft.Json integration for schema generation (e.g., types decorated with `[JsonProperty]`), install **`NSwag.Generation.NewtonsoftJson`** and call `.UseNewtonsoftJson()`. + +This migration also requires the updated **NJsonSchema** packages (which underwent the same migration). See the [NJsonSchema migration guide](https://github.com/RicoSuter/NJsonSchema/blob/feature/migrate-core-to-stj/migration.md) for NJsonSchema-specific changes. + +--- + +## Quick Start + +### Default (System.Text.Json) + +No changes needed if your types use `[JsonPropertyName]` and other STJ attributes: + +```csharp +services.AddOpenApiDocument(settings => { + // Uses SystemTextJsonSchemaGeneratorSettings by default +}); +``` + +### Opt-in Newtonsoft.Json support + +Install `NSwag.Generation.NewtonsoftJson`, then: + +```csharp +services.AddOpenApiDocument(settings => { + settings.UseNewtonsoftJson(); +}); +``` + +--- + +## Breaking Changes + +### 1. `OpenApiDocument.ToJson(SchemaType, Formatting)` → `ToJson(SchemaType, bool)` + +```csharp +// Before +var json = document.ToJson(SchemaType.OpenApi3, Formatting.Indented); + +// After +var json = document.ToJson(SchemaType.OpenApi3, writeIndented: true); +``` + +### 2. `GetJsonSerializerContractResolver` → `GetSchemaSerializationConverter` + +```csharp +// Before +var resolver = OpenApiDocument.GetJsonSerializerContractResolver(schemaType); + +// After +var converter = OpenApiDocument.GetSchemaSerializationConverter(schemaType); +``` + +Returns `SchemaSerializationConverter` (a `JsonConverterFactory`) instead of `IContractResolver`. + +### 3. `AspNetCoreOpenApiDocumentGenerator.GetJsonSerializerSettings` deprecated + +```csharp +// Before +JsonSerializerSettings settings = generator.GetJsonSerializerSettings(); + +// After — method is [Obsolete], returns object +// Use NSwag.Generation.NewtonsoftJson for Newtonsoft support +``` + +### 4. `OpenApiPathItem.ExtensionData` type changed + +```csharp +// Before +IDictionary extensionData = pathItem.ExtensionData; + +// After +IDictionary extensionData = pathItem.ExtensionData; +``` + +### 5. NSwag.Generation no longer depends on NJsonSchema.NewtonsoftJson + +If you use `NewtonsoftJsonSchemaGeneratorSettings`, you must now install `NSwag.Generation.NewtonsoftJson`: + +```csharp +// Before (implicit): +settings.SchemaSettings = new NewtonsoftJsonSchemaGeneratorSettings { ... }; + +// After (explicit package required): +// Install NSwag.Generation.NewtonsoftJson +settings.UseNewtonsoftJson(); +``` + +### 6. `JsonExceptionFilterAttribute` behavioral change + +The exception serialization no longer uses `JsonExceptionConverter` for polymorphic exception handling. Custom exception properties are serialized as primitives only. The `searchedNamespaces` constructor parameter is no longer used for deserialization. + +### 7. Schema generation defaults to System.Text.Json + +`OpenApiDocumentGeneratorSettings.SchemaSettings` now defaults to `SystemTextJsonSchemaGeneratorSettings` instead of `NewtonsoftJsonSchemaGeneratorSettings`. Types decorated with `[JsonProperty]` will no longer have those attributes recognized unless you opt in via `UseNewtonsoftJson()`. + +--- + +## .nswag Document Compatibility + +Existing `.nswag` configuration files should continue to load. The serialization format uses `JsonNamingPolicy.CamelCase` to match the existing camelCase convention. + +If you encounter loading issues with manually edited `.nswag` files, ensure property names use camelCase (e.g., `"openApiToCSharpClient"`, not `"OpenApiToCSharpClient"`). + +--- + +## Behavioral Differences + +### Extension Data Types + +Extension data values may differ in type: +- Integers: `int` (if in range) instead of `long` +- Dates: remain as `string` instead of being auto-parsed to `DateTime` +- Objects: `JsonElement` or `Dictionary` instead of `JObject` + +### Property Ordering + +JSON property ordering in serialized OpenAPI documents may differ slightly. The semantic content is identical. + +### Lenient JSON Parsing + +Non-standard JSON (single quotes, unquoted property names, comments) is handled via: +- `AllowTrailingCommas = true` +- `ReadCommentHandling = JsonCommentHandling.Skip` +- Automatic fallback fixing for common issues + +--- + +## NSwag.Generation.NewtonsoftJson Package + +Provides opt-in Newtonsoft.Json support for NSwag document generation: + +- **`UseNewtonsoftJson()`** — extension method on `OpenApiDocumentGeneratorSettings` +- **`NewtonsoftJsonSettingsResolver`** — resolves Newtonsoft `JsonSerializerSettings` from DI + +### ASP.NET Core Setup + +```csharp +// In Program.cs / Startup.cs: +builder.Services.AddOpenApiDocument(settings => { + settings.UseNewtonsoftJson(); + // Optionally configure Newtonsoft settings: + settings.UseNewtonsoftJson(serializerSettings => { + serializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver(); + }); +}); +``` + +--- + +## Namespace Quick Reference + +| Old (Newtonsoft) | New (System.Text.Json) | +|---|---| +| `using Newtonsoft.Json` | `using System.Text.Json` | +| `using Newtonsoft.Json.Linq` | `using System.Text.Json.Nodes` | +| `Formatting.Indented` | `true` (bool) | +| `Formatting.None` | `false` (bool) | +| `IContractResolver` | `SchemaSerializationConverter` | +| `NewtonsoftJsonSchemaGeneratorSettings` | `SystemTextJsonSchemaGeneratorSettings` (default) | + +--- + +*Last updated: 2026-04-07* +*Branch: `feature/migrate-core-to-stj`* diff --git a/review.md b/review.md new file mode 100644 index 0000000000..38498bc5e0 --- /dev/null +++ b/review.md @@ -0,0 +1,174 @@ +# Code Review: NSwag — Newtonsoft.Json to System.Text.Json Migration + +## Scope + +Branch `feature/migrate-core-to-stj` — 146 files, ~3800 insertions / ~3200 deletions across 7 commits. + +--- + +## What Was Done Well + +- Clean separation: `NSwag.Generation.NewtonsoftJson` as opt-in package follows ASP.NET Core `AddNewtonsoftJson()` pattern +- `EnumMemberStringEnumConverter` correctly handles `[EnumMember]` attributes with AOT-compatible generic approach +- `OpenApiParameterJsonConverter` correctly resolves the `required` property collision (bool vs string[]) +- `OpenApiPathItemConverter` rewrite correctly handles dictionary-like serialization with HTTP methods as keys +- New snapshot tests cover minimal/full/security/round-trip scenarios +- `NSwagDocumentBase.GetSerializerOptions()` correctly returns static readonly instance +- Conditional `UseLocalNJsonSchemaProjects` for development with sibling NJsonSchema repo +- `NSwagServiceCollectionExtensions` uses reflection for optional Newtonsoft detection +- All Newtonsoft `using` statements removed from core projects +- `TransformLegacyDocument` correctly uses `.DeepClone()` for JsonNode parent ownership +- `[JsonObjectCreationHandling(Populate)]` correctly replaces Newtonsoft's auto-population on `OpenApiDocument.Components` +- `[JsonConstructor]` on `OpenApiComponents` for STJ deserialization +- `Lazy` caching in `OpenApiDocument.Serialization.cs` +- Property casing (`"DocumentGenerator"` → `"documentGenerator"`) is NOT a breaking change — Newtonsoft's `CamelCasePropertyNamesContractResolver` already produced camelCase + +--- + +## Critical Issues + +### 1. Recursive reverse rename destroys `deprecated` on `OpenApiOperation` +**Root cause:** NJsonSchema's `ApplyReverseRenamesRecursively` in `SchemaSerializationConverter`. + +The renames registered for `JsonSchema` include `x-deprecated` → `deprecated` (OpenApi3). During deserialization, the reverse rename `deprecated` → `x-deprecated` is applied to the **entire** JSON tree — including `OpenApiOperation` objects where `deprecated` is a native OpenAPI property mapped to `[JsonPropertyName("deprecated")]`. + +**Impact:** `OpenApiOperation.IsDeprecated` is never populated. All `[System.Obsolete]` and `@deprecated` annotations are dropped from generated code. Same issue affects `example` properties on non-schema objects. + +**Fix required in NJsonSchema.** See NJsonSchema review.md. + +### 2. `CodeGeneratorCollection` PascalCase breaks .nswag backward compatibility +**File:** `NSwag.Commands/CodeGeneratorCollection.cs:12,17,22` + +```csharp +[JsonPropertyName("OpenApiToTypeScriptClient")] // PascalCase +``` + +On master, Newtonsoft's `CamelCasePropertyNamesContractResolver` overrode `[JsonProperty("OpenApiToTypeScriptClient")]` to produce `"openApiToTypeScriptClient"` (camelCase). In STJ, `[JsonPropertyName]` takes precedence over `PropertyNamingPolicy` — so the output is now PascalCase. + +Existing `.nswag` files use camelCase keys (confirmed in `sample.nswag`). They will fail to load. + +**Fix:** Change to `[JsonPropertyName("openApiToTypeScriptClient")]`, etc. + +### 3. `UseLocalNJsonSchemaProjects` defaults to `true` +**File:** `Directory.Build.props:40` + +Every build attempts to resolve local NJsonSchema projects at `../NJsonSchema/`. Fails for CI, other developers, and NuGet packing. + +**Fix:** Default to `false`. + +--- + +## Major Issues + +### 4. `NSwag.Generation.NewtonsoftJson` not in solution file +**File:** `NSwag.sln` + +The new project is not listed in the solution. Won't appear in IDE, can't be independently built from solution. + +### 5. Breaking public API: `ToJson(SchemaType, Formatting)` → `ToJson(SchemaType, bool)` +**File:** `NSwag.Core/OpenApiDocument.cs:151` + +Parameter type changed from `Newtonsoft.Json.Formatting` enum to `bool writeIndented`. Compile-breaking for consumers. Intentional but must be documented. + +### 6. Breaking public API: `GetJsonSerializerContractResolver` → `GetSchemaSerializationConverter` +Method renamed with different return type. Compile-breaking for consumers. + +### 7. `JsonExceptionFilterAttribute` behavioral change +**File:** `NSwag.AspNetCore/JsonExceptionFilterAttribute.cs:129-160` + +- Lost polymorphic exception serialization (no longer uses `JsonExceptionConverter`) +- `_searchedNamespaces` constructor parameter is now dead code +- `JsonValue.Create()` at line 150 only supports primitive types — complex exception properties will fail (caught by try-catch but silently dropped) + +### 8. `OpenApiPathItem.ExtensionData` type changed +**File:** `NSwag.Core/OpenApiPathItem.cs:71` + +Changed from `IDictionary` to `IDictionary`. Breaking for consumers accessing extension data. + +### 9. No `PropertyNameCaseInsensitive` for .nswag deserialization +**File:** `NSwag.Commands/NSwagDocumentBase.cs:287-295` + +Newtonsoft's `CamelCasePropertyNamesContractResolver` was case-insensitive during deserialization. STJ's `CamelCase` naming policy only affects serialization. Manually edited `.nswag` files with mixed casing may fail. + +**Fix:** Add `PropertyNameCaseInsensitive = true` to serializer options. + +--- + +## Minor Issues + +- `NSwag.CodeGeneration.Tests` still references `NJsonSchema.NewtonsoftJson` but doesn't use it +- `hasSchemaAnnotations` check in `OpenApiDocumentGenerator.cs:128` uses default serializer options (fragile empty-schema detection) +- No STJ integration tests for AspNetCore generation (only Newtonsoft tests exist in `NSwag.Generation.AspNetCore.Tests`) +- Removed `UseRequiredKeywordNewtonsoftJsonSchemaGeneratorTests` without STJ replacement +- Blank line removal in generated controller code (cosmetic output change) +- `NSwagStudio` still uses `JsonConvert` in 3 files — relies on transitive Newtonsoft +- Several files use fully-qualified `System.Text.Json.JsonSerializer.Serialize(...)` instead of adding `using` +- `OpenApiOperation.ParametersRaw` removed `IsWriting` guard — changes serialization behavior for body parameters (likely an improvement but undocumented) +- `ConvertJsonNodeToExpandoObject` falls back to `null` for unhandled node types (could silently drop data) + +--- + +## Resolved / Non-Issues + +| Finding | Status | Notes | +|---------|--------|-------| +| `OpenApiParameterJsonConverter` omits `required: false` | **CORRECT** | Matches OpenAPI spec default | +| NSwag.AspNet.WebApi still references NJsonSchema.NewtonsoftJson | **INTENTIONAL** | Legacy net462 project | +| Test files still using NewtonsoftJsonSchemaGeneratorSettings | **CORRECT** | Tests backward-compat path via NSwag.Generation.NewtonsoftJson | +| `JsonStringEnumConverter` not AOT-compatible | **ACCEPTABLE** | NSwag CLI isn't AOT-compiled | + +--- + +## Known Open Issues (pre-existing, not from migration) + +### `PathItemTests.PathItem_With_External_Ref_Can_Be_Serialized` +External file reference resolution produces empty `OpenApiPathItem` because raw extension data isn't converted to typed objects. The reference resolver's `ResolveDocumentReference` needs to handle converting raw extension data to typed NSwag objects. + +### `HttpLoadingTests.When_openapi_is_loaded_without_scopes_it_should_deserialize` +Remote SaaS API spec deserialization fails, triggering `FixLenientJson` fallback which corrupts apostrophes in strings. Fix direction: identify root deserialization failure or make `FixLenientJson` safe with a proper tokenizer. + +### `NSwag.ConsoleCore.Tests` (6 tests) +Pre-existing `FileNotFoundException: openapi.json` — console tool integration tests require NSwag CLI to be built and generate sample specs. Not caused by migration. + +--- + +## Already Fixed During Review + +| # | Item | Fix | +|---|------|-----| +| 1 | NSwag.AspNetCore build error — hard Newtonsoft import | Replaced with reflection-based `TryCreateNewtonsoftJsonSchemaGeneratorSettings()` | +| 2 | NSwagDocumentBase null references (lines 181, 518) | Added `obj != null &&` guards and `?.` on `GetValue()` | +| 3 | JsonExceptionFilterAttribute safety | Added `MaxInnerExceptionDepth = 10`, wrapped property serialization in try-catch | +| 4 | `JsonSerializerOptions` allocation per call | Cached as `static readonly` field | +| 5 | YAML boolean/number type loss | Added `.WithAttemptingUnquotedStringTypeDeserialization()` in both repos | +| 6 | YAML null paths deserialization | Added `RemoveNullCollectionProperties()` in NJsonSchema converter | +| 7 | Namotion.Reflection crash on indexer properties | Added `GetIndexParameters().Length == 0` guards (in NJsonSchema) | +| 8 | YAML test Newtonsoft dependency | Migrated `YamlDocumentTests.cs` from `JObject` to `JsonNode` | +| 9 | AspNetCore.Tests missing project reference | Added `ProjectReference` to `NSwag.Generation.NewtonsoftJson` | +| 10 | QueryParametersTests JToken assertion | Changed to `Assert.Equal("42", ...?.ToString())` | +| 11 | Namotion.Reflection version | Bumped to 3.5.0 in both repos | + +--- + +## Priority Action Items + +### Must Fix Before Merge +1. `CodeGeneratorCollection` — change `[JsonPropertyName]` values to camelCase +2. `UseLocalNJsonSchemaProjects` — default to `false` in `Directory.Build.props` +3. Recursive reverse rename fix (in NJsonSchema, but blocks NSwag correctness) + +### Should Fix Before Release +4. Add `NSwag.Generation.NewtonsoftJson` to solution file +5. Add `PropertyNameCaseInsensitive = true` to NSwagDocumentBase options +6. Document all public API breaking changes in release notes + +### Nice to Have +7. Remove unused `NJsonSchema.NewtonsoftJson` reference from `NSwag.CodeGeneration.Tests` +8. Add STJ integration tests for AspNetCore generation +9. Clean up fully-qualified `System.Text.Json` references + +--- + +*Last updated: 2026-04-07* +*Branch: `feature/migrate-core-to-stj`* +*Tests: 854 passed, 0 failed (last full run)* diff --git a/src/NSwag.AspNetCore/Extensions/NSwagServiceCollectionExtensions.cs b/src/NSwag.AspNetCore/Extensions/NSwagServiceCollectionExtensions.cs index 5e0bb0ceb9..8dbefea67a 100644 --- a/src/NSwag.AspNetCore/Extensions/NSwagServiceCollectionExtensions.cs +++ b/src/NSwag.AspNetCore/Extensions/NSwagServiceCollectionExtensions.cs @@ -6,12 +6,12 @@ // Rico Suter, mail@rsuter.com //----------------------------------------------------------------------- +using System.Reflection; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.ApiDescriptions; using Microsoft.Extensions.Options; using NJsonSchema; using NJsonSchema.Generation; -using NJsonSchema.NewtonsoftJson.Generation; using NSwag.AspNetCore; using NSwag.Generation; using NSwag.Generation.AspNetCore; @@ -69,7 +69,9 @@ public static IServiceCollection AddSwaggerDocument(this IServiceCollection serv var hasSystemTextJsonOutputFormatter = mvcOptions.Value.OutputFormatters .Any(f => f.GetType().Name == "SystemTextJsonOutputFormatter"); +#pragma warning disable CS0618 // Obsolete: we use the reflection-based resolver directly here var newtonsoftSettings = AspNetCoreOpenApiDocumentGenerator.GetJsonSerializerSettings(services); +#pragma warning restore CS0618 var systemTextJsonOptions = hasSystemTextJsonOutputFormatter ? AspNetCoreOpenApiDocumentGenerator.GetSystemTextJsonSettings(services) #if NET6_0_OR_GREATER @@ -80,7 +82,15 @@ public static IServiceCollection AddSwaggerDocument(this IServiceCollection serv if (newtonsoftSettings != null && !hasSystemTextJsonOutputFormatter) { - settings.ApplySettings(new NewtonsoftJsonSchemaGeneratorSettings { SerializerSettings = newtonsoftSettings }, mvcOptions.Value); + var newtonsoftSchemaSettings = TryCreateNewtonsoftJsonSchemaGeneratorSettings(newtonsoftSettings); + if (newtonsoftSchemaSettings != null) + { + settings.ApplySettings(newtonsoftSchemaSettings, mvcOptions.Value); + } + else + { + settings.ApplySettings(new SystemTextJsonSchemaGeneratorSettings(), mvcOptions.Value); + } } else if (systemTextJsonOptions != null) { @@ -130,5 +140,26 @@ public static IServiceCollection AddSwagger(this IServiceCollection serviceColle { return AddSwaggerDocument(serviceCollection, configure); } + + private static JsonSchemaGeneratorSettings TryCreateNewtonsoftJsonSchemaGeneratorSettings(object newtonsoftSettings) + { + try + { + var assembly = Assembly.Load(new AssemblyName("NJsonSchema.NewtonsoftJson")); + var settingsType = assembly.GetType("NJsonSchema.NewtonsoftJson.Generation.NewtonsoftJsonSchemaGeneratorSettings"); + if (settingsType == null) + { + return null; + } + + var instance = Activator.CreateInstance(settingsType); + settingsType.GetProperty("SerializerSettings")?.SetValue(instance, newtonsoftSettings); + return instance as JsonSchemaGeneratorSettings; + } + catch + { + return null; + } + } } } diff --git a/src/NSwag.AspNetCore/JsonExceptionFilterAttribute.cs b/src/NSwag.AspNetCore/JsonExceptionFilterAttribute.cs index c6e959b54d..c2f26e22b2 100644 --- a/src/NSwag.AspNetCore/JsonExceptionFilterAttribute.cs +++ b/src/NSwag.AspNetCore/JsonExceptionFilterAttribute.cs @@ -7,11 +7,11 @@ //----------------------------------------------------------------------- using System.Reflection; +using System.Text.Json; +using System.Text.Json.Nodes; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Controllers; using Microsoft.AspNetCore.Mvc.Filters; -using Newtonsoft.Json; -using NJsonSchema.NewtonsoftJson.Converters; using NSwag.Annotations; using NSwag.Generation.AspNetCore; @@ -56,11 +56,20 @@ public override void OnActionExecuted(ActionExecutedContext context) { if (context.Exception != null && (_exceptionTypes.Count == 0 || _exceptionTypes.Exists(t => t.IsInstanceOfType(context.Exception)))) { - var settings = AspNetCoreOpenApiDocumentGenerator.GetJsonSerializerSettings(context.HttpContext?.RequestServices); - settings = settings != null ? CopySettings(settings) : (JsonConvert.DefaultSettings?.Invoke() ?? new JsonSerializerSettings()); - settings.Converters.Add(new JsonExceptionConverter(_hideStackTrace, _searchedNamespaces)); + var options = AspNetCoreOpenApiDocumentGenerator.GetSystemTextJsonSettings(context.HttpContext?.RequestServices) + ?? new JsonSerializerOptions(); + +#pragma warning disable CA1869 // options vary per request (from DI) + var optionsCopy = new JsonSerializerOptions(options) +#pragma warning restore CA1869 + { + DefaultIgnoreCondition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull, + WriteIndented = true + }; + + var json = System.Text.Json.JsonSerializer.Serialize( + SerializeException(context.Exception), optionsCopy); - var json = JsonConvert.SerializeObject(context.Exception, settings); context.Result = new ContentResult { StatusCode = GetStatusCode(context.Exception, context), @@ -112,23 +121,42 @@ private static int GetStatusCode(Exception exception, ActionExecutedContext cont return 500; } - private static JsonSerializerSettings CopySettings(JsonSerializerSettings settings) - { - var settingsCopy = new JsonSerializerSettings(); + private const int MaxInnerExceptionDepth = 10; - foreach (var property in typeof(JsonSerializerSettings) - .GetRuntimeProperties() - .Where(p => p.Name != "Converters")) - { - property.SetValue(settingsCopy, property.GetValue(settings)); - } + private static readonly HashSet _ignoredExceptionProperties = + ["Message", "StackTrace", "Source", "InnerException", "Data", "TargetSite", "HelpLink", "HResult"]; - foreach (var converter in settings.Converters) + private JsonObject SerializeException(Exception exception, int depth = 0) + { + var jsonObject = new JsonObject + { + ["discriminator"] = exception.GetType().Name, + ["Message"] = exception.Message, + ["StackTrace"] = _hideStackTrace ? "HIDDEN" : exception.StackTrace, + ["Source"] = exception.Source, + ["InnerException"] = exception.InnerException != null && depth < MaxInnerExceptionDepth + ? SerializeException(exception.InnerException, depth + 1) + : null + }; + + foreach (var property in exception.GetType().GetRuntimeProperties() + .Where(p => p.GetMethod?.IsPublic == true && !_ignoredExceptionProperties.Contains(p.Name))) { - settingsCopy.Converters.Add(converter); + try + { + var propertyValue = property.GetValue(exception); + if (propertyValue != null) + { + jsonObject[property.Name] = JsonValue.Create(propertyValue); + } + } + catch + { + // Skip properties that cannot be serialized (complex types, circular refs, etc.) + } } - return settingsCopy; + return jsonObject; } } } \ No newline at end of file diff --git a/src/NSwag.AspNetCore/SwaggerUiSettings.cs b/src/NSwag.AspNetCore/SwaggerUiSettings.cs index 663ff302db..2ef126cf78 100644 --- a/src/NSwag.AspNetCore/SwaggerUiSettings.cs +++ b/src/NSwag.AspNetCore/SwaggerUiSettings.cs @@ -11,7 +11,8 @@ using System.Collections; using System.Reflection; using System.Text; -using Newtonsoft.Json; +using System.Text.Json; +using System.Text.Json.Serialization; using NSwag.Generation; #if AspNetOwin @@ -145,7 +146,7 @@ internal override async Task TransformHtmlAsync(string html, HttpRequest var value = property.GetValue(oauth2Settings); if (value is ICollection collection) { - htmlBuilder.Replace("{" + property.Name + "}", JsonConvert.SerializeObject(collection)); + htmlBuilder.Replace("{" + property.Name + "}", System.Text.Json.JsonSerializer.Serialize(collection)); } else if (value is bool boolean) { @@ -163,7 +164,7 @@ internal override async Task TransformHtmlAsync(string html, HttpRequest htmlBuilder.Replace("{Urls}", swaggerRoutes.Count == 0 ? "undefined" - : JsonConvert.SerializeObject( + : System.Text.Json.JsonSerializer.Serialize( #pragma warning disable 618 swaggerRoutes.Select(r => new SwaggerUiRoute(r.Name, TransformToExternalPath(r.Url.Substring(MiddlewareBasePath?.Length ?? 0), request))) @@ -207,11 +208,11 @@ public SwaggerUiRoute(string name, string url) } /// Gets the route URL. - [JsonProperty("url")] + [JsonPropertyName("url")] public string Url { get; internal set; } /// Gets the route name. - [JsonProperty("name")] + [JsonPropertyName("name")] public string Name { get; internal set; } } } \ No newline at end of file diff --git a/src/NSwag.AspNetCore/SwaggerUiSettingsBase.cs b/src/NSwag.AspNetCore/SwaggerUiSettingsBase.cs index 721ab22cf0..7e9c50fcf2 100644 --- a/src/NSwag.AspNetCore/SwaggerUiSettingsBase.cs +++ b/src/NSwag.AspNetCore/SwaggerUiSettingsBase.cs @@ -10,7 +10,7 @@ #pragma warning disable CA1305 using NSwag.Generation; -using Newtonsoft.Json; +using System.Text.Json; using System.Text; #if AspNetOwin @@ -124,7 +124,7 @@ protected static string GenerateAdditionalSettings(IDictionary a var code = ""; foreach (var pair in additionalSettings) { - code += pair.Key + ": " + JsonConvert.SerializeObject(pair.Value) + ", \n "; + code += pair.Key + ": " + System.Text.Json.JsonSerializer.Serialize(pair.Value) + ", \n "; } return code; diff --git a/src/NSwag.CodeGeneration.CSharp.Tests/AllowNullableBodyParametersTests.cs b/src/NSwag.CodeGeneration.CSharp.Tests/AllowNullableBodyParametersTests.cs index ae34b0c38d..54d99874f3 100644 --- a/src/NSwag.CodeGeneration.CSharp.Tests/AllowNullableBodyParametersTests.cs +++ b/src/NSwag.CodeGeneration.CSharp.Tests/AllowNullableBodyParametersTests.cs @@ -1,5 +1,5 @@ using Microsoft.AspNetCore.Mvc; -using NJsonSchema.NewtonsoftJson.Generation; +using NJsonSchema.Generation; using NSwag.CodeGeneration.OperationNameGenerators; using NSwag.Generation.WebApi; using System.ComponentModel.DataAnnotations; @@ -87,7 +87,7 @@ private static async Task GenerateCode(bool allowNullable var swaggerGenerator = new WebApiOpenApiDocumentGenerator(new WebApiOpenApiDocumentGeneratorSettings { AllowNullableBodyParameters = allowNullableBodyParameters, - SchemaSettings = new NewtonsoftJsonSchemaGeneratorSettings() + SchemaSettings = new SystemTextJsonSchemaGeneratorSettings() }); var document = await swaggerGenerator.GenerateForControllerAsync(); diff --git a/src/NSwag.CodeGeneration.CSharp.Tests/CSharpClientSettingsTests.cs b/src/NSwag.CodeGeneration.CSharp.Tests/CSharpClientSettingsTests.cs index 0fa6f36758..ceb3be6d41 100644 --- a/src/NSwag.CodeGeneration.CSharp.Tests/CSharpClientSettingsTests.cs +++ b/src/NSwag.CodeGeneration.CSharp.Tests/CSharpClientSettingsTests.cs @@ -1,5 +1,5 @@ using Microsoft.AspNetCore.Mvc; -using NJsonSchema.NewtonsoftJson.Generation; +using NJsonSchema.Generation; using NSwag.CodeGeneration.Tests; using NSwag.Generation.WebApi; using System.Collections; @@ -32,7 +32,7 @@ public async Task When_ConfigurationClass_is_set_then_correct_ctor_is_generated( // Arrange var swaggerGenerator = new WebApiOpenApiDocumentGenerator(new WebApiOpenApiDocumentGeneratorSettings { - SchemaSettings = new NewtonsoftJsonSchemaGeneratorSettings() + SchemaSettings = new SystemTextJsonSchemaGeneratorSettings() }); var document = await swaggerGenerator.GenerateForControllerAsync(); @@ -56,7 +56,7 @@ public async Task When_UseHttpRequestMessageCreationMethod_is_set_then_CreateReq // Arrange var swaggerGenerator = new WebApiOpenApiDocumentGenerator(new WebApiOpenApiDocumentGeneratorSettings { - SchemaSettings = new NewtonsoftJsonSchemaGeneratorSettings() + SchemaSettings = new SystemTextJsonSchemaGeneratorSettings() }); var document = await swaggerGenerator.GenerateForControllerAsync(); @@ -80,7 +80,7 @@ public async Task WhenUsingBaseUrl_ButNoProperty_ThenPropertyIsNotUsedAndFieldIs // Arrange var swaggerGenerator = new WebApiOpenApiDocumentGenerator(new WebApiOpenApiDocumentGeneratorSettings { - SchemaSettings = new NewtonsoftJsonSchemaGeneratorSettings() + SchemaSettings = new SystemTextJsonSchemaGeneratorSettings() }); var document = await swaggerGenerator.GenerateForControllerAsync(); @@ -104,7 +104,7 @@ public async Task When_parameter_name_is_reserved_keyword_then_it_is_appended_wi // Arrange var swaggerGenerator = new WebApiOpenApiDocumentGenerator(new WebApiOpenApiDocumentGeneratorSettings { - SchemaSettings = new NewtonsoftJsonSchemaGeneratorSettings() + SchemaSettings = new SystemTextJsonSchemaGeneratorSettings() }); var document = await swaggerGenerator.GenerateForControllerAsync(); @@ -124,7 +124,7 @@ public async Task When_code_is_generated_then_by_default_the_system_httpclient_i // Arrange var swaggerGenerator = new WebApiOpenApiDocumentGenerator(new WebApiOpenApiDocumentGeneratorSettings { - SchemaSettings = new NewtonsoftJsonSchemaGeneratorSettings() + SchemaSettings = new SystemTextJsonSchemaGeneratorSettings() }); var document = await swaggerGenerator.GenerateForControllerAsync(); @@ -147,7 +147,7 @@ public async Task When_custom_http_client_type_is_specified_then_an_instance_of_ // Arrange var swaggerGenerator = new WebApiOpenApiDocumentGenerator(new WebApiOpenApiDocumentGeneratorSettings { - SchemaSettings = new NewtonsoftJsonSchemaGeneratorSettings() + SchemaSettings = new SystemTextJsonSchemaGeneratorSettings() }); var document = await swaggerGenerator.GenerateForControllerAsync(); @@ -170,7 +170,7 @@ public async Task When_client_base_interface_is_not_specified_then_client_interf // Arrange var swaggerGenerator = new WebApiOpenApiDocumentGenerator(new WebApiOpenApiDocumentGeneratorSettings { - SchemaSettings = new NewtonsoftJsonSchemaGeneratorSettings() + SchemaSettings = new SystemTextJsonSchemaGeneratorSettings() }); var document = await swaggerGenerator.GenerateForControllerAsync(); @@ -193,7 +193,7 @@ public async Task When_client_base_interface_is_not_specified_then_client_interf // Arrange var swaggerGenerator = new WebApiOpenApiDocumentGenerator(new WebApiOpenApiDocumentGeneratorSettings { - SchemaSettings = new NewtonsoftJsonSchemaGeneratorSettings() + SchemaSettings = new SystemTextJsonSchemaGeneratorSettings() }); var document = await swaggerGenerator.GenerateForControllerAsync(); @@ -217,7 +217,7 @@ public async Task When_client_base_interface_is_specified_then_client_interface_ // Arrange var swaggerGenerator = new WebApiOpenApiDocumentGenerator(new WebApiOpenApiDocumentGeneratorSettings { - SchemaSettings = new NewtonsoftJsonSchemaGeneratorSettings() + SchemaSettings = new SystemTextJsonSchemaGeneratorSettings() }); var document = await swaggerGenerator.GenerateForControllerAsync(); @@ -240,7 +240,7 @@ public async Task When_client_base_interface_is_specified_with_access_modifier_t // Arrange var swaggerGenerator = new WebApiOpenApiDocumentGenerator(new WebApiOpenApiDocumentGeneratorSettings { - SchemaSettings = new NewtonsoftJsonSchemaGeneratorSettings() + SchemaSettings = new SystemTextJsonSchemaGeneratorSettings() }); var document = await swaggerGenerator.GenerateForControllerAsync(); @@ -264,7 +264,7 @@ public async Task When_client_class_generation_is_enabled_and_suppressed_then_cl // Arrange var swaggerGenerator = new WebApiOpenApiDocumentGenerator(new WebApiOpenApiDocumentGeneratorSettings { - SchemaSettings = new NewtonsoftJsonSchemaGeneratorSettings() + SchemaSettings = new SystemTextJsonSchemaGeneratorSettings() }); var document = await swaggerGenerator.GenerateForControllerAsync(); @@ -290,7 +290,7 @@ public async Task When_client_interface_generation_is_enabled_and_suppressed_the // Arrange var swaggerGenerator = new WebApiOpenApiDocumentGenerator(new WebApiOpenApiDocumentGeneratorSettings { - SchemaSettings = new NewtonsoftJsonSchemaGeneratorSettings() + SchemaSettings = new SystemTextJsonSchemaGeneratorSettings() }); var document = await swaggerGenerator.GenerateForControllerAsync(); @@ -334,7 +334,7 @@ string[] operationIds // Arrange var swaggerGenerator = new WebApiOpenApiDocumentGenerator(new WebApiOpenApiDocumentGeneratorSettings { - SchemaSettings = new NewtonsoftJsonSchemaGeneratorSettings() + SchemaSettings = new SystemTextJsonSchemaGeneratorSettings() }); var document = await swaggerGenerator.GenerateForControllerAsync(); @@ -365,7 +365,7 @@ string[] includedOperationIds // Arrange var swaggerGenerator = new WebApiOpenApiDocumentGenerator(new WebApiOpenApiDocumentGeneratorSettings { - SchemaSettings = new NewtonsoftJsonSchemaGeneratorSettings() + SchemaSettings = new SystemTextJsonSchemaGeneratorSettings() }); var document = await swaggerGenerator.GenerateForControllerAsync(); @@ -394,7 +394,7 @@ string[] excludedOperationIds // Arrange var swaggerGenerator = new WebApiOpenApiDocumentGenerator(new WebApiOpenApiDocumentGeneratorSettings { - SchemaSettings = new NewtonsoftJsonSchemaGeneratorSettings() + SchemaSettings = new SystemTextJsonSchemaGeneratorSettings() }); var document = await swaggerGenerator.GenerateForControllerAsync(); diff --git a/src/NSwag.CodeGeneration.CSharp.Tests/CodeGenerationTests.cs b/src/NSwag.CodeGeneration.CSharp.Tests/CodeGenerationTests.cs index 926a005215..f64cc01fb4 100644 --- a/src/NSwag.CodeGeneration.CSharp.Tests/CodeGenerationTests.cs +++ b/src/NSwag.CodeGeneration.CSharp.Tests/CodeGenerationTests.cs @@ -1,7 +1,6 @@ using System.ComponentModel.DataAnnotations; using NJsonSchema; using NJsonSchema.Generation; -using NJsonSchema.NewtonsoftJson.Generation; using NSwag.CodeGeneration.Tests; namespace NSwag.CodeGeneration.CSharp.Tests @@ -409,7 +408,7 @@ public async Task When_media_type_contains_quotes_can_generate_client() private static OpenApiDocument CreateDocument() { var document = new OpenApiDocument(); - var settings = new NewtonsoftJsonSchemaGeneratorSettings(); + var settings = new SystemTextJsonSchemaGeneratorSettings(); var generator = new JsonSchemaGenerator(settings); document.Paths["/Person"] = new OpenApiPathItem diff --git a/src/NSwag.CodeGeneration.CSharp.Tests/ControllerGenerationFormatTests.cs b/src/NSwag.CodeGeneration.CSharp.Tests/ControllerGenerationFormatTests.cs index 4b9123fe2a..735adf5221 100644 --- a/src/NSwag.CodeGeneration.CSharp.Tests/ControllerGenerationFormatTests.cs +++ b/src/NSwag.CodeGeneration.CSharp.Tests/ControllerGenerationFormatTests.cs @@ -1,6 +1,6 @@ using System.Text.RegularExpressions; using NJsonSchema; -using NJsonSchema.NewtonsoftJson.Generation; +using NJsonSchema.Generation; using NSwag.CodeGeneration.CSharp.Models; using NSwag.CodeGeneration.OperationNameGenerators; using NSwag.CodeGeneration.Tests; @@ -244,7 +244,7 @@ private static OpenApiDocument GetOpenApiDocument() complexTypeReponseSchema.Properties["Prop3"] = new JsonSchemaProperty { Type = JsonObjectType.Boolean, IsRequired = true }; complexTypeReponseSchema.Properties["Prop4"] = new JsonSchemaProperty { Type = JsonObjectType.Object, Reference = complexTypeSchema, IsRequired = true }; - var typeString = NewtonsoftJsonSchemaGenerator.FromType(); + var typeString = JsonSchema.FromType(); var document = new OpenApiDocument(); document.Paths["Foo"] = new OpenApiPathItem diff --git a/src/NSwag.CodeGeneration.CSharp.Tests/FileDownloadTests.cs b/src/NSwag.CodeGeneration.CSharp.Tests/FileDownloadTests.cs index 9f0cbc900a..caa883bb1d 100644 --- a/src/NSwag.CodeGeneration.CSharp.Tests/FileDownloadTests.cs +++ b/src/NSwag.CodeGeneration.CSharp.Tests/FileDownloadTests.cs @@ -1,5 +1,5 @@ using Microsoft.AspNetCore.Mvc; -using NJsonSchema.NewtonsoftJson.Generation; +using NJsonSchema.Generation; using NSwag.CodeGeneration.Tests; using NSwag.Generation.WebApi; @@ -22,7 +22,7 @@ public async Task When_response_is_file_and_stream_is_not_used_then_byte_array_i // Arrange var swaggerGenerator = new WebApiOpenApiDocumentGenerator(new WebApiOpenApiDocumentGeneratorSettings { - SchemaSettings = new NewtonsoftJsonSchemaGeneratorSettings() + SchemaSettings = new SystemTextJsonSchemaGeneratorSettings() }); var document = await swaggerGenerator.GenerateForControllerAsync(); diff --git a/src/NSwag.CodeGeneration.CSharp.Tests/FileTests.cs b/src/NSwag.CodeGeneration.CSharp.Tests/FileTests.cs index 137f89dc31..b49c215cb6 100644 --- a/src/NSwag.CodeGeneration.CSharp.Tests/FileTests.cs +++ b/src/NSwag.CodeGeneration.CSharp.Tests/FileTests.cs @@ -1,5 +1,5 @@ using Microsoft.AspNetCore.Mvc; -using NJsonSchema.NewtonsoftJson.Generation; +using NJsonSchema.Generation; using NSwag.CodeGeneration.Tests; using NSwag.Generation.WebApi; @@ -22,7 +22,7 @@ public async Task When_file_is_generated_system_alias_is_there() // Arrange var swaggerGenerator = new WebApiOpenApiDocumentGenerator(new WebApiOpenApiDocumentGeneratorSettings { - SchemaSettings = new NewtonsoftJsonSchemaGeneratorSettings() + SchemaSettings = new SystemTextJsonSchemaGeneratorSettings() }); var document = await swaggerGenerator.GenerateForControllerAsync(); diff --git a/src/NSwag.CodeGeneration.CSharp.Tests/FormParameterTests.cs b/src/NSwag.CodeGeneration.CSharp.Tests/FormParameterTests.cs index 87a9519f3d..3e34558046 100644 --- a/src/NSwag.CodeGeneration.CSharp.Tests/FormParameterTests.cs +++ b/src/NSwag.CodeGeneration.CSharp.Tests/FormParameterTests.cs @@ -1,6 +1,6 @@ using Microsoft.AspNetCore.Mvc; using NJsonSchema; -using NJsonSchema.NewtonsoftJson.Generation; +using NJsonSchema.Generation; using NSwag.CodeGeneration.Tests; using NSwag.Generation.WebApi; @@ -68,7 +68,7 @@ public async Task When_action_has_file_parameter_then_Stream_is_generated_in_CSh // Arrange var generator = new WebApiOpenApiDocumentGenerator(new WebApiOpenApiDocumentGeneratorSettings { - SchemaSettings = new NewtonsoftJsonSchemaGeneratorSettings() + SchemaSettings = new SystemTextJsonSchemaGeneratorSettings() }); var document = await generator.GenerateForControllerAsync(); diff --git a/src/NSwag.CodeGeneration.CSharp.Tests/HeadRequestTests.cs b/src/NSwag.CodeGeneration.CSharp.Tests/HeadRequestTests.cs index 057e60b37c..d4e8eaba9c 100644 --- a/src/NSwag.CodeGeneration.CSharp.Tests/HeadRequestTests.cs +++ b/src/NSwag.CodeGeneration.CSharp.Tests/HeadRequestTests.cs @@ -1,5 +1,5 @@ using Microsoft.AspNetCore.Mvc; -using NJsonSchema.NewtonsoftJson.Generation; +using NJsonSchema.Generation; using NSwag.CodeGeneration.Tests; using NSwag.Generation.WebApi; @@ -21,7 +21,7 @@ public async Task When_operation_is_HTTP_head_then_no_content_is_not_used() // Arrange var generator = new WebApiOpenApiDocumentGenerator(new WebApiOpenApiDocumentGeneratorSettings { - SchemaSettings = new NewtonsoftJsonSchemaGeneratorSettings() + SchemaSettings = new SystemTextJsonSchemaGeneratorSettings() }); var document = await generator.GenerateForControllerAsync(); diff --git a/src/NSwag.CodeGeneration.CSharp.Tests/OptionalParameterTests.cs b/src/NSwag.CodeGeneration.CSharp.Tests/OptionalParameterTests.cs index 7efd971c3b..463b55bef5 100644 --- a/src/NSwag.CodeGeneration.CSharp.Tests/OptionalParameterTests.cs +++ b/src/NSwag.CodeGeneration.CSharp.Tests/OptionalParameterTests.cs @@ -1,6 +1,6 @@ using Microsoft.AspNetCore.Mvc; using NJsonSchema; -using NJsonSchema.NewtonsoftJson.Generation; +using NJsonSchema.Generation; using NSwag.CodeGeneration.Tests; using NSwag.Generation.WebApi; @@ -54,7 +54,7 @@ public async Task When_setting_is_enabled_with_enum_fromuri_should_make_enum_nul // Arrange var generator = new WebApiOpenApiDocumentGenerator(new WebApiOpenApiDocumentGeneratorSettings { - SchemaSettings = new NewtonsoftJsonSchemaGeneratorSettings() + SchemaSettings = new SystemTextJsonSchemaGeneratorSettings() }); var document = await generator.GenerateForControllerAsync(); @@ -77,7 +77,7 @@ public async Task When_setting_is_enabled_with_class_fromuri_should_make_enum_nu // Arrange var generator = new WebApiOpenApiDocumentGenerator(new WebApiOpenApiDocumentGeneratorSettings { - SchemaSettings = new NewtonsoftJsonSchemaGeneratorSettings() + SchemaSettings = new SystemTextJsonSchemaGeneratorSettings() }); var document = await generator.GenerateForControllerAsync(); @@ -100,7 +100,7 @@ public async Task When_setting_is_enabled_then_optional_parameters_have_null_opt // Arrange var generator = new WebApiOpenApiDocumentGenerator(new WebApiOpenApiDocumentGeneratorSettings { - SchemaSettings = new NewtonsoftJsonSchemaGeneratorSettings() + SchemaSettings = new SystemTextJsonSchemaGeneratorSettings() }); var document = await generator.GenerateForControllerAsync(); @@ -122,7 +122,7 @@ public async Task When_setting_is_enabled_then_parameters_are_reordered() { var generator = new WebApiOpenApiDocumentGenerator(new WebApiOpenApiDocumentGeneratorSettings { - SchemaSettings = new NewtonsoftJsonSchemaGeneratorSettings { SchemaType = SchemaType.Swagger2 } + SchemaSettings = new SystemTextJsonSchemaGeneratorSettings { SchemaType = SchemaType.Swagger2 } }); var document = await generator.GenerateForControllerAsync(); diff --git a/src/NSwag.CodeGeneration.CSharp.Tests/RequiredParameterTests.cs b/src/NSwag.CodeGeneration.CSharp.Tests/RequiredParameterTests.cs index e87cdab8c3..b5d2aad0a6 100644 --- a/src/NSwag.CodeGeneration.CSharp.Tests/RequiredParameterTests.cs +++ b/src/NSwag.CodeGeneration.CSharp.Tests/RequiredParameterTests.cs @@ -1,7 +1,6 @@ using System.ComponentModel.DataAnnotations; using Microsoft.AspNetCore.Mvc; using NJsonSchema.Generation; -using NJsonSchema.NewtonsoftJson.Generation; using NSwag.CodeGeneration.Tests; using NSwag.Generation.WebApi; @@ -116,10 +115,6 @@ public async Task When_setting_is_disabled_properties_with_required_keyword_and_ } } - public class UseRequiredKeywordNewtonsoftJsonSchemaGeneratorTests : UseRequiredKeywordTests - { - } - public class UseRequiredKeywordSystemTextJsonSchemaGeneratorSettingsTests : UseRequiredKeywordTests { } diff --git a/src/NSwag.CodeGeneration.CSharp.Tests/Snapshots/JIRA_OpenAPI.verified.txt b/src/NSwag.CodeGeneration.CSharp.Tests/Snapshots/JIRA_OpenAPI.verified.txt index 3801489d02..b33ceca3de 100644 --- a/src/NSwag.CodeGeneration.CSharp.Tests/Snapshots/JIRA_OpenAPI.verified.txt +++ b/src/NSwag.CodeGeneration.CSharp.Tests/Snapshots/JIRA_OpenAPI.verified.txt @@ -79318,7 +79318,7 @@ namespace MyNamespace /// Map of objects representing additional details for an error /// [Newtonsoft.Json.JsonProperty("details", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] - public System.Collections.Generic.IDictionary Details { get; set; } + public object Details { get; set; } /// /// The list of error messages produced by this operation. For example, "input parameter 'key' must be provided" @@ -82102,7 +82102,7 @@ namespace MyNamespace public string Expand { get; set; } [Newtonsoft.Json.JsonProperty("fields", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] - public System.Collections.Generic.IDictionary Fields { get; set; } + public object Fields { get; set; } [Newtonsoft.Json.JsonProperty("fieldsToInclude", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] public IncludedFields FieldsToInclude { get; set; } @@ -82591,7 +82591,7 @@ namespace MyNamespace /// The properties of the object, as arbitrary key-value pairs. These properties can be searched using JQL, if the extractions (see [Issue Field Option Property Index](https://developer.atlassian.com/cloud/jira/platform/modules/issue-field-option-property-index/)) are defined in the descriptor for the issue field module. /// [Newtonsoft.Json.JsonProperty("properties", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] - public System.Collections.Generic.IDictionary Properties { get; set; } + public object Properties { get; set; } /// /// The option's name, which is displayed in Jira. @@ -82632,7 +82632,7 @@ namespace MyNamespace /// The properties of the option as arbitrary key-value pairs. These properties can be searched using JQL, if the extractions (see https://developer.atlassian.com/cloud/jira/platform/modules/issue-field-option-property-index/) are defined in the descriptor for the issue field module. /// [Newtonsoft.Json.JsonProperty("properties", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] - public System.Collections.Generic.IDictionary Properties { get; set; } + public object Properties { get; set; } /// /// The option's name, which is displayed in Jira. @@ -84093,7 +84093,7 @@ namespace MyNamespace /// List of issue screen fields to update, specifying the sub-field to update and its value for each field. This field provides a straightforward option when setting a sub-field. When multiple sub-fields or other operations are required, use `update`. Fields included in here cannot be included in `update`. /// [Newtonsoft.Json.JsonProperty("fields", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] - public System.Collections.Generic.IDictionary Fields { get; set; } + public object Fields { get; set; } /// /// Additional issue history details. @@ -85310,7 +85310,7 @@ namespace MyNamespace { [Newtonsoft.Json.JsonProperty("adfValue", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] - public System.Collections.Generic.IDictionary AdfValue { get; set; } + public object AdfValue { get; set; } } diff --git a/src/NSwag.CodeGeneration.CSharp.Tests/Snapshots/UseRequiredKeywordNewtonsoftJsonSchemaGeneratorTests.When_setting_is_disabled_properties_with_required_attribute_should_generate_with_required_keyword.verified.txt b/src/NSwag.CodeGeneration.CSharp.Tests/Snapshots/UseRequiredKeywordNewtonsoftJsonSchemaGeneratorTests.When_setting_is_disabled_properties_with_required_attribute_should_generate_with_required_keyword.verified.txt deleted file mode 100644 index d5828be5a8..0000000000 --- a/src/NSwag.CodeGeneration.CSharp.Tests/Snapshots/UseRequiredKeywordNewtonsoftJsonSchemaGeneratorTests.When_setting_is_disabled_properties_with_required_attribute_should_generate_with_required_keyword.verified.txt +++ /dev/null @@ -1,310 +0,0 @@ - - -namespace MyNamespace -{ - using System = global::System; - - public partial class TestClient - { - #pragma warning disable 8618 - private string _baseUrl; - #pragma warning restore 8618 - - private System.Net.Http.HttpClient _httpClient; - private static System.Lazy _settings = new System.Lazy(CreateSerializerSettings, true); - private Newtonsoft.Json.JsonSerializerSettings _instanceSettings; - - #pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. - public TestClient(string baseUrl, System.Net.Http.HttpClient httpClient) - #pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. - { - BaseUrl = baseUrl; - _httpClient = httpClient; - Initialize(); - } - - private static Newtonsoft.Json.JsonSerializerSettings CreateSerializerSettings() - { - var settings = new Newtonsoft.Json.JsonSerializerSettings(); - UpdateJsonSerializerSettings(settings); - return settings; - } - - public string BaseUrl - { - get { return _baseUrl; } - set - { - _baseUrl = value; - if (!string.IsNullOrEmpty(_baseUrl) && !_baseUrl.EndsWith("/")) - _baseUrl += '/'; - } - } - - protected Newtonsoft.Json.JsonSerializerSettings JsonSerializerSettings { get { return _instanceSettings ?? _settings.Value; } } - - static partial void UpdateJsonSerializerSettings(Newtonsoft.Json.JsonSerializerSettings settings); - - partial void Initialize(); - - partial void PrepareRequest(System.Net.Http.HttpClient client, System.Net.Http.HttpRequestMessage request, string url); - partial void PrepareRequest(System.Net.Http.HttpClient client, System.Net.Http.HttpRequestMessage request, System.Text.StringBuilder urlBuilder); - partial void ProcessResponse(System.Net.Http.HttpClient client, System.Net.Http.HttpResponseMessage response); - - public virtual System.Threading.Tasks.Task TestWithInputAsync(DTOWithRequiredFieldsOfNewtonsoftJsonSchemaGeneratorSettings input) - { - return TestWithInputAsync(input, System.Threading.CancellationToken.None); - } - - public virtual async System.Threading.Tasks.Task TestWithInputAsync(DTOWithRequiredFieldsOfNewtonsoftJsonSchemaGeneratorSettings input, System.Threading.CancellationToken cancellationToken) - { - var client_ = _httpClient; - var disposeClient_ = false; - try - { - using (var request_ = new System.Net.Http.HttpRequestMessage()) - { - var json_ = Newtonsoft.Json.JsonConvert.SerializeObject(input, JsonSerializerSettings); - var content_ = new System.Net.Http.StringContent(json_); - content_.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse("application/json"); - request_.Content = content_; - request_.Method = new System.Net.Http.HttpMethod("POST"); - - var urlBuilder_ = new System.Text.StringBuilder(); - if (!string.IsNullOrEmpty(_baseUrl)) urlBuilder_.Append(_baseUrl); - // Operation Path: "TestWithInput" - urlBuilder_.Append("TestWithInput"); - - PrepareRequest(client_, request_, urlBuilder_); - - var url_ = urlBuilder_.ToString(); - request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); - - PrepareRequest(client_, request_, url_); - - var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); - var disposeResponse_ = true; - try - { - var headers_ = new System.Collections.Generic.Dictionary>(); - foreach (var item_ in response_.Headers) - headers_[item_.Key] = item_.Value; - if (response_.Content != null && response_.Content.Headers != null) - { - foreach (var item_ in response_.Content.Headers) - headers_[item_.Key] = item_.Value; - } - - ProcessResponse(client_, response_); - - var status_ = (int)response_.StatusCode; - if (status_ == 204) - { - return; - } - else - { - var responseData_ = response_.Content == null ? null : await ReadAsStringAsync(response_.Content, cancellationToken).ConfigureAwait(false); - throw new ApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null); - } - } - finally - { - if (disposeResponse_) - response_.Dispose(); - } - } - } - finally - { - if (disposeClient_) - client_.Dispose(); - } - } - - protected struct ObjectResponseResult - { - public ObjectResponseResult(T responseObject, string responseText) - { - this.Object = responseObject; - this.Text = responseText; - } - - public T Object { get; } - - public string Text { get; } - } - - [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] - private static System.Threading.Tasks.Task ReadAsStringAsync(System.Net.Http.HttpContent content, System.Threading.CancellationToken cancellationToken) - { - #if NET5_0_OR_GREATER - return content.ReadAsStringAsync(cancellationToken); - #else - return content.ReadAsStringAsync(); - #endif - } - - [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] - private static System.Threading.Tasks.Task ReadAsStreamAsync(System.Net.Http.HttpContent content, System.Threading.CancellationToken cancellationToken) - { - #if NET5_0_OR_GREATER - return content.ReadAsStreamAsync(cancellationToken); - #else - return content.ReadAsStreamAsync(); - #endif - } - - public bool ReadResponseAsString { get; set; } - - protected virtual async System.Threading.Tasks.Task> ReadObjectResponseAsync(System.Net.Http.HttpResponseMessage response, System.Collections.Generic.IReadOnlyDictionary> headers, System.Threading.CancellationToken cancellationToken) - { - if (response == null || response.Content == null) - { - return new ObjectResponseResult(default(T), string.Empty); - } - - if (ReadResponseAsString) - { - var responseText = await ReadAsStringAsync(response.Content, cancellationToken).ConfigureAwait(false); - try - { - var typedBody = Newtonsoft.Json.JsonConvert.DeserializeObject(responseText, JsonSerializerSettings); - return new ObjectResponseResult(typedBody, responseText); - } - catch (Newtonsoft.Json.JsonException exception) - { - var message = "Could not deserialize the response body string as " + typeof(T).FullName + "."; - throw new ApiException(message, (int)response.StatusCode, responseText, headers, exception); - } - } - else - { - try - { - using (var responseStream = await ReadAsStreamAsync(response.Content, cancellationToken).ConfigureAwait(false)) - using (var streamReader = new System.IO.StreamReader(responseStream)) - using (var jsonTextReader = new Newtonsoft.Json.JsonTextReader(streamReader)) - { - var serializer = Newtonsoft.Json.JsonSerializer.Create(JsonSerializerSettings); - var typedBody = serializer.Deserialize(jsonTextReader); - return new ObjectResponseResult(typedBody, string.Empty); - } - } - catch (Newtonsoft.Json.JsonException exception) - { - var message = "Could not deserialize the response body stream as " + typeof(T).FullName + "."; - throw new ApiException(message, (int)response.StatusCode, string.Empty, headers, exception); - } - } - } - - private string ConvertToString(object value, System.Globalization.CultureInfo cultureInfo) - { - if (value == null) - { - return ""; - } - - if (value is System.Enum) - { - var name = System.Enum.GetName(value.GetType(), value); - if (name != null) - { - var field_ = System.Reflection.IntrospectionExtensions.GetTypeInfo(value.GetType()).GetDeclaredField(name); - if (field_ != null) - { - var attribute = System.Reflection.CustomAttributeExtensions.GetCustomAttribute(field_, typeof(System.Runtime.Serialization.EnumMemberAttribute)) - as System.Runtime.Serialization.EnumMemberAttribute; - if (attribute != null) - { - return attribute.Value != null ? attribute.Value : name; - } - } - - var converted = System.Convert.ToString(System.Convert.ChangeType(value, System.Enum.GetUnderlyingType(value.GetType()), cultureInfo)); - return converted == null ? string.Empty : converted; - } - } - else if (value is bool) - { - return System.Convert.ToString((bool)value, cultureInfo).ToLowerInvariant(); - } - else if (value is byte[]) - { - return System.Convert.ToBase64String((byte[]) value); - } - else if (value is string[]) - { - return string.Join(",", (string[])value); - } - else if (value.GetType().IsArray) - { - var valueArray = (System.Array)value; - var valueTextArray = new string[valueArray.Length]; - for (var i = 0; i < valueArray.Length; i++) - { - valueTextArray[i] = ConvertToString(valueArray.GetValue(i), cultureInfo); - } - return string.Join(",", valueTextArray); - } - - var result = System.Convert.ToString(value, cultureInfo); - return result == null ? "" : result; - } - } - - public partial class DTOWithRequiredFieldsOfNewtonsoftJsonSchemaGeneratorSettings - { - - [Newtonsoft.Json.JsonProperty("RequiredByAttribute", Required = Newtonsoft.Json.Required.Always)] - public int RequiredByAttribute { get; set; } - - [Newtonsoft.Json.JsonProperty("RequiredByC11Keyword", Required = Newtonsoft.Json.Required.AllowNull)] - public string RequiredByC11Keyword { get; set; } - - [Newtonsoft.Json.JsonProperty("RequiredByAttributeAndC11Keyword", Required = Newtonsoft.Json.Required.Always)] - [System.ComponentModel.DataAnnotations.Required] - public string RequiredByAttributeAndC11Keyword { get; set; } - - [Newtonsoft.Json.JsonProperty("NotRequired", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] - public string NotRequired { get; set; } - - } - - - - public partial class ApiException : System.Exception - { - public int StatusCode { get; private set; } - - public string Response { get; private set; } - - public System.Collections.Generic.IReadOnlyDictionary> Headers { get; private set; } - - public ApiException(string message, int statusCode, string response, System.Collections.Generic.IReadOnlyDictionary> headers, System.Exception innerException) - : base(message + "\n\nStatus: " + statusCode + "\nResponse: \n" + ((response == null) ? "(null)" : response.Substring(0, response.Length >= 512 ? 512 : response.Length)), innerException) - { - StatusCode = statusCode; - Response = response; - Headers = headers; - } - - public override string ToString() - { - return string.Format("HTTP Response: \n\n{0}\n\n{1}", Response, base.ToString()); - } - } - - public partial class ApiException : ApiException - { - public TResult Result { get; private set; } - - public ApiException(string message, int statusCode, string response, System.Collections.Generic.IReadOnlyDictionary> headers, TResult result, System.Exception innerException) - : base(message, statusCode, response, headers, innerException) - { - Result = result; - } - } - -} diff --git a/src/NSwag.CodeGeneration.CSharp.Tests/Snapshots/UseRequiredKeywordNewtonsoftJsonSchemaGeneratorTests.When_setting_is_disabled_properties_with_required_keyword_and_attribute_should_generate_with_required_keyword.verified.txt b/src/NSwag.CodeGeneration.CSharp.Tests/Snapshots/UseRequiredKeywordNewtonsoftJsonSchemaGeneratorTests.When_setting_is_disabled_properties_with_required_keyword_and_attribute_should_generate_with_required_keyword.verified.txt deleted file mode 100644 index d5828be5a8..0000000000 --- a/src/NSwag.CodeGeneration.CSharp.Tests/Snapshots/UseRequiredKeywordNewtonsoftJsonSchemaGeneratorTests.When_setting_is_disabled_properties_with_required_keyword_and_attribute_should_generate_with_required_keyword.verified.txt +++ /dev/null @@ -1,310 +0,0 @@ - - -namespace MyNamespace -{ - using System = global::System; - - public partial class TestClient - { - #pragma warning disable 8618 - private string _baseUrl; - #pragma warning restore 8618 - - private System.Net.Http.HttpClient _httpClient; - private static System.Lazy _settings = new System.Lazy(CreateSerializerSettings, true); - private Newtonsoft.Json.JsonSerializerSettings _instanceSettings; - - #pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. - public TestClient(string baseUrl, System.Net.Http.HttpClient httpClient) - #pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. - { - BaseUrl = baseUrl; - _httpClient = httpClient; - Initialize(); - } - - private static Newtonsoft.Json.JsonSerializerSettings CreateSerializerSettings() - { - var settings = new Newtonsoft.Json.JsonSerializerSettings(); - UpdateJsonSerializerSettings(settings); - return settings; - } - - public string BaseUrl - { - get { return _baseUrl; } - set - { - _baseUrl = value; - if (!string.IsNullOrEmpty(_baseUrl) && !_baseUrl.EndsWith("/")) - _baseUrl += '/'; - } - } - - protected Newtonsoft.Json.JsonSerializerSettings JsonSerializerSettings { get { return _instanceSettings ?? _settings.Value; } } - - static partial void UpdateJsonSerializerSettings(Newtonsoft.Json.JsonSerializerSettings settings); - - partial void Initialize(); - - partial void PrepareRequest(System.Net.Http.HttpClient client, System.Net.Http.HttpRequestMessage request, string url); - partial void PrepareRequest(System.Net.Http.HttpClient client, System.Net.Http.HttpRequestMessage request, System.Text.StringBuilder urlBuilder); - partial void ProcessResponse(System.Net.Http.HttpClient client, System.Net.Http.HttpResponseMessage response); - - public virtual System.Threading.Tasks.Task TestWithInputAsync(DTOWithRequiredFieldsOfNewtonsoftJsonSchemaGeneratorSettings input) - { - return TestWithInputAsync(input, System.Threading.CancellationToken.None); - } - - public virtual async System.Threading.Tasks.Task TestWithInputAsync(DTOWithRequiredFieldsOfNewtonsoftJsonSchemaGeneratorSettings input, System.Threading.CancellationToken cancellationToken) - { - var client_ = _httpClient; - var disposeClient_ = false; - try - { - using (var request_ = new System.Net.Http.HttpRequestMessage()) - { - var json_ = Newtonsoft.Json.JsonConvert.SerializeObject(input, JsonSerializerSettings); - var content_ = new System.Net.Http.StringContent(json_); - content_.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse("application/json"); - request_.Content = content_; - request_.Method = new System.Net.Http.HttpMethod("POST"); - - var urlBuilder_ = new System.Text.StringBuilder(); - if (!string.IsNullOrEmpty(_baseUrl)) urlBuilder_.Append(_baseUrl); - // Operation Path: "TestWithInput" - urlBuilder_.Append("TestWithInput"); - - PrepareRequest(client_, request_, urlBuilder_); - - var url_ = urlBuilder_.ToString(); - request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); - - PrepareRequest(client_, request_, url_); - - var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); - var disposeResponse_ = true; - try - { - var headers_ = new System.Collections.Generic.Dictionary>(); - foreach (var item_ in response_.Headers) - headers_[item_.Key] = item_.Value; - if (response_.Content != null && response_.Content.Headers != null) - { - foreach (var item_ in response_.Content.Headers) - headers_[item_.Key] = item_.Value; - } - - ProcessResponse(client_, response_); - - var status_ = (int)response_.StatusCode; - if (status_ == 204) - { - return; - } - else - { - var responseData_ = response_.Content == null ? null : await ReadAsStringAsync(response_.Content, cancellationToken).ConfigureAwait(false); - throw new ApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null); - } - } - finally - { - if (disposeResponse_) - response_.Dispose(); - } - } - } - finally - { - if (disposeClient_) - client_.Dispose(); - } - } - - protected struct ObjectResponseResult - { - public ObjectResponseResult(T responseObject, string responseText) - { - this.Object = responseObject; - this.Text = responseText; - } - - public T Object { get; } - - public string Text { get; } - } - - [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] - private static System.Threading.Tasks.Task ReadAsStringAsync(System.Net.Http.HttpContent content, System.Threading.CancellationToken cancellationToken) - { - #if NET5_0_OR_GREATER - return content.ReadAsStringAsync(cancellationToken); - #else - return content.ReadAsStringAsync(); - #endif - } - - [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] - private static System.Threading.Tasks.Task ReadAsStreamAsync(System.Net.Http.HttpContent content, System.Threading.CancellationToken cancellationToken) - { - #if NET5_0_OR_GREATER - return content.ReadAsStreamAsync(cancellationToken); - #else - return content.ReadAsStreamAsync(); - #endif - } - - public bool ReadResponseAsString { get; set; } - - protected virtual async System.Threading.Tasks.Task> ReadObjectResponseAsync(System.Net.Http.HttpResponseMessage response, System.Collections.Generic.IReadOnlyDictionary> headers, System.Threading.CancellationToken cancellationToken) - { - if (response == null || response.Content == null) - { - return new ObjectResponseResult(default(T), string.Empty); - } - - if (ReadResponseAsString) - { - var responseText = await ReadAsStringAsync(response.Content, cancellationToken).ConfigureAwait(false); - try - { - var typedBody = Newtonsoft.Json.JsonConvert.DeserializeObject(responseText, JsonSerializerSettings); - return new ObjectResponseResult(typedBody, responseText); - } - catch (Newtonsoft.Json.JsonException exception) - { - var message = "Could not deserialize the response body string as " + typeof(T).FullName + "."; - throw new ApiException(message, (int)response.StatusCode, responseText, headers, exception); - } - } - else - { - try - { - using (var responseStream = await ReadAsStreamAsync(response.Content, cancellationToken).ConfigureAwait(false)) - using (var streamReader = new System.IO.StreamReader(responseStream)) - using (var jsonTextReader = new Newtonsoft.Json.JsonTextReader(streamReader)) - { - var serializer = Newtonsoft.Json.JsonSerializer.Create(JsonSerializerSettings); - var typedBody = serializer.Deserialize(jsonTextReader); - return new ObjectResponseResult(typedBody, string.Empty); - } - } - catch (Newtonsoft.Json.JsonException exception) - { - var message = "Could not deserialize the response body stream as " + typeof(T).FullName + "."; - throw new ApiException(message, (int)response.StatusCode, string.Empty, headers, exception); - } - } - } - - private string ConvertToString(object value, System.Globalization.CultureInfo cultureInfo) - { - if (value == null) - { - return ""; - } - - if (value is System.Enum) - { - var name = System.Enum.GetName(value.GetType(), value); - if (name != null) - { - var field_ = System.Reflection.IntrospectionExtensions.GetTypeInfo(value.GetType()).GetDeclaredField(name); - if (field_ != null) - { - var attribute = System.Reflection.CustomAttributeExtensions.GetCustomAttribute(field_, typeof(System.Runtime.Serialization.EnumMemberAttribute)) - as System.Runtime.Serialization.EnumMemberAttribute; - if (attribute != null) - { - return attribute.Value != null ? attribute.Value : name; - } - } - - var converted = System.Convert.ToString(System.Convert.ChangeType(value, System.Enum.GetUnderlyingType(value.GetType()), cultureInfo)); - return converted == null ? string.Empty : converted; - } - } - else if (value is bool) - { - return System.Convert.ToString((bool)value, cultureInfo).ToLowerInvariant(); - } - else if (value is byte[]) - { - return System.Convert.ToBase64String((byte[]) value); - } - else if (value is string[]) - { - return string.Join(",", (string[])value); - } - else if (value.GetType().IsArray) - { - var valueArray = (System.Array)value; - var valueTextArray = new string[valueArray.Length]; - for (var i = 0; i < valueArray.Length; i++) - { - valueTextArray[i] = ConvertToString(valueArray.GetValue(i), cultureInfo); - } - return string.Join(",", valueTextArray); - } - - var result = System.Convert.ToString(value, cultureInfo); - return result == null ? "" : result; - } - } - - public partial class DTOWithRequiredFieldsOfNewtonsoftJsonSchemaGeneratorSettings - { - - [Newtonsoft.Json.JsonProperty("RequiredByAttribute", Required = Newtonsoft.Json.Required.Always)] - public int RequiredByAttribute { get; set; } - - [Newtonsoft.Json.JsonProperty("RequiredByC11Keyword", Required = Newtonsoft.Json.Required.AllowNull)] - public string RequiredByC11Keyword { get; set; } - - [Newtonsoft.Json.JsonProperty("RequiredByAttributeAndC11Keyword", Required = Newtonsoft.Json.Required.Always)] - [System.ComponentModel.DataAnnotations.Required] - public string RequiredByAttributeAndC11Keyword { get; set; } - - [Newtonsoft.Json.JsonProperty("NotRequired", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] - public string NotRequired { get; set; } - - } - - - - public partial class ApiException : System.Exception - { - public int StatusCode { get; private set; } - - public string Response { get; private set; } - - public System.Collections.Generic.IReadOnlyDictionary> Headers { get; private set; } - - public ApiException(string message, int statusCode, string response, System.Collections.Generic.IReadOnlyDictionary> headers, System.Exception innerException) - : base(message + "\n\nStatus: " + statusCode + "\nResponse: \n" + ((response == null) ? "(null)" : response.Substring(0, response.Length >= 512 ? 512 : response.Length)), innerException) - { - StatusCode = statusCode; - Response = response; - Headers = headers; - } - - public override string ToString() - { - return string.Format("HTTP Response: \n\n{0}\n\n{1}", Response, base.ToString()); - } - } - - public partial class ApiException : ApiException - { - public TResult Result { get; private set; } - - public ApiException(string message, int statusCode, string response, System.Collections.Generic.IReadOnlyDictionary> headers, TResult result, System.Exception innerException) - : base(message, statusCode, response, headers, innerException) - { - Result = result; - } - } - -} diff --git a/src/NSwag.CodeGeneration.CSharp.Tests/Snapshots/UseRequiredKeywordNewtonsoftJsonSchemaGeneratorTests.When_setting_is_disabled_properties_with_required_keyword_should_generate_with_required_keyword.verified.txt b/src/NSwag.CodeGeneration.CSharp.Tests/Snapshots/UseRequiredKeywordNewtonsoftJsonSchemaGeneratorTests.When_setting_is_disabled_properties_with_required_keyword_should_generate_with_required_keyword.verified.txt deleted file mode 100644 index d5828be5a8..0000000000 --- a/src/NSwag.CodeGeneration.CSharp.Tests/Snapshots/UseRequiredKeywordNewtonsoftJsonSchemaGeneratorTests.When_setting_is_disabled_properties_with_required_keyword_should_generate_with_required_keyword.verified.txt +++ /dev/null @@ -1,310 +0,0 @@ - - -namespace MyNamespace -{ - using System = global::System; - - public partial class TestClient - { - #pragma warning disable 8618 - private string _baseUrl; - #pragma warning restore 8618 - - private System.Net.Http.HttpClient _httpClient; - private static System.Lazy _settings = new System.Lazy(CreateSerializerSettings, true); - private Newtonsoft.Json.JsonSerializerSettings _instanceSettings; - - #pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. - public TestClient(string baseUrl, System.Net.Http.HttpClient httpClient) - #pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. - { - BaseUrl = baseUrl; - _httpClient = httpClient; - Initialize(); - } - - private static Newtonsoft.Json.JsonSerializerSettings CreateSerializerSettings() - { - var settings = new Newtonsoft.Json.JsonSerializerSettings(); - UpdateJsonSerializerSettings(settings); - return settings; - } - - public string BaseUrl - { - get { return _baseUrl; } - set - { - _baseUrl = value; - if (!string.IsNullOrEmpty(_baseUrl) && !_baseUrl.EndsWith("/")) - _baseUrl += '/'; - } - } - - protected Newtonsoft.Json.JsonSerializerSettings JsonSerializerSettings { get { return _instanceSettings ?? _settings.Value; } } - - static partial void UpdateJsonSerializerSettings(Newtonsoft.Json.JsonSerializerSettings settings); - - partial void Initialize(); - - partial void PrepareRequest(System.Net.Http.HttpClient client, System.Net.Http.HttpRequestMessage request, string url); - partial void PrepareRequest(System.Net.Http.HttpClient client, System.Net.Http.HttpRequestMessage request, System.Text.StringBuilder urlBuilder); - partial void ProcessResponse(System.Net.Http.HttpClient client, System.Net.Http.HttpResponseMessage response); - - public virtual System.Threading.Tasks.Task TestWithInputAsync(DTOWithRequiredFieldsOfNewtonsoftJsonSchemaGeneratorSettings input) - { - return TestWithInputAsync(input, System.Threading.CancellationToken.None); - } - - public virtual async System.Threading.Tasks.Task TestWithInputAsync(DTOWithRequiredFieldsOfNewtonsoftJsonSchemaGeneratorSettings input, System.Threading.CancellationToken cancellationToken) - { - var client_ = _httpClient; - var disposeClient_ = false; - try - { - using (var request_ = new System.Net.Http.HttpRequestMessage()) - { - var json_ = Newtonsoft.Json.JsonConvert.SerializeObject(input, JsonSerializerSettings); - var content_ = new System.Net.Http.StringContent(json_); - content_.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse("application/json"); - request_.Content = content_; - request_.Method = new System.Net.Http.HttpMethod("POST"); - - var urlBuilder_ = new System.Text.StringBuilder(); - if (!string.IsNullOrEmpty(_baseUrl)) urlBuilder_.Append(_baseUrl); - // Operation Path: "TestWithInput" - urlBuilder_.Append("TestWithInput"); - - PrepareRequest(client_, request_, urlBuilder_); - - var url_ = urlBuilder_.ToString(); - request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); - - PrepareRequest(client_, request_, url_); - - var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); - var disposeResponse_ = true; - try - { - var headers_ = new System.Collections.Generic.Dictionary>(); - foreach (var item_ in response_.Headers) - headers_[item_.Key] = item_.Value; - if (response_.Content != null && response_.Content.Headers != null) - { - foreach (var item_ in response_.Content.Headers) - headers_[item_.Key] = item_.Value; - } - - ProcessResponse(client_, response_); - - var status_ = (int)response_.StatusCode; - if (status_ == 204) - { - return; - } - else - { - var responseData_ = response_.Content == null ? null : await ReadAsStringAsync(response_.Content, cancellationToken).ConfigureAwait(false); - throw new ApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null); - } - } - finally - { - if (disposeResponse_) - response_.Dispose(); - } - } - } - finally - { - if (disposeClient_) - client_.Dispose(); - } - } - - protected struct ObjectResponseResult - { - public ObjectResponseResult(T responseObject, string responseText) - { - this.Object = responseObject; - this.Text = responseText; - } - - public T Object { get; } - - public string Text { get; } - } - - [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] - private static System.Threading.Tasks.Task ReadAsStringAsync(System.Net.Http.HttpContent content, System.Threading.CancellationToken cancellationToken) - { - #if NET5_0_OR_GREATER - return content.ReadAsStringAsync(cancellationToken); - #else - return content.ReadAsStringAsync(); - #endif - } - - [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] - private static System.Threading.Tasks.Task ReadAsStreamAsync(System.Net.Http.HttpContent content, System.Threading.CancellationToken cancellationToken) - { - #if NET5_0_OR_GREATER - return content.ReadAsStreamAsync(cancellationToken); - #else - return content.ReadAsStreamAsync(); - #endif - } - - public bool ReadResponseAsString { get; set; } - - protected virtual async System.Threading.Tasks.Task> ReadObjectResponseAsync(System.Net.Http.HttpResponseMessage response, System.Collections.Generic.IReadOnlyDictionary> headers, System.Threading.CancellationToken cancellationToken) - { - if (response == null || response.Content == null) - { - return new ObjectResponseResult(default(T), string.Empty); - } - - if (ReadResponseAsString) - { - var responseText = await ReadAsStringAsync(response.Content, cancellationToken).ConfigureAwait(false); - try - { - var typedBody = Newtonsoft.Json.JsonConvert.DeserializeObject(responseText, JsonSerializerSettings); - return new ObjectResponseResult(typedBody, responseText); - } - catch (Newtonsoft.Json.JsonException exception) - { - var message = "Could not deserialize the response body string as " + typeof(T).FullName + "."; - throw new ApiException(message, (int)response.StatusCode, responseText, headers, exception); - } - } - else - { - try - { - using (var responseStream = await ReadAsStreamAsync(response.Content, cancellationToken).ConfigureAwait(false)) - using (var streamReader = new System.IO.StreamReader(responseStream)) - using (var jsonTextReader = new Newtonsoft.Json.JsonTextReader(streamReader)) - { - var serializer = Newtonsoft.Json.JsonSerializer.Create(JsonSerializerSettings); - var typedBody = serializer.Deserialize(jsonTextReader); - return new ObjectResponseResult(typedBody, string.Empty); - } - } - catch (Newtonsoft.Json.JsonException exception) - { - var message = "Could not deserialize the response body stream as " + typeof(T).FullName + "."; - throw new ApiException(message, (int)response.StatusCode, string.Empty, headers, exception); - } - } - } - - private string ConvertToString(object value, System.Globalization.CultureInfo cultureInfo) - { - if (value == null) - { - return ""; - } - - if (value is System.Enum) - { - var name = System.Enum.GetName(value.GetType(), value); - if (name != null) - { - var field_ = System.Reflection.IntrospectionExtensions.GetTypeInfo(value.GetType()).GetDeclaredField(name); - if (field_ != null) - { - var attribute = System.Reflection.CustomAttributeExtensions.GetCustomAttribute(field_, typeof(System.Runtime.Serialization.EnumMemberAttribute)) - as System.Runtime.Serialization.EnumMemberAttribute; - if (attribute != null) - { - return attribute.Value != null ? attribute.Value : name; - } - } - - var converted = System.Convert.ToString(System.Convert.ChangeType(value, System.Enum.GetUnderlyingType(value.GetType()), cultureInfo)); - return converted == null ? string.Empty : converted; - } - } - else if (value is bool) - { - return System.Convert.ToString((bool)value, cultureInfo).ToLowerInvariant(); - } - else if (value is byte[]) - { - return System.Convert.ToBase64String((byte[]) value); - } - else if (value is string[]) - { - return string.Join(",", (string[])value); - } - else if (value.GetType().IsArray) - { - var valueArray = (System.Array)value; - var valueTextArray = new string[valueArray.Length]; - for (var i = 0; i < valueArray.Length; i++) - { - valueTextArray[i] = ConvertToString(valueArray.GetValue(i), cultureInfo); - } - return string.Join(",", valueTextArray); - } - - var result = System.Convert.ToString(value, cultureInfo); - return result == null ? "" : result; - } - } - - public partial class DTOWithRequiredFieldsOfNewtonsoftJsonSchemaGeneratorSettings - { - - [Newtonsoft.Json.JsonProperty("RequiredByAttribute", Required = Newtonsoft.Json.Required.Always)] - public int RequiredByAttribute { get; set; } - - [Newtonsoft.Json.JsonProperty("RequiredByC11Keyword", Required = Newtonsoft.Json.Required.AllowNull)] - public string RequiredByC11Keyword { get; set; } - - [Newtonsoft.Json.JsonProperty("RequiredByAttributeAndC11Keyword", Required = Newtonsoft.Json.Required.Always)] - [System.ComponentModel.DataAnnotations.Required] - public string RequiredByAttributeAndC11Keyword { get; set; } - - [Newtonsoft.Json.JsonProperty("NotRequired", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] - public string NotRequired { get; set; } - - } - - - - public partial class ApiException : System.Exception - { - public int StatusCode { get; private set; } - - public string Response { get; private set; } - - public System.Collections.Generic.IReadOnlyDictionary> Headers { get; private set; } - - public ApiException(string message, int statusCode, string response, System.Collections.Generic.IReadOnlyDictionary> headers, System.Exception innerException) - : base(message + "\n\nStatus: " + statusCode + "\nResponse: \n" + ((response == null) ? "(null)" : response.Substring(0, response.Length >= 512 ? 512 : response.Length)), innerException) - { - StatusCode = statusCode; - Response = response; - Headers = headers; - } - - public override string ToString() - { - return string.Format("HTTP Response: \n\n{0}\n\n{1}", Response, base.ToString()); - } - } - - public partial class ApiException : ApiException - { - public TResult Result { get; private set; } - - public ApiException(string message, int statusCode, string response, System.Collections.Generic.IReadOnlyDictionary> headers, TResult result, System.Exception innerException) - : base(message, statusCode, response, headers, innerException) - { - Result = result; - } - } - -} diff --git a/src/NSwag.CodeGeneration.CSharp.Tests/Snapshots/UseRequiredKeywordNewtonsoftJsonSchemaGeneratorTests.When_setting_is_enabled_properties_with_required_attribute_should_generate_with_required_keyword.verified.txt b/src/NSwag.CodeGeneration.CSharp.Tests/Snapshots/UseRequiredKeywordNewtonsoftJsonSchemaGeneratorTests.When_setting_is_enabled_properties_with_required_attribute_should_generate_with_required_keyword.verified.txt deleted file mode 100644 index 4f9ba32981..0000000000 --- a/src/NSwag.CodeGeneration.CSharp.Tests/Snapshots/UseRequiredKeywordNewtonsoftJsonSchemaGeneratorTests.When_setting_is_enabled_properties_with_required_attribute_should_generate_with_required_keyword.verified.txt +++ /dev/null @@ -1,310 +0,0 @@ - - -namespace MyNamespace -{ - using System = global::System; - - public partial class TestClient - { - #pragma warning disable 8618 - private string _baseUrl; - #pragma warning restore 8618 - - private System.Net.Http.HttpClient _httpClient; - private static System.Lazy _settings = new System.Lazy(CreateSerializerSettings, true); - private Newtonsoft.Json.JsonSerializerSettings _instanceSettings; - - #pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. - public TestClient(string baseUrl, System.Net.Http.HttpClient httpClient) - #pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. - { - BaseUrl = baseUrl; - _httpClient = httpClient; - Initialize(); - } - - private static Newtonsoft.Json.JsonSerializerSettings CreateSerializerSettings() - { - var settings = new Newtonsoft.Json.JsonSerializerSettings(); - UpdateJsonSerializerSettings(settings); - return settings; - } - - public string BaseUrl - { - get { return _baseUrl; } - set - { - _baseUrl = value; - if (!string.IsNullOrEmpty(_baseUrl) && !_baseUrl.EndsWith("/")) - _baseUrl += '/'; - } - } - - protected Newtonsoft.Json.JsonSerializerSettings JsonSerializerSettings { get { return _instanceSettings ?? _settings.Value; } } - - static partial void UpdateJsonSerializerSettings(Newtonsoft.Json.JsonSerializerSettings settings); - - partial void Initialize(); - - partial void PrepareRequest(System.Net.Http.HttpClient client, System.Net.Http.HttpRequestMessage request, string url); - partial void PrepareRequest(System.Net.Http.HttpClient client, System.Net.Http.HttpRequestMessage request, System.Text.StringBuilder urlBuilder); - partial void ProcessResponse(System.Net.Http.HttpClient client, System.Net.Http.HttpResponseMessage response); - - public virtual System.Threading.Tasks.Task TestWithInputAsync(DTOWithRequiredFieldsOfNewtonsoftJsonSchemaGeneratorSettings input) - { - return TestWithInputAsync(input, System.Threading.CancellationToken.None); - } - - public virtual async System.Threading.Tasks.Task TestWithInputAsync(DTOWithRequiredFieldsOfNewtonsoftJsonSchemaGeneratorSettings input, System.Threading.CancellationToken cancellationToken) - { - var client_ = _httpClient; - var disposeClient_ = false; - try - { - using (var request_ = new System.Net.Http.HttpRequestMessage()) - { - var json_ = Newtonsoft.Json.JsonConvert.SerializeObject(input, JsonSerializerSettings); - var content_ = new System.Net.Http.StringContent(json_); - content_.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse("application/json"); - request_.Content = content_; - request_.Method = new System.Net.Http.HttpMethod("POST"); - - var urlBuilder_ = new System.Text.StringBuilder(); - if (!string.IsNullOrEmpty(_baseUrl)) urlBuilder_.Append(_baseUrl); - // Operation Path: "TestWithInput" - urlBuilder_.Append("TestWithInput"); - - PrepareRequest(client_, request_, urlBuilder_); - - var url_ = urlBuilder_.ToString(); - request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); - - PrepareRequest(client_, request_, url_); - - var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); - var disposeResponse_ = true; - try - { - var headers_ = new System.Collections.Generic.Dictionary>(); - foreach (var item_ in response_.Headers) - headers_[item_.Key] = item_.Value; - if (response_.Content != null && response_.Content.Headers != null) - { - foreach (var item_ in response_.Content.Headers) - headers_[item_.Key] = item_.Value; - } - - ProcessResponse(client_, response_); - - var status_ = (int)response_.StatusCode; - if (status_ == 204) - { - return; - } - else - { - var responseData_ = response_.Content == null ? null : await ReadAsStringAsync(response_.Content, cancellationToken).ConfigureAwait(false); - throw new ApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null); - } - } - finally - { - if (disposeResponse_) - response_.Dispose(); - } - } - } - finally - { - if (disposeClient_) - client_.Dispose(); - } - } - - protected struct ObjectResponseResult - { - public ObjectResponseResult(T responseObject, string responseText) - { - this.Object = responseObject; - this.Text = responseText; - } - - public T Object { get; } - - public string Text { get; } - } - - [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] - private static System.Threading.Tasks.Task ReadAsStringAsync(System.Net.Http.HttpContent content, System.Threading.CancellationToken cancellationToken) - { - #if NET5_0_OR_GREATER - return content.ReadAsStringAsync(cancellationToken); - #else - return content.ReadAsStringAsync(); - #endif - } - - [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] - private static System.Threading.Tasks.Task ReadAsStreamAsync(System.Net.Http.HttpContent content, System.Threading.CancellationToken cancellationToken) - { - #if NET5_0_OR_GREATER - return content.ReadAsStreamAsync(cancellationToken); - #else - return content.ReadAsStreamAsync(); - #endif - } - - public bool ReadResponseAsString { get; set; } - - protected virtual async System.Threading.Tasks.Task> ReadObjectResponseAsync(System.Net.Http.HttpResponseMessage response, System.Collections.Generic.IReadOnlyDictionary> headers, System.Threading.CancellationToken cancellationToken) - { - if (response == null || response.Content == null) - { - return new ObjectResponseResult(default(T), string.Empty); - } - - if (ReadResponseAsString) - { - var responseText = await ReadAsStringAsync(response.Content, cancellationToken).ConfigureAwait(false); - try - { - var typedBody = Newtonsoft.Json.JsonConvert.DeserializeObject(responseText, JsonSerializerSettings); - return new ObjectResponseResult(typedBody, responseText); - } - catch (Newtonsoft.Json.JsonException exception) - { - var message = "Could not deserialize the response body string as " + typeof(T).FullName + "."; - throw new ApiException(message, (int)response.StatusCode, responseText, headers, exception); - } - } - else - { - try - { - using (var responseStream = await ReadAsStreamAsync(response.Content, cancellationToken).ConfigureAwait(false)) - using (var streamReader = new System.IO.StreamReader(responseStream)) - using (var jsonTextReader = new Newtonsoft.Json.JsonTextReader(streamReader)) - { - var serializer = Newtonsoft.Json.JsonSerializer.Create(JsonSerializerSettings); - var typedBody = serializer.Deserialize(jsonTextReader); - return new ObjectResponseResult(typedBody, string.Empty); - } - } - catch (Newtonsoft.Json.JsonException exception) - { - var message = "Could not deserialize the response body stream as " + typeof(T).FullName + "."; - throw new ApiException(message, (int)response.StatusCode, string.Empty, headers, exception); - } - } - } - - private string ConvertToString(object value, System.Globalization.CultureInfo cultureInfo) - { - if (value == null) - { - return ""; - } - - if (value is System.Enum) - { - var name = System.Enum.GetName(value.GetType(), value); - if (name != null) - { - var field_ = System.Reflection.IntrospectionExtensions.GetTypeInfo(value.GetType()).GetDeclaredField(name); - if (field_ != null) - { - var attribute = System.Reflection.CustomAttributeExtensions.GetCustomAttribute(field_, typeof(System.Runtime.Serialization.EnumMemberAttribute)) - as System.Runtime.Serialization.EnumMemberAttribute; - if (attribute != null) - { - return attribute.Value != null ? attribute.Value : name; - } - } - - var converted = System.Convert.ToString(System.Convert.ChangeType(value, System.Enum.GetUnderlyingType(value.GetType()), cultureInfo)); - return converted == null ? string.Empty : converted; - } - } - else if (value is bool) - { - return System.Convert.ToString((bool)value, cultureInfo).ToLowerInvariant(); - } - else if (value is byte[]) - { - return System.Convert.ToBase64String((byte[]) value); - } - else if (value is string[]) - { - return string.Join(",", (string[])value); - } - else if (value.GetType().IsArray) - { - var valueArray = (System.Array)value; - var valueTextArray = new string[valueArray.Length]; - for (var i = 0; i < valueArray.Length; i++) - { - valueTextArray[i] = ConvertToString(valueArray.GetValue(i), cultureInfo); - } - return string.Join(",", valueTextArray); - } - - var result = System.Convert.ToString(value, cultureInfo); - return result == null ? "" : result; - } - } - - public partial class DTOWithRequiredFieldsOfNewtonsoftJsonSchemaGeneratorSettings - { - - [Newtonsoft.Json.JsonProperty("RequiredByAttribute", Required = Newtonsoft.Json.Required.Always)] - public required int RequiredByAttribute { get; set; } - - [Newtonsoft.Json.JsonProperty("RequiredByC11Keyword", Required = Newtonsoft.Json.Required.AllowNull)] - public required string RequiredByC11Keyword { get; set; } - - [Newtonsoft.Json.JsonProperty("RequiredByAttributeAndC11Keyword", Required = Newtonsoft.Json.Required.Always)] - [System.ComponentModel.DataAnnotations.Required] - public required string RequiredByAttributeAndC11Keyword { get; set; } - - [Newtonsoft.Json.JsonProperty("NotRequired", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] - public string NotRequired { get; set; } - - } - - - - public partial class ApiException : System.Exception - { - public int StatusCode { get; private set; } - - public string Response { get; private set; } - - public System.Collections.Generic.IReadOnlyDictionary> Headers { get; private set; } - - public ApiException(string message, int statusCode, string response, System.Collections.Generic.IReadOnlyDictionary> headers, System.Exception innerException) - : base(message + "\n\nStatus: " + statusCode + "\nResponse: \n" + ((response == null) ? "(null)" : response.Substring(0, response.Length >= 512 ? 512 : response.Length)), innerException) - { - StatusCode = statusCode; - Response = response; - Headers = headers; - } - - public override string ToString() - { - return string.Format("HTTP Response: \n\n{0}\n\n{1}", Response, base.ToString()); - } - } - - public partial class ApiException : ApiException - { - public TResult Result { get; private set; } - - public ApiException(string message, int statusCode, string response, System.Collections.Generic.IReadOnlyDictionary> headers, TResult result, System.Exception innerException) - : base(message, statusCode, response, headers, innerException) - { - Result = result; - } - } - -} diff --git a/src/NSwag.CodeGeneration.CSharp.Tests/Snapshots/UseRequiredKeywordNewtonsoftJsonSchemaGeneratorTests.When_setting_is_enabled_properties_with_required_keyword_and_attribute_should_generate_with_required_keyword.verified.txt b/src/NSwag.CodeGeneration.CSharp.Tests/Snapshots/UseRequiredKeywordNewtonsoftJsonSchemaGeneratorTests.When_setting_is_enabled_properties_with_required_keyword_and_attribute_should_generate_with_required_keyword.verified.txt deleted file mode 100644 index 4f9ba32981..0000000000 --- a/src/NSwag.CodeGeneration.CSharp.Tests/Snapshots/UseRequiredKeywordNewtonsoftJsonSchemaGeneratorTests.When_setting_is_enabled_properties_with_required_keyword_and_attribute_should_generate_with_required_keyword.verified.txt +++ /dev/null @@ -1,310 +0,0 @@ - - -namespace MyNamespace -{ - using System = global::System; - - public partial class TestClient - { - #pragma warning disable 8618 - private string _baseUrl; - #pragma warning restore 8618 - - private System.Net.Http.HttpClient _httpClient; - private static System.Lazy _settings = new System.Lazy(CreateSerializerSettings, true); - private Newtonsoft.Json.JsonSerializerSettings _instanceSettings; - - #pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. - public TestClient(string baseUrl, System.Net.Http.HttpClient httpClient) - #pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. - { - BaseUrl = baseUrl; - _httpClient = httpClient; - Initialize(); - } - - private static Newtonsoft.Json.JsonSerializerSettings CreateSerializerSettings() - { - var settings = new Newtonsoft.Json.JsonSerializerSettings(); - UpdateJsonSerializerSettings(settings); - return settings; - } - - public string BaseUrl - { - get { return _baseUrl; } - set - { - _baseUrl = value; - if (!string.IsNullOrEmpty(_baseUrl) && !_baseUrl.EndsWith("/")) - _baseUrl += '/'; - } - } - - protected Newtonsoft.Json.JsonSerializerSettings JsonSerializerSettings { get { return _instanceSettings ?? _settings.Value; } } - - static partial void UpdateJsonSerializerSettings(Newtonsoft.Json.JsonSerializerSettings settings); - - partial void Initialize(); - - partial void PrepareRequest(System.Net.Http.HttpClient client, System.Net.Http.HttpRequestMessage request, string url); - partial void PrepareRequest(System.Net.Http.HttpClient client, System.Net.Http.HttpRequestMessage request, System.Text.StringBuilder urlBuilder); - partial void ProcessResponse(System.Net.Http.HttpClient client, System.Net.Http.HttpResponseMessage response); - - public virtual System.Threading.Tasks.Task TestWithInputAsync(DTOWithRequiredFieldsOfNewtonsoftJsonSchemaGeneratorSettings input) - { - return TestWithInputAsync(input, System.Threading.CancellationToken.None); - } - - public virtual async System.Threading.Tasks.Task TestWithInputAsync(DTOWithRequiredFieldsOfNewtonsoftJsonSchemaGeneratorSettings input, System.Threading.CancellationToken cancellationToken) - { - var client_ = _httpClient; - var disposeClient_ = false; - try - { - using (var request_ = new System.Net.Http.HttpRequestMessage()) - { - var json_ = Newtonsoft.Json.JsonConvert.SerializeObject(input, JsonSerializerSettings); - var content_ = new System.Net.Http.StringContent(json_); - content_.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse("application/json"); - request_.Content = content_; - request_.Method = new System.Net.Http.HttpMethod("POST"); - - var urlBuilder_ = new System.Text.StringBuilder(); - if (!string.IsNullOrEmpty(_baseUrl)) urlBuilder_.Append(_baseUrl); - // Operation Path: "TestWithInput" - urlBuilder_.Append("TestWithInput"); - - PrepareRequest(client_, request_, urlBuilder_); - - var url_ = urlBuilder_.ToString(); - request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); - - PrepareRequest(client_, request_, url_); - - var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); - var disposeResponse_ = true; - try - { - var headers_ = new System.Collections.Generic.Dictionary>(); - foreach (var item_ in response_.Headers) - headers_[item_.Key] = item_.Value; - if (response_.Content != null && response_.Content.Headers != null) - { - foreach (var item_ in response_.Content.Headers) - headers_[item_.Key] = item_.Value; - } - - ProcessResponse(client_, response_); - - var status_ = (int)response_.StatusCode; - if (status_ == 204) - { - return; - } - else - { - var responseData_ = response_.Content == null ? null : await ReadAsStringAsync(response_.Content, cancellationToken).ConfigureAwait(false); - throw new ApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null); - } - } - finally - { - if (disposeResponse_) - response_.Dispose(); - } - } - } - finally - { - if (disposeClient_) - client_.Dispose(); - } - } - - protected struct ObjectResponseResult - { - public ObjectResponseResult(T responseObject, string responseText) - { - this.Object = responseObject; - this.Text = responseText; - } - - public T Object { get; } - - public string Text { get; } - } - - [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] - private static System.Threading.Tasks.Task ReadAsStringAsync(System.Net.Http.HttpContent content, System.Threading.CancellationToken cancellationToken) - { - #if NET5_0_OR_GREATER - return content.ReadAsStringAsync(cancellationToken); - #else - return content.ReadAsStringAsync(); - #endif - } - - [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] - private static System.Threading.Tasks.Task ReadAsStreamAsync(System.Net.Http.HttpContent content, System.Threading.CancellationToken cancellationToken) - { - #if NET5_0_OR_GREATER - return content.ReadAsStreamAsync(cancellationToken); - #else - return content.ReadAsStreamAsync(); - #endif - } - - public bool ReadResponseAsString { get; set; } - - protected virtual async System.Threading.Tasks.Task> ReadObjectResponseAsync(System.Net.Http.HttpResponseMessage response, System.Collections.Generic.IReadOnlyDictionary> headers, System.Threading.CancellationToken cancellationToken) - { - if (response == null || response.Content == null) - { - return new ObjectResponseResult(default(T), string.Empty); - } - - if (ReadResponseAsString) - { - var responseText = await ReadAsStringAsync(response.Content, cancellationToken).ConfigureAwait(false); - try - { - var typedBody = Newtonsoft.Json.JsonConvert.DeserializeObject(responseText, JsonSerializerSettings); - return new ObjectResponseResult(typedBody, responseText); - } - catch (Newtonsoft.Json.JsonException exception) - { - var message = "Could not deserialize the response body string as " + typeof(T).FullName + "."; - throw new ApiException(message, (int)response.StatusCode, responseText, headers, exception); - } - } - else - { - try - { - using (var responseStream = await ReadAsStreamAsync(response.Content, cancellationToken).ConfigureAwait(false)) - using (var streamReader = new System.IO.StreamReader(responseStream)) - using (var jsonTextReader = new Newtonsoft.Json.JsonTextReader(streamReader)) - { - var serializer = Newtonsoft.Json.JsonSerializer.Create(JsonSerializerSettings); - var typedBody = serializer.Deserialize(jsonTextReader); - return new ObjectResponseResult(typedBody, string.Empty); - } - } - catch (Newtonsoft.Json.JsonException exception) - { - var message = "Could not deserialize the response body stream as " + typeof(T).FullName + "."; - throw new ApiException(message, (int)response.StatusCode, string.Empty, headers, exception); - } - } - } - - private string ConvertToString(object value, System.Globalization.CultureInfo cultureInfo) - { - if (value == null) - { - return ""; - } - - if (value is System.Enum) - { - var name = System.Enum.GetName(value.GetType(), value); - if (name != null) - { - var field_ = System.Reflection.IntrospectionExtensions.GetTypeInfo(value.GetType()).GetDeclaredField(name); - if (field_ != null) - { - var attribute = System.Reflection.CustomAttributeExtensions.GetCustomAttribute(field_, typeof(System.Runtime.Serialization.EnumMemberAttribute)) - as System.Runtime.Serialization.EnumMemberAttribute; - if (attribute != null) - { - return attribute.Value != null ? attribute.Value : name; - } - } - - var converted = System.Convert.ToString(System.Convert.ChangeType(value, System.Enum.GetUnderlyingType(value.GetType()), cultureInfo)); - return converted == null ? string.Empty : converted; - } - } - else if (value is bool) - { - return System.Convert.ToString((bool)value, cultureInfo).ToLowerInvariant(); - } - else if (value is byte[]) - { - return System.Convert.ToBase64String((byte[]) value); - } - else if (value is string[]) - { - return string.Join(",", (string[])value); - } - else if (value.GetType().IsArray) - { - var valueArray = (System.Array)value; - var valueTextArray = new string[valueArray.Length]; - for (var i = 0; i < valueArray.Length; i++) - { - valueTextArray[i] = ConvertToString(valueArray.GetValue(i), cultureInfo); - } - return string.Join(",", valueTextArray); - } - - var result = System.Convert.ToString(value, cultureInfo); - return result == null ? "" : result; - } - } - - public partial class DTOWithRequiredFieldsOfNewtonsoftJsonSchemaGeneratorSettings - { - - [Newtonsoft.Json.JsonProperty("RequiredByAttribute", Required = Newtonsoft.Json.Required.Always)] - public required int RequiredByAttribute { get; set; } - - [Newtonsoft.Json.JsonProperty("RequiredByC11Keyword", Required = Newtonsoft.Json.Required.AllowNull)] - public required string RequiredByC11Keyword { get; set; } - - [Newtonsoft.Json.JsonProperty("RequiredByAttributeAndC11Keyword", Required = Newtonsoft.Json.Required.Always)] - [System.ComponentModel.DataAnnotations.Required] - public required string RequiredByAttributeAndC11Keyword { get; set; } - - [Newtonsoft.Json.JsonProperty("NotRequired", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] - public string NotRequired { get; set; } - - } - - - - public partial class ApiException : System.Exception - { - public int StatusCode { get; private set; } - - public string Response { get; private set; } - - public System.Collections.Generic.IReadOnlyDictionary> Headers { get; private set; } - - public ApiException(string message, int statusCode, string response, System.Collections.Generic.IReadOnlyDictionary> headers, System.Exception innerException) - : base(message + "\n\nStatus: " + statusCode + "\nResponse: \n" + ((response == null) ? "(null)" : response.Substring(0, response.Length >= 512 ? 512 : response.Length)), innerException) - { - StatusCode = statusCode; - Response = response; - Headers = headers; - } - - public override string ToString() - { - return string.Format("HTTP Response: \n\n{0}\n\n{1}", Response, base.ToString()); - } - } - - public partial class ApiException : ApiException - { - public TResult Result { get; private set; } - - public ApiException(string message, int statusCode, string response, System.Collections.Generic.IReadOnlyDictionary> headers, TResult result, System.Exception innerException) - : base(message, statusCode, response, headers, innerException) - { - Result = result; - } - } - -} diff --git a/src/NSwag.CodeGeneration.CSharp.Tests/UseCancellationTokenTests.cs b/src/NSwag.CodeGeneration.CSharp.Tests/UseCancellationTokenTests.cs index faa6aaa79b..32ce8abbf0 100644 --- a/src/NSwag.CodeGeneration.CSharp.Tests/UseCancellationTokenTests.cs +++ b/src/NSwag.CodeGeneration.CSharp.Tests/UseCancellationTokenTests.cs @@ -1,5 +1,5 @@ using Microsoft.AspNetCore.Mvc; -using NJsonSchema.NewtonsoftJson.Generation; +using NJsonSchema.Generation; using NSwag.CodeGeneration.CSharp.Models; using NSwag.CodeGeneration.Tests; using NSwag.Generation.WebApi; @@ -29,7 +29,7 @@ public async Task When_controllerstyleispartial_and_usecancellationtokenistrue_a // Arrange var swaggerGen = new WebApiOpenApiDocumentGenerator(new WebApiOpenApiDocumentGeneratorSettings { - SchemaSettings = new NewtonsoftJsonSchemaGeneratorSettings() + SchemaSettings = new SystemTextJsonSchemaGeneratorSettings() }); var document = await swaggerGen.GenerateForControllerAsync(); @@ -51,7 +51,7 @@ public async Task When_controllerstyleispartial_and_usecancellationtokenistrue_a // Arrange var swaggerGen = new WebApiOpenApiDocumentGenerator(new WebApiOpenApiDocumentGeneratorSettings { - SchemaSettings = new NewtonsoftJsonSchemaGeneratorSettings() + SchemaSettings = new SystemTextJsonSchemaGeneratorSettings() }); var document = await swaggerGen.GenerateForControllerAsync(); @@ -73,7 +73,7 @@ public async Task When_controllerstyleisabstract_and_usecancellationtokenistrue_ // Arrange var swaggerGen = new WebApiOpenApiDocumentGenerator(new WebApiOpenApiDocumentGeneratorSettings { - SchemaSettings = new NewtonsoftJsonSchemaGeneratorSettings() + SchemaSettings = new SystemTextJsonSchemaGeneratorSettings() }); var document = await swaggerGen.GenerateForControllerAsync(); @@ -97,7 +97,7 @@ public async Task When_controllerstyleisabstract_and_usecancellationtokenistrue_ // Arrange var swaggerGen = new WebApiOpenApiDocumentGenerator(new WebApiOpenApiDocumentGeneratorSettings { - SchemaSettings = new NewtonsoftJsonSchemaGeneratorSettings() + SchemaSettings = new SystemTextJsonSchemaGeneratorSettings() }); var document = await swaggerGen.GenerateForControllerAsync(); @@ -121,7 +121,7 @@ public async Task When_usecancellationtokenparameter_notsetted_then_cancellation // Arrange var swaggerGen = new WebApiOpenApiDocumentGenerator(new WebApiOpenApiDocumentGeneratorSettings { - SchemaSettings = new NewtonsoftJsonSchemaGeneratorSettings() + SchemaSettings = new SystemTextJsonSchemaGeneratorSettings() }); var document = await swaggerGen.GenerateForControllerAsync(); diff --git a/src/NSwag.CodeGeneration.CSharp.Tests/WrapResponsesTests.cs b/src/NSwag.CodeGeneration.CSharp.Tests/WrapResponsesTests.cs index 1a608deefe..c4a9a8dc24 100644 --- a/src/NSwag.CodeGeneration.CSharp.Tests/WrapResponsesTests.cs +++ b/src/NSwag.CodeGeneration.CSharp.Tests/WrapResponsesTests.cs @@ -1,5 +1,5 @@ using Microsoft.AspNetCore.Mvc; -using NJsonSchema.NewtonsoftJson.Generation; +using NJsonSchema.Generation; using NSwag.CodeGeneration.Tests; using NSwag.Generation.WebApi; @@ -28,7 +28,7 @@ public async Task When_success_responses_are_wrapped_then_SwaggerResponse_is_ret // Arrange var swaggerGen = new WebApiOpenApiDocumentGenerator(new WebApiOpenApiDocumentGeneratorSettings { - SchemaSettings = new NewtonsoftJsonSchemaGeneratorSettings() + SchemaSettings = new SystemTextJsonSchemaGeneratorSettings() }); var document = await swaggerGen.GenerateForControllerAsync(); @@ -50,7 +50,7 @@ public async Task When_success_responses_are_wrapped_then_SwaggerResponse_is_ret // Arrange var swaggerGen = new WebApiOpenApiDocumentGenerator(new WebApiOpenApiDocumentGeneratorSettings { - SchemaSettings = new NewtonsoftJsonSchemaGeneratorSettings() + SchemaSettings = new SystemTextJsonSchemaGeneratorSettings() }); var document = await swaggerGen.GenerateForControllerAsync(); @@ -72,7 +72,7 @@ public async Task When_success_responses_are_wrapped_then_SwaggerResponse_is_ret var swaggerGen = new WebApiOpenApiDocumentGenerator(new WebApiOpenApiDocumentGeneratorSettings { IsAspNetCore = true, - SchemaSettings = new NewtonsoftJsonSchemaGeneratorSettings() + SchemaSettings = new SystemTextJsonSchemaGeneratorSettings() }); var document = await swaggerGen.GenerateForControllerAsync(); diff --git a/src/NSwag.CodeGeneration.CSharp/CSharpGeneratorBaseSettings.cs b/src/NSwag.CodeGeneration.CSharp/CSharpGeneratorBaseSettings.cs index aede0182a9..ecf9e97ab9 100644 --- a/src/NSwag.CodeGeneration.CSharp/CSharpGeneratorBaseSettings.cs +++ b/src/NSwag.CodeGeneration.CSharp/CSharpGeneratorBaseSettings.cs @@ -6,7 +6,7 @@ // Rico Suter, mail@rsuter.com //----------------------------------------------------------------------- -using Newtonsoft.Json; +using System.Text.Json.Serialization; using NJsonSchema; using NJsonSchema.CodeGeneration; using NJsonSchema.CodeGeneration.CSharp; diff --git a/src/NSwag.CodeGeneration.CSharp/NSwag.CodeGeneration.CSharp.csproj b/src/NSwag.CodeGeneration.CSharp/NSwag.CodeGeneration.CSharp.csproj index 48fc660c0f..8ccfd6bc08 100644 --- a/src/NSwag.CodeGeneration.CSharp/NSwag.CodeGeneration.CSharp.csproj +++ b/src/NSwag.CodeGeneration.CSharp/NSwag.CodeGeneration.CSharp.csproj @@ -12,9 +12,11 @@ + + diff --git a/src/NSwag.CodeGeneration.TypeScript.Tests/AngularJSTests.cs b/src/NSwag.CodeGeneration.TypeScript.Tests/AngularJSTests.cs index adf9d1663b..8e7aed5b6a 100644 --- a/src/NSwag.CodeGeneration.TypeScript.Tests/AngularJSTests.cs +++ b/src/NSwag.CodeGeneration.TypeScript.Tests/AngularJSTests.cs @@ -1,7 +1,7 @@ using NSwag.Generation.WebApi; using Microsoft.AspNetCore.Mvc; using NJsonSchema; -using NJsonSchema.NewtonsoftJson.Generation; +using NJsonSchema.Generation; using NSwag.CodeGeneration.Tests; namespace NSwag.CodeGeneration.TypeScript.Tests @@ -36,7 +36,7 @@ public async Task When_export_types_is_true_then_add_export_before_classes() // Arrange var generator = new WebApiOpenApiDocumentGenerator(new WebApiOpenApiDocumentGeneratorSettings { - SchemaSettings = new NewtonsoftJsonSchemaGeneratorSettings { SchemaType = SchemaType.Swagger2 } + SchemaSettings = new SystemTextJsonSchemaGeneratorSettings { SchemaType = SchemaType.Swagger2 } }); var document = await generator.GenerateForControllerAsync(); @@ -67,7 +67,7 @@ public async Task When_export_types_is_false_then_dont_add_export_before_classes // Arrange var generator = new WebApiOpenApiDocumentGenerator(new WebApiOpenApiDocumentGeneratorSettings { - SchemaSettings = new NewtonsoftJsonSchemaGeneratorSettings { SchemaType = SchemaType.Swagger2 } + SchemaSettings = new SystemTextJsonSchemaGeneratorSettings { SchemaType = SchemaType.Swagger2 } }); var document = await generator.GenerateForControllerAsync(); @@ -98,7 +98,7 @@ public async Task When_consumes_is_url_encoded_then_construct_url_encoded_reques // Arrange var generator = new WebApiOpenApiDocumentGenerator(new WebApiOpenApiDocumentGeneratorSettings { - SchemaSettings = new NewtonsoftJsonSchemaGeneratorSettings { SchemaType = SchemaType.Swagger2 } + SchemaSettings = new SystemTextJsonSchemaGeneratorSettings { SchemaType = SchemaType.Swagger2 } }); var document = await generator.GenerateForControllerAsync(); var json = document.ToJson(); diff --git a/src/NSwag.CodeGeneration.TypeScript.Tests/AngularTests.cs b/src/NSwag.CodeGeneration.TypeScript.Tests/AngularTests.cs index f9e17aa20b..e9db558e0b 100644 --- a/src/NSwag.CodeGeneration.TypeScript.Tests/AngularTests.cs +++ b/src/NSwag.CodeGeneration.TypeScript.Tests/AngularTests.cs @@ -2,7 +2,7 @@ using Microsoft.AspNetCore.Mvc; using System.ComponentModel.DataAnnotations; using NJsonSchema; -using NJsonSchema.NewtonsoftJson.Generation; +using NJsonSchema.Generation; using NSwag.CodeGeneration.Tests; namespace NSwag.CodeGeneration.TypeScript.Tests @@ -77,7 +77,7 @@ public async Task When_return_value_is_void_then_client_returns_observable_of_vo // Arrange var generator = new WebApiOpenApiDocumentGenerator(new WebApiOpenApiDocumentGeneratorSettings { - SchemaSettings = new NewtonsoftJsonSchemaGeneratorSettings { SchemaType = SchemaType.Swagger2 } + SchemaSettings = new SystemTextJsonSchemaGeneratorSettings { SchemaType = SchemaType.Swagger2 } }); var document = await generator.GenerateForControllerAsync(); var json = document.ToJson(); @@ -105,7 +105,7 @@ public async Task When_export_types_is_true_then_add_export_before_classes() // Arrange var generator = new WebApiOpenApiDocumentGenerator(new WebApiOpenApiDocumentGeneratorSettings { - SchemaSettings = new NewtonsoftJsonSchemaGeneratorSettings { SchemaType = SchemaType.Swagger2 } + SchemaSettings = new SystemTextJsonSchemaGeneratorSettings { SchemaType = SchemaType.Swagger2 } }); var document = await generator.GenerateForControllerAsync(); var json = document.ToJson(); @@ -134,7 +134,7 @@ public async Task When_export_types_is_false_then_dont_add_export_before_classes // Arrange var generator = new WebApiOpenApiDocumentGenerator(new WebApiOpenApiDocumentGeneratorSettings { - SchemaSettings = new NewtonsoftJsonSchemaGeneratorSettings { SchemaType = SchemaType.Swagger2 } + SchemaSettings = new SystemTextJsonSchemaGeneratorSettings { SchemaType = SchemaType.Swagger2 } }); var document = await generator.GenerateForControllerAsync(); var json = document.ToJson(); @@ -163,7 +163,7 @@ public async Task When_generic_request() // Arrange var generator = new WebApiOpenApiDocumentGenerator(new WebApiOpenApiDocumentGeneratorSettings { - SchemaSettings = new NewtonsoftJsonSchemaGeneratorSettings { SchemaType = SchemaType.Swagger2 } + SchemaSettings = new SystemTextJsonSchemaGeneratorSettings { SchemaType = SchemaType.Swagger2 } }); var document = await generator.GenerateForControllerAsync(); var json = document.ToJson(); @@ -192,7 +192,7 @@ public async Task When_consumes_is_url_encoded_then_construct_url_encoded_reques // Arrange var generator = new WebApiOpenApiDocumentGenerator(new WebApiOpenApiDocumentGeneratorSettings { - SchemaSettings = new NewtonsoftJsonSchemaGeneratorSettings { SchemaType = SchemaType.Swagger2 } + SchemaSettings = new SystemTextJsonSchemaGeneratorSettings { SchemaType = SchemaType.Swagger2 } }); var document = await generator.GenerateForControllerAsync(); var json = document.ToJson(); diff --git a/src/NSwag.CodeGeneration.TypeScript.Tests/AxiosTests.cs b/src/NSwag.CodeGeneration.TypeScript.Tests/AxiosTests.cs index 34c83bd376..fe7e5976f1 100644 --- a/src/NSwag.CodeGeneration.TypeScript.Tests/AxiosTests.cs +++ b/src/NSwag.CodeGeneration.TypeScript.Tests/AxiosTests.cs @@ -1,7 +1,7 @@ using NSwag.Generation.WebApi; using Microsoft.AspNetCore.Mvc; using NJsonSchema; -using NJsonSchema.NewtonsoftJson.Generation; +using NJsonSchema.Generation; using NSwag.CodeGeneration.Tests; namespace NSwag.CodeGeneration.TypeScript.Tests @@ -42,7 +42,7 @@ public async Task When_export_types_is_true_then_add_export_before_classes() // Arrange var generator = new WebApiOpenApiDocumentGenerator(new WebApiOpenApiDocumentGeneratorSettings { - SchemaSettings = new NewtonsoftJsonSchemaGeneratorSettings { SchemaType = SchemaType.Swagger2 } + SchemaSettings = new SystemTextJsonSchemaGeneratorSettings { SchemaType = SchemaType.Swagger2 } }); var document = await generator.GenerateForControllerAsync(); @@ -73,7 +73,7 @@ public async Task When_export_types_is_false_then_dont_add_export_before_classes // Arrange var generator = new WebApiOpenApiDocumentGenerator(new WebApiOpenApiDocumentGeneratorSettings { - SchemaSettings = new NewtonsoftJsonSchemaGeneratorSettings { SchemaType = SchemaType.Swagger2 } + SchemaSettings = new SystemTextJsonSchemaGeneratorSettings { SchemaType = SchemaType.Swagger2 } }); var document = await generator.GenerateForControllerAsync(); @@ -104,7 +104,7 @@ public async Task When_consumes_is_url_encoded_then_construct_url_encoded_reques // Arrange var generator = new WebApiOpenApiDocumentGenerator(new WebApiOpenApiDocumentGeneratorSettings { - SchemaSettings = new NewtonsoftJsonSchemaGeneratorSettings { SchemaType = SchemaType.Swagger2 } + SchemaSettings = new SystemTextJsonSchemaGeneratorSettings { SchemaType = SchemaType.Swagger2 } }); var document = await generator.GenerateForControllerAsync(); @@ -133,7 +133,7 @@ public async Task Add_cancel_token_to_every_call() // Arrange var generator = new WebApiOpenApiDocumentGenerator(new WebApiOpenApiDocumentGeneratorSettings { - SchemaSettings = new NewtonsoftJsonSchemaGeneratorSettings { SchemaType = SchemaType.Swagger2 } + SchemaSettings = new SystemTextJsonSchemaGeneratorSettings { SchemaType = SchemaType.Swagger2 } }); var document = await generator.GenerateForControllerAsync(); @@ -163,7 +163,7 @@ public async Task When_abort_signal() // Arrange var generator = new WebApiOpenApiDocumentGenerator(new WebApiOpenApiDocumentGeneratorSettings { - SchemaSettings = new NewtonsoftJsonSchemaGeneratorSettings + SchemaSettings = new SystemTextJsonSchemaGeneratorSettings { SchemaType = SchemaType.OpenApi3 } @@ -196,7 +196,7 @@ public async Task When_abort_signal_and_generate_client_interfaces_contains_sign // Arrange var generator = new WebApiOpenApiDocumentGenerator(new WebApiOpenApiDocumentGeneratorSettings { - SchemaSettings = new NewtonsoftJsonSchemaGeneratorSettings + SchemaSettings = new SystemTextJsonSchemaGeneratorSettings { SchemaType = SchemaType.OpenApi3 } diff --git a/src/NSwag.CodeGeneration.TypeScript.Tests/CodeGenerationTests.cs b/src/NSwag.CodeGeneration.TypeScript.Tests/CodeGenerationTests.cs index d3ef7f74f8..ca84546972 100644 --- a/src/NSwag.CodeGeneration.TypeScript.Tests/CodeGenerationTests.cs +++ b/src/NSwag.CodeGeneration.TypeScript.Tests/CodeGenerationTests.cs @@ -2,7 +2,6 @@ using NJsonSchema; using NJsonSchema.CodeGeneration.TypeScript; using NJsonSchema.Generation; -using NJsonSchema.NewtonsoftJson.Generation; using NSwag.CodeGeneration.Tests; namespace NSwag.CodeGeneration.TypeScript.Tests; @@ -207,7 +206,7 @@ public async Task When_generating_TypeScript_code_then_output_contains_expected_ private static OpenApiDocument CreateDocument() { var document = new OpenApiDocument(); - var settings = new NewtonsoftJsonSchemaGeneratorSettings(); + var settings = new SystemTextJsonSchemaGeneratorSettings(); var generator = new JsonSchemaGenerator(settings); document.Paths["/Person"] = new OpenApiPathItem diff --git a/src/NSwag.CodeGeneration.TypeScript.Tests/FetchTests.cs b/src/NSwag.CodeGeneration.TypeScript.Tests/FetchTests.cs index 0bc9354cc8..09bc1ba1d3 100644 --- a/src/NSwag.CodeGeneration.TypeScript.Tests/FetchTests.cs +++ b/src/NSwag.CodeGeneration.TypeScript.Tests/FetchTests.cs @@ -1,7 +1,7 @@ using NSwag.Generation.WebApi; using Microsoft.AspNetCore.Mvc; using NJsonSchema; -using NJsonSchema.NewtonsoftJson.Generation; +using NJsonSchema.Generation; using NSwag.CodeGeneration.Tests; namespace NSwag.CodeGeneration.TypeScript.Tests @@ -43,7 +43,7 @@ public async Task When_export_types_is_true_then_add_export_before_classes() // Arrange var generator = new WebApiOpenApiDocumentGenerator(new WebApiOpenApiDocumentGeneratorSettings { - SchemaSettings = new NewtonsoftJsonSchemaGeneratorSettings { SchemaType = SchemaType.Swagger2 } + SchemaSettings = new SystemTextJsonSchemaGeneratorSettings { SchemaType = SchemaType.Swagger2 } }); var document = await generator.GenerateForControllerAsync(); @@ -74,7 +74,7 @@ public async Task When_export_types_is_false_then_dont_add_export_before_classes // Arrange var generator = new WebApiOpenApiDocumentGenerator(new WebApiOpenApiDocumentGeneratorSettings { - SchemaSettings = new NewtonsoftJsonSchemaGeneratorSettings { SchemaType = SchemaType.Swagger2 } + SchemaSettings = new SystemTextJsonSchemaGeneratorSettings { SchemaType = SchemaType.Swagger2 } }); var document = await generator.GenerateForControllerAsync(); @@ -105,7 +105,7 @@ public async Task When_consumes_is_url_encoded_then_construct_url_encoded_reques // Arrange var generator = new WebApiOpenApiDocumentGenerator(new WebApiOpenApiDocumentGeneratorSettings { - SchemaSettings = new NewtonsoftJsonSchemaGeneratorSettings { SchemaType = SchemaType.Swagger2 } + SchemaSettings = new SystemTextJsonSchemaGeneratorSettings { SchemaType = SchemaType.Swagger2 } }); var document = await generator.GenerateForControllerAsync(); @@ -134,7 +134,7 @@ public async Task When_abort_signal() // Arrange var generator = new WebApiOpenApiDocumentGenerator(new WebApiOpenApiDocumentGeneratorSettings { - SchemaSettings = new NewtonsoftJsonSchemaGeneratorSettings { SchemaType = SchemaType.Swagger2 } + SchemaSettings = new SystemTextJsonSchemaGeneratorSettings { SchemaType = SchemaType.Swagger2 } }); var document = await generator.GenerateForControllerAsync(); @@ -164,7 +164,7 @@ public async Task When_no_abort_signal() // Arrange var generator = new WebApiOpenApiDocumentGenerator(new WebApiOpenApiDocumentGeneratorSettings { - SchemaSettings = new NewtonsoftJsonSchemaGeneratorSettings { SchemaType = SchemaType.Swagger2 } + SchemaSettings = new SystemTextJsonSchemaGeneratorSettings { SchemaType = SchemaType.Swagger2 } }); var document = await generator.GenerateForControllerAsync(); @@ -193,7 +193,7 @@ public async Task When_abort_signal_and_generate_client_interfaces_interface_con // Arrange var generator = new WebApiOpenApiDocumentGenerator(new WebApiOpenApiDocumentGeneratorSettings { - SchemaSettings = new NewtonsoftJsonSchemaGeneratorSettings { SchemaType = SchemaType.Swagger2 } + SchemaSettings = new SystemTextJsonSchemaGeneratorSettings { SchemaType = SchemaType.Swagger2 } }); var document = await generator.GenerateForControllerAsync(); @@ -224,7 +224,7 @@ public async Task When_includeHttpContext() // Arrange var generator = new WebApiOpenApiDocumentGenerator(new WebApiOpenApiDocumentGeneratorSettings { - SchemaSettings = new NewtonsoftJsonSchemaGeneratorSettings { SchemaType = SchemaType.Swagger2 } + SchemaSettings = new SystemTextJsonSchemaGeneratorSettings { SchemaType = SchemaType.Swagger2 } }); var document = await generator.GenerateForControllerAsync(); @@ -254,7 +254,7 @@ public async Task When_no_includeHttpContext() // Arrange var generator = new WebApiOpenApiDocumentGenerator(new WebApiOpenApiDocumentGeneratorSettings { - SchemaSettings = new NewtonsoftJsonSchemaGeneratorSettings { SchemaType = SchemaType.Swagger2 } + SchemaSettings = new SystemTextJsonSchemaGeneratorSettings { SchemaType = SchemaType.Swagger2 } }); var document = await generator.GenerateForControllerAsync(); @@ -285,7 +285,7 @@ public async Task When_include_RequestCredentialsType_Include() // Arrange var generator = new WebApiOpenApiDocumentGenerator(new WebApiOpenApiDocumentGeneratorSettings { - SchemaSettings = new NewtonsoftJsonSchemaGeneratorSettings { SchemaType = SchemaType.Swagger2 } + SchemaSettings = new SystemTextJsonSchemaGeneratorSettings { SchemaType = SchemaType.Swagger2 } }); var document = await generator.GenerateForControllerAsync(); @@ -311,7 +311,7 @@ public async Task When_include_RequestModeType_Cors() // Arrange var generator = new WebApiOpenApiDocumentGenerator(new WebApiOpenApiDocumentGeneratorSettings { - SchemaSettings = new NewtonsoftJsonSchemaGeneratorSettings { SchemaType = SchemaType.Swagger2 } + SchemaSettings = new SystemTextJsonSchemaGeneratorSettings { SchemaType = SchemaType.Swagger2 } }); var document = await generator.GenerateForControllerAsync(); diff --git a/src/NSwag.CodeGeneration.TypeScript.Tests/JQueryCallbacksTests.cs b/src/NSwag.CodeGeneration.TypeScript.Tests/JQueryCallbacksTests.cs index c8affe0488..f93c4faac4 100644 --- a/src/NSwag.CodeGeneration.TypeScript.Tests/JQueryCallbacksTests.cs +++ b/src/NSwag.CodeGeneration.TypeScript.Tests/JQueryCallbacksTests.cs @@ -1,7 +1,7 @@ using NSwag.Generation.WebApi; using Microsoft.AspNetCore.Mvc; using NJsonSchema; -using NJsonSchema.NewtonsoftJson.Generation; +using NJsonSchema.Generation; using NSwag.CodeGeneration.Tests; namespace NSwag.CodeGeneration.TypeScript.Tests @@ -36,7 +36,7 @@ public async Task When_export_types_is_true_then_add_export_before_classes() // Arrange var generator = new WebApiOpenApiDocumentGenerator(new WebApiOpenApiDocumentGeneratorSettings { - SchemaSettings = new NewtonsoftJsonSchemaGeneratorSettings { SchemaType = SchemaType.Swagger2 } + SchemaSettings = new SystemTextJsonSchemaGeneratorSettings { SchemaType = SchemaType.Swagger2 } }); var document = await generator.GenerateForControllerAsync(); @@ -67,7 +67,7 @@ public async Task When_export_types_is_false_then_dont_add_export_before_classes // Arrange var generator = new WebApiOpenApiDocumentGenerator(new WebApiOpenApiDocumentGeneratorSettings { - SchemaSettings = new NewtonsoftJsonSchemaGeneratorSettings { SchemaType = SchemaType.Swagger2 } + SchemaSettings = new SystemTextJsonSchemaGeneratorSettings { SchemaType = SchemaType.Swagger2 } }); var document = await generator.GenerateForControllerAsync(); @@ -98,7 +98,7 @@ public async Task When_consumes_is_url_encoded_then_construct_url_encoded_reques // Arrange var generator = new WebApiOpenApiDocumentGenerator(new WebApiOpenApiDocumentGeneratorSettings { - SchemaSettings = new NewtonsoftJsonSchemaGeneratorSettings { SchemaType = SchemaType.Swagger2 } + SchemaSettings = new SystemTextJsonSchemaGeneratorSettings { SchemaType = SchemaType.Swagger2 } }); var document = await generator.GenerateForControllerAsync(); diff --git a/src/NSwag.CodeGeneration.TypeScript.Tests/JQueryPromisesTests.cs b/src/NSwag.CodeGeneration.TypeScript.Tests/JQueryPromisesTests.cs index b5a9f1cfde..1d616f6673 100644 --- a/src/NSwag.CodeGeneration.TypeScript.Tests/JQueryPromisesTests.cs +++ b/src/NSwag.CodeGeneration.TypeScript.Tests/JQueryPromisesTests.cs @@ -1,6 +1,6 @@ using Microsoft.AspNetCore.Mvc; using NJsonSchema; -using NJsonSchema.NewtonsoftJson.Generation; +using NJsonSchema.Generation; using NSwag.CodeGeneration.Tests; using NSwag.Generation.WebApi; @@ -36,7 +36,7 @@ public async Task When_export_types_is_true_then_add_export_before_classes() // Arrange var generator = new WebApiOpenApiDocumentGenerator(new WebApiOpenApiDocumentGeneratorSettings { - SchemaSettings = new NewtonsoftJsonSchemaGeneratorSettings { SchemaType = SchemaType.Swagger2 } + SchemaSettings = new SystemTextJsonSchemaGeneratorSettings { SchemaType = SchemaType.Swagger2 } }); var document = await generator.GenerateForControllerAsync(); @@ -67,7 +67,7 @@ public async Task When_export_types_is_false_then_dont_add_export_before_classes // Arrange var generator = new WebApiOpenApiDocumentGenerator(new WebApiOpenApiDocumentGeneratorSettings { - SchemaSettings = new NewtonsoftJsonSchemaGeneratorSettings { SchemaType = SchemaType.Swagger2 } + SchemaSettings = new SystemTextJsonSchemaGeneratorSettings { SchemaType = SchemaType.Swagger2 } }); var document = await generator.GenerateForControllerAsync(); @@ -98,7 +98,7 @@ public async Task When_consumes_is_url_encoded_then_construct_url_encoded_reques // Arrange var generator = new WebApiOpenApiDocumentGenerator(new WebApiOpenApiDocumentGeneratorSettings { - SchemaSettings = new NewtonsoftJsonSchemaGeneratorSettings { SchemaType = SchemaType.Swagger2 } + SchemaSettings = new SystemTextJsonSchemaGeneratorSettings { SchemaType = SchemaType.Swagger2 } }); var document = await generator.GenerateForControllerAsync(); diff --git a/src/NSwag.CodeGeneration.TypeScript.Tests/K6Tests.cs b/src/NSwag.CodeGeneration.TypeScript.Tests/K6Tests.cs index 8e4bf22e18..6d5d86f64a 100644 --- a/src/NSwag.CodeGeneration.TypeScript.Tests/K6Tests.cs +++ b/src/NSwag.CodeGeneration.TypeScript.Tests/K6Tests.cs @@ -2,7 +2,7 @@ using Microsoft.AspNetCore.Mvc; using System.ComponentModel.DataAnnotations; using NJsonSchema; -using NJsonSchema.NewtonsoftJson.Generation; +using NJsonSchema.Generation; using NSwag.CodeGeneration.Tests; namespace NSwag.CodeGeneration.TypeScript.Tests @@ -77,7 +77,7 @@ public async Task When_return_value_is_void_then_client_returns_observable_of_vo // Arrange var generator = new WebApiOpenApiDocumentGenerator(new WebApiOpenApiDocumentGeneratorSettings { - SchemaSettings = new NewtonsoftJsonSchemaGeneratorSettings { SchemaType = SchemaType.Swagger2 } + SchemaSettings = new SystemTextJsonSchemaGeneratorSettings { SchemaType = SchemaType.Swagger2 } }); var document = await generator.GenerateForControllerAsync(); var json = document.ToJson(); @@ -106,7 +106,7 @@ public async Task When_export_types_is_true_then_add_export_before_classes() // Arrange var generator = new WebApiOpenApiDocumentGenerator(new WebApiOpenApiDocumentGeneratorSettings { - SchemaSettings = new NewtonsoftJsonSchemaGeneratorSettings { SchemaType = SchemaType.Swagger2 } + SchemaSettings = new SystemTextJsonSchemaGeneratorSettings { SchemaType = SchemaType.Swagger2 } }); var document = await generator.GenerateForControllerAsync(); var json = document.ToJson(); @@ -136,7 +136,7 @@ public async Task When_export_types_is_false_then_dont_add_export_before_classes // Arrange var generator = new WebApiOpenApiDocumentGenerator(new WebApiOpenApiDocumentGeneratorSettings { - SchemaSettings = new NewtonsoftJsonSchemaGeneratorSettings { SchemaType = SchemaType.Swagger2 } + SchemaSettings = new SystemTextJsonSchemaGeneratorSettings { SchemaType = SchemaType.Swagger2 } }); var document = await generator.GenerateForControllerAsync(); var json = document.ToJson(); @@ -166,7 +166,7 @@ public async Task When_generic_request() // Arrange var generator = new WebApiOpenApiDocumentGenerator(new WebApiOpenApiDocumentGeneratorSettings { - SchemaSettings = new NewtonsoftJsonSchemaGeneratorSettings { SchemaType = SchemaType.Swagger2 } + SchemaSettings = new SystemTextJsonSchemaGeneratorSettings { SchemaType = SchemaType.Swagger2 } }); var document = await generator.GenerateForControllerAsync(); var json = document.ToJson(); @@ -195,7 +195,7 @@ public async Task When_consumes_is_url_encoded_then_construct_url_encoded_reques // Arrange var generator = new WebApiOpenApiDocumentGenerator(new WebApiOpenApiDocumentGeneratorSettings { - SchemaSettings = new NewtonsoftJsonSchemaGeneratorSettings { SchemaType = SchemaType.Swagger2 } + SchemaSettings = new SystemTextJsonSchemaGeneratorSettings { SchemaType = SchemaType.Swagger2 } }); var document = await generator.GenerateForControllerAsync(); var json = document.ToJson(); diff --git a/src/NSwag.CodeGeneration.TypeScript.Tests/OperationParameterTests.cs b/src/NSwag.CodeGeneration.TypeScript.Tests/OperationParameterTests.cs index 8291ceb6b5..dacba725e5 100644 --- a/src/NSwag.CodeGeneration.TypeScript.Tests/OperationParameterTests.cs +++ b/src/NSwag.CodeGeneration.TypeScript.Tests/OperationParameterTests.cs @@ -1,9 +1,9 @@ -using Microsoft.AspNetCore.Mvc; -using Newtonsoft.Json; -using Newtonsoft.Json.Converters; +using System.Text.Json; +using System.Text.Json.Serialization; +using Microsoft.AspNetCore.Mvc; using NJsonSchema; using NSwag.Generation.WebApi; -using NJsonSchema.NewtonsoftJson.Generation; +using NJsonSchema.Generation; using NSwag.CodeGeneration.Tests; namespace NSwag.CodeGeneration.TypeScript.Tests @@ -38,18 +38,18 @@ public class Foo [Fact] public async Task When_query_parameter_is_enum_array_then_the_enum_is_referenced() { - var serializerSettings = new JsonSerializerSettings + var serializerOptions = new JsonSerializerOptions { - Converters = [new StringEnumConverter()] + Converters = { new JsonStringEnumConverter() } }; // Arrange var settings = new WebApiOpenApiDocumentGeneratorSettings { DefaultUrlTemplate = "api/{controller}/{action}/{id}", - SchemaSettings = new NewtonsoftJsonSchemaGeneratorSettings + SchemaSettings = new SystemTextJsonSchemaGeneratorSettings { - SerializerSettings = serializerSettings, + SerializerOptions = serializerOptions, SchemaType = SchemaType.Swagger2 } }; diff --git a/src/NSwag.CodeGeneration.TypeScript.Tests/Snapshots/JIRA_OpenAPI_Angular.verified.txt b/src/NSwag.CodeGeneration.TypeScript.Tests/Snapshots/JIRA_OpenAPI_Angular.verified.txt index 081d32d9b1..9a39593cf4 100644 --- a/src/NSwag.CodeGeneration.TypeScript.Tests/Snapshots/JIRA_OpenAPI_Angular.verified.txt +++ b/src/NSwag.CodeGeneration.TypeScript.Tests/Snapshots/JIRA_OpenAPI_Angular.verified.txt @@ -59021,7 +59021,7 @@ export interface IDeprecatedWorkflow { export class DetailedErrorCollection implements IDetailedErrorCollection { /** Map of objects representing additional details for an error */ - details?: { [key: string]: any; }; + details?: any; /** The list of error messages produced by this operation. For example, "input parameter 'key' must be provided" */ errorMessages?: string[]; /** The list of errors by parameter returned by the operation. For example,"projectKey": "Project keys must start with an uppercase letter, followed by one or more uppercase alphanumeric characters." */ @@ -59038,13 +59038,7 @@ export class DetailedErrorCollection implements IDetailedErrorCollection { init(_data?: any) { if (_data) { - if (_data["details"]) { - this.details = {} as any; - for (let key in _data["details"]) { - if (_data["details"].hasOwnProperty(key)) - (this.details as any)![key] = _data["details"][key]; - } - } + this.details = _data["details"]; if (Array.isArray(_data["errorMessages"])) { this.errorMessages = [] as any; for (let item of _data["errorMessages"]) @@ -59069,13 +59063,7 @@ export class DetailedErrorCollection implements IDetailedErrorCollection { toJSON(data?: any) { data = typeof data === 'object' ? data : {}; - if (this.details) { - data["details"] = {}; - for (let key in this.details) { - if (this.details.hasOwnProperty(key)) - (data["details"] as any)[key] = (this.details as any)[key]; - } - } + data["details"] = this.details; if (Array.isArray(this.errorMessages)) { data["errorMessages"] = []; for (let item of this.errorMessages) @@ -59094,7 +59082,7 @@ export class DetailedErrorCollection implements IDetailedErrorCollection { export interface IDetailedErrorCollection { /** Map of objects representing additional details for an error */ - details?: { [key: string]: any; }; + details?: any; /** The list of error messages produced by this operation. For example, "input parameter 'key' must be provided" */ errorMessages?: string[]; /** The list of errors by parameter returned by the operation. For example,"projectKey": "Project keys must start with an uppercase letter, followed by one or more uppercase alphanumeric characters." */ @@ -64623,7 +64611,7 @@ export class IssueBean implements IIssueBean { readonly editmeta?: IssueUpdateMetadata; /** Expand options that include additional issue details in the response. */ readonly expand?: string; - fields?: { [key: string]: any; }; + fields?: any; fieldsToInclude?: IncludedFields; /** The ID of the issue. */ readonly id?: string; @@ -64660,13 +64648,7 @@ export class IssueBean implements IIssueBean { (this as any).changelog = _data["changelog"] ? PageOfChangelogs.fromJS(_data["changelog"]) : undefined as any; (this as any).editmeta = _data["editmeta"] ? IssueUpdateMetadata.fromJS(_data["editmeta"]) : undefined as any; (this as any).expand = _data["expand"]; - if (_data["fields"]) { - this.fields = {} as any; - for (let key in _data["fields"]) { - if (_data["fields"].hasOwnProperty(key)) - (this.fields as any)![key] = _data["fields"][key]; - } - } + this.fields = _data["fields"]; this.fieldsToInclude = _data["fieldsToInclude"] ? IncludedFields.fromJS(_data["fieldsToInclude"]) : undefined as any; (this as any).id = _data["id"]; (this as any).key = _data["key"]; @@ -64727,13 +64709,7 @@ export class IssueBean implements IIssueBean { data["changelog"] = this.changelog ? this.changelog.toJSON() : undefined as any; data["editmeta"] = this.editmeta ? this.editmeta.toJSON() : undefined as any; data["expand"] = this.expand; - if (this.fields) { - data["fields"] = {}; - for (let key in this.fields) { - if (this.fields.hasOwnProperty(key)) - (data["fields"] as any)[key] = (this.fields as any)[key]; - } - } + data["fields"] = this.fields; data["fieldsToInclude"] = this.fieldsToInclude ? this.fieldsToInclude.toJSON() : undefined as any; data["id"] = this.id; data["key"] = this.key; @@ -64791,7 +64767,7 @@ export interface IIssueBean { editmeta?: IssueUpdateMetadata; /** Expand options that include additional issue details in the response. */ expand?: string; - fields?: { [key: string]: any; }; + fields?: any; fieldsToInclude?: IncludedFields; /** The ID of the issue. */ id?: string; @@ -65853,7 +65829,7 @@ export class IssueFieldOption implements IIssueFieldOption { /** The unique identifier for the option. This is only unique within the select field's set of options. */ id!: number; /** The properties of the object, as arbitrary key-value pairs. These properties can be searched using JQL, if the extractions (see [Issue Field Option Property Index](https://developer.atlassian.com/cloud/jira/platform/modules/issue-field-option-property-index/)) are defined in the descriptor for the issue field module. */ - properties?: { [key: string]: any; }; + properties?: any; /** The option's name, which is displayed in Jira. */ value!: string; @@ -65870,13 +65846,7 @@ export class IssueFieldOption implements IIssueFieldOption { if (_data) { this.config = _data["config"] ? IssueFieldOptionConfiguration.fromJS(_data["config"]) : undefined as any; this.id = _data["id"]; - if (_data["properties"]) { - this.properties = {} as any; - for (let key in _data["properties"]) { - if (_data["properties"].hasOwnProperty(key)) - (this.properties as any)![key] = _data["properties"][key]; - } - } + this.properties = _data["properties"]; this.value = _data["value"]; } } @@ -65892,13 +65862,7 @@ export class IssueFieldOption implements IIssueFieldOption { data = typeof data === 'object' ? data : {}; data["config"] = this.config ? this.config.toJSON() : undefined as any; data["id"] = this.id; - if (this.properties) { - data["properties"] = {}; - for (let key in this.properties) { - if (this.properties.hasOwnProperty(key)) - (data["properties"] as any)[key] = (this.properties as any)[key]; - } - } + data["properties"] = this.properties; data["value"] = this.value; return data; } @@ -65910,7 +65874,7 @@ export interface IIssueFieldOption { /** The unique identifier for the option. This is only unique within the select field's set of options. */ id: number; /** The properties of the object, as arbitrary key-value pairs. These properties can be searched using JQL, if the extractions (see [Issue Field Option Property Index](https://developer.atlassian.com/cloud/jira/platform/modules/issue-field-option-property-index/)) are defined in the descriptor for the issue field module. */ - properties?: { [key: string]: any; }; + properties?: any; /** The option's name, which is displayed in Jira. */ value: string; } @@ -65972,7 +65936,7 @@ export interface IIssueFieldOptionConfiguration { export class IssueFieldOptionCreateBean implements IIssueFieldOptionCreateBean { config?: IssueFieldOptionConfiguration; /** The properties of the option as arbitrary key-value pairs. These properties can be searched using JQL, if the extractions (see https://developer.atlassian.com/cloud/jira/platform/modules/issue-field-option-property-index/) are defined in the descriptor for the issue field module. */ - properties?: { [key: string]: any; }; + properties?: any; /** The option's name, which is displayed in Jira. */ value!: string; @@ -65994,13 +65958,7 @@ export class IssueFieldOptionCreateBean implements IIssueFieldOptionCreateBean { this[property] = _data[property]; } this.config = _data["config"] ? IssueFieldOptionConfiguration.fromJS(_data["config"]) : undefined as any; - if (_data["properties"]) { - this.properties = {} as any; - for (let key in _data["properties"]) { - if (_data["properties"].hasOwnProperty(key)) - (this.properties as any)![key] = _data["properties"][key]; - } - } + this.properties = _data["properties"]; this.value = _data["value"]; } } @@ -66019,13 +65977,7 @@ export class IssueFieldOptionCreateBean implements IIssueFieldOptionCreateBean { data[property] = this[property]; } data["config"] = this.config ? this.config.toJSON() : undefined as any; - if (this.properties) { - data["properties"] = {}; - for (let key in this.properties) { - if (this.properties.hasOwnProperty(key)) - (data["properties"] as any)[key] = (this.properties as any)[key]; - } - } + data["properties"] = this.properties; data["value"] = this.value; return data; } @@ -66034,7 +65986,7 @@ export class IssueFieldOptionCreateBean implements IIssueFieldOptionCreateBean { export interface IIssueFieldOptionCreateBean { config?: IssueFieldOptionConfiguration; /** The properties of the option as arbitrary key-value pairs. These properties can be searched using JQL, if the extractions (see https://developer.atlassian.com/cloud/jira/platform/modules/issue-field-option-property-index/) are defined in the descriptor for the issue field module. */ - properties?: { [key: string]: any; }; + properties?: any; /** The option's name, which is displayed in Jira. */ value: string; @@ -69078,7 +69030,7 @@ export interface IIssueTypesWorkflowMapping { /** Details of an issue update request. */ export class IssueUpdateDetails implements IIssueUpdateDetails { /** List of issue screen fields to update, specifying the sub-field to update and its value for each field. This field provides a straightforward option when setting a sub-field. When multiple sub-fields or other operations are required, use `update`. Fields included in here cannot be included in `update`. */ - fields?: { [key: string]: any; }; + fields?: any; /** Additional issue history details. */ historyMetadata?: HistoryMetadata; /** Details of issue properties to be add or update. */ @@ -69105,13 +69057,7 @@ export class IssueUpdateDetails implements IIssueUpdateDetails { if (_data.hasOwnProperty(property)) this[property] = _data[property]; } - if (_data["fields"]) { - this.fields = {} as any; - for (let key in _data["fields"]) { - if (_data["fields"].hasOwnProperty(key)) - (this.fields as any)![key] = _data["fields"][key]; - } - } + this.fields = _data["fields"]; this.historyMetadata = _data["historyMetadata"] ? HistoryMetadata.fromJS(_data["historyMetadata"]) : undefined as any; if (Array.isArray(_data["properties"])) { this.properties = [] as any; @@ -69142,13 +69088,7 @@ export class IssueUpdateDetails implements IIssueUpdateDetails { if (this.hasOwnProperty(property)) data[property] = this[property]; } - if (this.fields) { - data["fields"] = {}; - for (let key in this.fields) { - if (this.fields.hasOwnProperty(key)) - (data["fields"] as any)[key] = (this.fields as any)[key]; - } - } + data["fields"] = this.fields; data["historyMetadata"] = this.historyMetadata ? this.historyMetadata.toJSON() : undefined as any; if (Array.isArray(this.properties)) { data["properties"] = []; @@ -69170,7 +69110,7 @@ export class IssueUpdateDetails implements IIssueUpdateDetails { /** Details of an issue update request. */ export interface IIssueUpdateDetails { /** List of issue screen fields to update, specifying the sub-field to update and its value for each field. This field provides a straightforward option when setting a sub-field. When multiple sub-fields or other operations are required, use `update`. Fields included in here cannot be included in `update`. */ - fields?: { [key: string]: any; }; + fields?: any; /** Additional issue history details. */ historyMetadata?: HistoryMetadata; /** Details of issue properties to be add or update. */ @@ -72202,7 +72142,7 @@ export interface IJiraRichTextField { } export class JiraRichTextInput implements IJiraRichTextInput { - adfValue?: { [key: string]: any; }; + adfValue?: any; constructor(data?: IJiraRichTextInput) { if (data) { @@ -72215,13 +72155,7 @@ export class JiraRichTextInput implements IJiraRichTextInput { init(_data?: any) { if (_data) { - if (_data["adfValue"]) { - this.adfValue = {} as any; - for (let key in _data["adfValue"]) { - if (_data["adfValue"].hasOwnProperty(key)) - (this.adfValue as any)![key] = _data["adfValue"][key]; - } - } + this.adfValue = _data["adfValue"]; } } @@ -72234,19 +72168,13 @@ export class JiraRichTextInput implements IJiraRichTextInput { toJSON(data?: any) { data = typeof data === 'object' ? data : {}; - if (this.adfValue) { - data["adfValue"] = {}; - for (let key in this.adfValue) { - if (this.adfValue.hasOwnProperty(key)) - (data["adfValue"] as any)[key] = (this.adfValue as any)[key]; - } - } + data["adfValue"] = this.adfValue; return data; } } export interface IJiraRichTextInput { - adfValue?: { [key: string]: any; }; + adfValue?: any; } export class JiraSelectedOptionField implements IJiraSelectedOptionField { diff --git a/src/NSwag.CodeGeneration.TypeScript.Tests/Snapshots/JIRA_OpenAPI_AngularJS.verified.txt b/src/NSwag.CodeGeneration.TypeScript.Tests/Snapshots/JIRA_OpenAPI_AngularJS.verified.txt index 9e264021b0..5c273aaa7b 100644 --- a/src/NSwag.CodeGeneration.TypeScript.Tests/Snapshots/JIRA_OpenAPI_AngularJS.verified.txt +++ b/src/NSwag.CodeGeneration.TypeScript.Tests/Snapshots/JIRA_OpenAPI_AngularJS.verified.txt @@ -54316,7 +54316,7 @@ export interface IDeprecatedWorkflow { export class DetailedErrorCollection implements IDetailedErrorCollection { /** Map of objects representing additional details for an error */ - details?: { [key: string]: any; }; + details?: any; /** The list of error messages produced by this operation. For example, "input parameter 'key' must be provided" */ errorMessages?: string[]; /** The list of errors by parameter returned by the operation. For example,"projectKey": "Project keys must start with an uppercase letter, followed by one or more uppercase alphanumeric characters." */ @@ -54333,13 +54333,7 @@ export class DetailedErrorCollection implements IDetailedErrorCollection { init(_data?: any) { if (_data) { - if (_data["details"]) { - this.details = {} as any; - for (let key in _data["details"]) { - if (_data["details"].hasOwnProperty(key)) - (this.details as any)![key] = _data["details"][key]; - } - } + this.details = _data["details"]; if (Array.isArray(_data["errorMessages"])) { this.errorMessages = [] as any; for (let item of _data["errorMessages"]) @@ -54364,13 +54358,7 @@ export class DetailedErrorCollection implements IDetailedErrorCollection { toJSON(data?: any) { data = typeof data === 'object' ? data : {}; - if (this.details) { - data["details"] = {}; - for (let key in this.details) { - if (this.details.hasOwnProperty(key)) - (data["details"] as any)[key] = (this.details as any)[key]; - } - } + data["details"] = this.details; if (Array.isArray(this.errorMessages)) { data["errorMessages"] = []; for (let item of this.errorMessages) @@ -54389,7 +54377,7 @@ export class DetailedErrorCollection implements IDetailedErrorCollection { export interface IDetailedErrorCollection { /** Map of objects representing additional details for an error */ - details?: { [key: string]: any; }; + details?: any; /** The list of error messages produced by this operation. For example, "input parameter 'key' must be provided" */ errorMessages?: string[]; /** The list of errors by parameter returned by the operation. For example,"projectKey": "Project keys must start with an uppercase letter, followed by one or more uppercase alphanumeric characters." */ @@ -59918,7 +59906,7 @@ export class IssueBean implements IIssueBean { readonly editmeta?: IssueUpdateMetadata; /** Expand options that include additional issue details in the response. */ readonly expand?: string; - fields?: { [key: string]: any; }; + fields?: any; fieldsToInclude?: IncludedFields; /** The ID of the issue. */ readonly id?: string; @@ -59955,13 +59943,7 @@ export class IssueBean implements IIssueBean { (this as any).changelog = _data["changelog"] ? PageOfChangelogs.fromJS(_data["changelog"]) : undefined as any; (this as any).editmeta = _data["editmeta"] ? IssueUpdateMetadata.fromJS(_data["editmeta"]) : undefined as any; (this as any).expand = _data["expand"]; - if (_data["fields"]) { - this.fields = {} as any; - for (let key in _data["fields"]) { - if (_data["fields"].hasOwnProperty(key)) - (this.fields as any)![key] = _data["fields"][key]; - } - } + this.fields = _data["fields"]; this.fieldsToInclude = _data["fieldsToInclude"] ? IncludedFields.fromJS(_data["fieldsToInclude"]) : undefined as any; (this as any).id = _data["id"]; (this as any).key = _data["key"]; @@ -60022,13 +60004,7 @@ export class IssueBean implements IIssueBean { data["changelog"] = this.changelog ? this.changelog.toJSON() : undefined as any; data["editmeta"] = this.editmeta ? this.editmeta.toJSON() : undefined as any; data["expand"] = this.expand; - if (this.fields) { - data["fields"] = {}; - for (let key in this.fields) { - if (this.fields.hasOwnProperty(key)) - (data["fields"] as any)[key] = (this.fields as any)[key]; - } - } + data["fields"] = this.fields; data["fieldsToInclude"] = this.fieldsToInclude ? this.fieldsToInclude.toJSON() : undefined as any; data["id"] = this.id; data["key"] = this.key; @@ -60086,7 +60062,7 @@ export interface IIssueBean { editmeta?: IssueUpdateMetadata; /** Expand options that include additional issue details in the response. */ expand?: string; - fields?: { [key: string]: any; }; + fields?: any; fieldsToInclude?: IncludedFields; /** The ID of the issue. */ id?: string; @@ -61148,7 +61124,7 @@ export class IssueFieldOption implements IIssueFieldOption { /** The unique identifier for the option. This is only unique within the select field's set of options. */ id!: number; /** The properties of the object, as arbitrary key-value pairs. These properties can be searched using JQL, if the extractions (see [Issue Field Option Property Index](https://developer.atlassian.com/cloud/jira/platform/modules/issue-field-option-property-index/)) are defined in the descriptor for the issue field module. */ - properties?: { [key: string]: any; }; + properties?: any; /** The option's name, which is displayed in Jira. */ value!: string; @@ -61165,13 +61141,7 @@ export class IssueFieldOption implements IIssueFieldOption { if (_data) { this.config = _data["config"] ? IssueFieldOptionConfiguration.fromJS(_data["config"]) : undefined as any; this.id = _data["id"]; - if (_data["properties"]) { - this.properties = {} as any; - for (let key in _data["properties"]) { - if (_data["properties"].hasOwnProperty(key)) - (this.properties as any)![key] = _data["properties"][key]; - } - } + this.properties = _data["properties"]; this.value = _data["value"]; } } @@ -61187,13 +61157,7 @@ export class IssueFieldOption implements IIssueFieldOption { data = typeof data === 'object' ? data : {}; data["config"] = this.config ? this.config.toJSON() : undefined as any; data["id"] = this.id; - if (this.properties) { - data["properties"] = {}; - for (let key in this.properties) { - if (this.properties.hasOwnProperty(key)) - (data["properties"] as any)[key] = (this.properties as any)[key]; - } - } + data["properties"] = this.properties; data["value"] = this.value; return data; } @@ -61205,7 +61169,7 @@ export interface IIssueFieldOption { /** The unique identifier for the option. This is only unique within the select field's set of options. */ id: number; /** The properties of the object, as arbitrary key-value pairs. These properties can be searched using JQL, if the extractions (see [Issue Field Option Property Index](https://developer.atlassian.com/cloud/jira/platform/modules/issue-field-option-property-index/)) are defined in the descriptor for the issue field module. */ - properties?: { [key: string]: any; }; + properties?: any; /** The option's name, which is displayed in Jira. */ value: string; } @@ -61267,7 +61231,7 @@ export interface IIssueFieldOptionConfiguration { export class IssueFieldOptionCreateBean implements IIssueFieldOptionCreateBean { config?: IssueFieldOptionConfiguration; /** The properties of the option as arbitrary key-value pairs. These properties can be searched using JQL, if the extractions (see https://developer.atlassian.com/cloud/jira/platform/modules/issue-field-option-property-index/) are defined in the descriptor for the issue field module. */ - properties?: { [key: string]: any; }; + properties?: any; /** The option's name, which is displayed in Jira. */ value!: string; @@ -61289,13 +61253,7 @@ export class IssueFieldOptionCreateBean implements IIssueFieldOptionCreateBean { this[property] = _data[property]; } this.config = _data["config"] ? IssueFieldOptionConfiguration.fromJS(_data["config"]) : undefined as any; - if (_data["properties"]) { - this.properties = {} as any; - for (let key in _data["properties"]) { - if (_data["properties"].hasOwnProperty(key)) - (this.properties as any)![key] = _data["properties"][key]; - } - } + this.properties = _data["properties"]; this.value = _data["value"]; } } @@ -61314,13 +61272,7 @@ export class IssueFieldOptionCreateBean implements IIssueFieldOptionCreateBean { data[property] = this[property]; } data["config"] = this.config ? this.config.toJSON() : undefined as any; - if (this.properties) { - data["properties"] = {}; - for (let key in this.properties) { - if (this.properties.hasOwnProperty(key)) - (data["properties"] as any)[key] = (this.properties as any)[key]; - } - } + data["properties"] = this.properties; data["value"] = this.value; return data; } @@ -61329,7 +61281,7 @@ export class IssueFieldOptionCreateBean implements IIssueFieldOptionCreateBean { export interface IIssueFieldOptionCreateBean { config?: IssueFieldOptionConfiguration; /** The properties of the option as arbitrary key-value pairs. These properties can be searched using JQL, if the extractions (see https://developer.atlassian.com/cloud/jira/platform/modules/issue-field-option-property-index/) are defined in the descriptor for the issue field module. */ - properties?: { [key: string]: any; }; + properties?: any; /** The option's name, which is displayed in Jira. */ value: string; @@ -64373,7 +64325,7 @@ export interface IIssueTypesWorkflowMapping { /** Details of an issue update request. */ export class IssueUpdateDetails implements IIssueUpdateDetails { /** List of issue screen fields to update, specifying the sub-field to update and its value for each field. This field provides a straightforward option when setting a sub-field. When multiple sub-fields or other operations are required, use `update`. Fields included in here cannot be included in `update`. */ - fields?: { [key: string]: any; }; + fields?: any; /** Additional issue history details. */ historyMetadata?: HistoryMetadata; /** Details of issue properties to be add or update. */ @@ -64400,13 +64352,7 @@ export class IssueUpdateDetails implements IIssueUpdateDetails { if (_data.hasOwnProperty(property)) this[property] = _data[property]; } - if (_data["fields"]) { - this.fields = {} as any; - for (let key in _data["fields"]) { - if (_data["fields"].hasOwnProperty(key)) - (this.fields as any)![key] = _data["fields"][key]; - } - } + this.fields = _data["fields"]; this.historyMetadata = _data["historyMetadata"] ? HistoryMetadata.fromJS(_data["historyMetadata"]) : undefined as any; if (Array.isArray(_data["properties"])) { this.properties = [] as any; @@ -64437,13 +64383,7 @@ export class IssueUpdateDetails implements IIssueUpdateDetails { if (this.hasOwnProperty(property)) data[property] = this[property]; } - if (this.fields) { - data["fields"] = {}; - for (let key in this.fields) { - if (this.fields.hasOwnProperty(key)) - (data["fields"] as any)[key] = (this.fields as any)[key]; - } - } + data["fields"] = this.fields; data["historyMetadata"] = this.historyMetadata ? this.historyMetadata.toJSON() : undefined as any; if (Array.isArray(this.properties)) { data["properties"] = []; @@ -64465,7 +64405,7 @@ export class IssueUpdateDetails implements IIssueUpdateDetails { /** Details of an issue update request. */ export interface IIssueUpdateDetails { /** List of issue screen fields to update, specifying the sub-field to update and its value for each field. This field provides a straightforward option when setting a sub-field. When multiple sub-fields or other operations are required, use `update`. Fields included in here cannot be included in `update`. */ - fields?: { [key: string]: any; }; + fields?: any; /** Additional issue history details. */ historyMetadata?: HistoryMetadata; /** Details of issue properties to be add or update. */ @@ -67497,7 +67437,7 @@ export interface IJiraRichTextField { } export class JiraRichTextInput implements IJiraRichTextInput { - adfValue?: { [key: string]: any; }; + adfValue?: any; constructor(data?: IJiraRichTextInput) { if (data) { @@ -67510,13 +67450,7 @@ export class JiraRichTextInput implements IJiraRichTextInput { init(_data?: any) { if (_data) { - if (_data["adfValue"]) { - this.adfValue = {} as any; - for (let key in _data["adfValue"]) { - if (_data["adfValue"].hasOwnProperty(key)) - (this.adfValue as any)![key] = _data["adfValue"][key]; - } - } + this.adfValue = _data["adfValue"]; } } @@ -67529,19 +67463,13 @@ export class JiraRichTextInput implements IJiraRichTextInput { toJSON(data?: any) { data = typeof data === 'object' ? data : {}; - if (this.adfValue) { - data["adfValue"] = {}; - for (let key in this.adfValue) { - if (this.adfValue.hasOwnProperty(key)) - (data["adfValue"] as any)[key] = (this.adfValue as any)[key]; - } - } + data["adfValue"] = this.adfValue; return data; } } export interface IJiraRichTextInput { - adfValue?: { [key: string]: any; }; + adfValue?: any; } export class JiraSelectedOptionField implements IJiraSelectedOptionField { diff --git a/src/NSwag.CodeGeneration.TypeScript.Tests/Snapshots/JIRA_OpenAPI_Aurelia.verified.txt b/src/NSwag.CodeGeneration.TypeScript.Tests/Snapshots/JIRA_OpenAPI_Aurelia.verified.txt index 38a1caf276..51d27539d7 100644 --- a/src/NSwag.CodeGeneration.TypeScript.Tests/Snapshots/JIRA_OpenAPI_Aurelia.verified.txt +++ b/src/NSwag.CodeGeneration.TypeScript.Tests/Snapshots/JIRA_OpenAPI_Aurelia.verified.txt @@ -50771,7 +50771,7 @@ export interface IDeprecatedWorkflow { export class DetailedErrorCollection implements IDetailedErrorCollection { /** Map of objects representing additional details for an error */ - details?: { [key: string]: any; }; + details?: any; /** The list of error messages produced by this operation. For example, "input parameter 'key' must be provided" */ errorMessages?: string[]; /** The list of errors by parameter returned by the operation. For example,"projectKey": "Project keys must start with an uppercase letter, followed by one or more uppercase alphanumeric characters." */ @@ -50788,13 +50788,7 @@ export class DetailedErrorCollection implements IDetailedErrorCollection { init(_data?: any) { if (_data) { - if (_data["details"]) { - this.details = {} as any; - for (let key in _data["details"]) { - if (_data["details"].hasOwnProperty(key)) - (this.details as any)![key] = _data["details"][key]; - } - } + this.details = _data["details"]; if (Array.isArray(_data["errorMessages"])) { this.errorMessages = [] as any; for (let item of _data["errorMessages"]) @@ -50819,13 +50813,7 @@ export class DetailedErrorCollection implements IDetailedErrorCollection { toJSON(data?: any) { data = typeof data === 'object' ? data : {}; - if (this.details) { - data["details"] = {}; - for (let key in this.details) { - if (this.details.hasOwnProperty(key)) - (data["details"] as any)[key] = (this.details as any)[key]; - } - } + data["details"] = this.details; if (Array.isArray(this.errorMessages)) { data["errorMessages"] = []; for (let item of this.errorMessages) @@ -50844,7 +50832,7 @@ export class DetailedErrorCollection implements IDetailedErrorCollection { export interface IDetailedErrorCollection { /** Map of objects representing additional details for an error */ - details?: { [key: string]: any; }; + details?: any; /** The list of error messages produced by this operation. For example, "input parameter 'key' must be provided" */ errorMessages?: string[]; /** The list of errors by parameter returned by the operation. For example,"projectKey": "Project keys must start with an uppercase letter, followed by one or more uppercase alphanumeric characters." */ @@ -56373,7 +56361,7 @@ export class IssueBean implements IIssueBean { readonly editmeta?: IssueUpdateMetadata; /** Expand options that include additional issue details in the response. */ readonly expand?: string; - fields?: { [key: string]: any; }; + fields?: any; fieldsToInclude?: IncludedFields; /** The ID of the issue. */ readonly id?: string; @@ -56410,13 +56398,7 @@ export class IssueBean implements IIssueBean { (this as any).changelog = _data["changelog"] ? PageOfChangelogs.fromJS(_data["changelog"]) : undefined as any; (this as any).editmeta = _data["editmeta"] ? IssueUpdateMetadata.fromJS(_data["editmeta"]) : undefined as any; (this as any).expand = _data["expand"]; - if (_data["fields"]) { - this.fields = {} as any; - for (let key in _data["fields"]) { - if (_data["fields"].hasOwnProperty(key)) - (this.fields as any)![key] = _data["fields"][key]; - } - } + this.fields = _data["fields"]; this.fieldsToInclude = _data["fieldsToInclude"] ? IncludedFields.fromJS(_data["fieldsToInclude"]) : undefined as any; (this as any).id = _data["id"]; (this as any).key = _data["key"]; @@ -56477,13 +56459,7 @@ export class IssueBean implements IIssueBean { data["changelog"] = this.changelog ? this.changelog.toJSON() : undefined as any; data["editmeta"] = this.editmeta ? this.editmeta.toJSON() : undefined as any; data["expand"] = this.expand; - if (this.fields) { - data["fields"] = {}; - for (let key in this.fields) { - if (this.fields.hasOwnProperty(key)) - (data["fields"] as any)[key] = (this.fields as any)[key]; - } - } + data["fields"] = this.fields; data["fieldsToInclude"] = this.fieldsToInclude ? this.fieldsToInclude.toJSON() : undefined as any; data["id"] = this.id; data["key"] = this.key; @@ -56541,7 +56517,7 @@ export interface IIssueBean { editmeta?: IssueUpdateMetadata; /** Expand options that include additional issue details in the response. */ expand?: string; - fields?: { [key: string]: any; }; + fields?: any; fieldsToInclude?: IncludedFields; /** The ID of the issue. */ id?: string; @@ -57603,7 +57579,7 @@ export class IssueFieldOption implements IIssueFieldOption { /** The unique identifier for the option. This is only unique within the select field's set of options. */ id!: number; /** The properties of the object, as arbitrary key-value pairs. These properties can be searched using JQL, if the extractions (see [Issue Field Option Property Index](https://developer.atlassian.com/cloud/jira/platform/modules/issue-field-option-property-index/)) are defined in the descriptor for the issue field module. */ - properties?: { [key: string]: any; }; + properties?: any; /** The option's name, which is displayed in Jira. */ value!: string; @@ -57620,13 +57596,7 @@ export class IssueFieldOption implements IIssueFieldOption { if (_data) { this.config = _data["config"] ? IssueFieldOptionConfiguration.fromJS(_data["config"]) : undefined as any; this.id = _data["id"]; - if (_data["properties"]) { - this.properties = {} as any; - for (let key in _data["properties"]) { - if (_data["properties"].hasOwnProperty(key)) - (this.properties as any)![key] = _data["properties"][key]; - } - } + this.properties = _data["properties"]; this.value = _data["value"]; } } @@ -57642,13 +57612,7 @@ export class IssueFieldOption implements IIssueFieldOption { data = typeof data === 'object' ? data : {}; data["config"] = this.config ? this.config.toJSON() : undefined as any; data["id"] = this.id; - if (this.properties) { - data["properties"] = {}; - for (let key in this.properties) { - if (this.properties.hasOwnProperty(key)) - (data["properties"] as any)[key] = (this.properties as any)[key]; - } - } + data["properties"] = this.properties; data["value"] = this.value; return data; } @@ -57660,7 +57624,7 @@ export interface IIssueFieldOption { /** The unique identifier for the option. This is only unique within the select field's set of options. */ id: number; /** The properties of the object, as arbitrary key-value pairs. These properties can be searched using JQL, if the extractions (see [Issue Field Option Property Index](https://developer.atlassian.com/cloud/jira/platform/modules/issue-field-option-property-index/)) are defined in the descriptor for the issue field module. */ - properties?: { [key: string]: any; }; + properties?: any; /** The option's name, which is displayed in Jira. */ value: string; } @@ -57722,7 +57686,7 @@ export interface IIssueFieldOptionConfiguration { export class IssueFieldOptionCreateBean implements IIssueFieldOptionCreateBean { config?: IssueFieldOptionConfiguration; /** The properties of the option as arbitrary key-value pairs. These properties can be searched using JQL, if the extractions (see https://developer.atlassian.com/cloud/jira/platform/modules/issue-field-option-property-index/) are defined in the descriptor for the issue field module. */ - properties?: { [key: string]: any; }; + properties?: any; /** The option's name, which is displayed in Jira. */ value!: string; @@ -57744,13 +57708,7 @@ export class IssueFieldOptionCreateBean implements IIssueFieldOptionCreateBean { this[property] = _data[property]; } this.config = _data["config"] ? IssueFieldOptionConfiguration.fromJS(_data["config"]) : undefined as any; - if (_data["properties"]) { - this.properties = {} as any; - for (let key in _data["properties"]) { - if (_data["properties"].hasOwnProperty(key)) - (this.properties as any)![key] = _data["properties"][key]; - } - } + this.properties = _data["properties"]; this.value = _data["value"]; } } @@ -57769,13 +57727,7 @@ export class IssueFieldOptionCreateBean implements IIssueFieldOptionCreateBean { data[property] = this[property]; } data["config"] = this.config ? this.config.toJSON() : undefined as any; - if (this.properties) { - data["properties"] = {}; - for (let key in this.properties) { - if (this.properties.hasOwnProperty(key)) - (data["properties"] as any)[key] = (this.properties as any)[key]; - } - } + data["properties"] = this.properties; data["value"] = this.value; return data; } @@ -57784,7 +57736,7 @@ export class IssueFieldOptionCreateBean implements IIssueFieldOptionCreateBean { export interface IIssueFieldOptionCreateBean { config?: IssueFieldOptionConfiguration; /** The properties of the option as arbitrary key-value pairs. These properties can be searched using JQL, if the extractions (see https://developer.atlassian.com/cloud/jira/platform/modules/issue-field-option-property-index/) are defined in the descriptor for the issue field module. */ - properties?: { [key: string]: any; }; + properties?: any; /** The option's name, which is displayed in Jira. */ value: string; @@ -60828,7 +60780,7 @@ export interface IIssueTypesWorkflowMapping { /** Details of an issue update request. */ export class IssueUpdateDetails implements IIssueUpdateDetails { /** List of issue screen fields to update, specifying the sub-field to update and its value for each field. This field provides a straightforward option when setting a sub-field. When multiple sub-fields or other operations are required, use `update`. Fields included in here cannot be included in `update`. */ - fields?: { [key: string]: any; }; + fields?: any; /** Additional issue history details. */ historyMetadata?: HistoryMetadata; /** Details of issue properties to be add or update. */ @@ -60855,13 +60807,7 @@ export class IssueUpdateDetails implements IIssueUpdateDetails { if (_data.hasOwnProperty(property)) this[property] = _data[property]; } - if (_data["fields"]) { - this.fields = {} as any; - for (let key in _data["fields"]) { - if (_data["fields"].hasOwnProperty(key)) - (this.fields as any)![key] = _data["fields"][key]; - } - } + this.fields = _data["fields"]; this.historyMetadata = _data["historyMetadata"] ? HistoryMetadata.fromJS(_data["historyMetadata"]) : undefined as any; if (Array.isArray(_data["properties"])) { this.properties = [] as any; @@ -60892,13 +60838,7 @@ export class IssueUpdateDetails implements IIssueUpdateDetails { if (this.hasOwnProperty(property)) data[property] = this[property]; } - if (this.fields) { - data["fields"] = {}; - for (let key in this.fields) { - if (this.fields.hasOwnProperty(key)) - (data["fields"] as any)[key] = (this.fields as any)[key]; - } - } + data["fields"] = this.fields; data["historyMetadata"] = this.historyMetadata ? this.historyMetadata.toJSON() : undefined as any; if (Array.isArray(this.properties)) { data["properties"] = []; @@ -60920,7 +60860,7 @@ export class IssueUpdateDetails implements IIssueUpdateDetails { /** Details of an issue update request. */ export interface IIssueUpdateDetails { /** List of issue screen fields to update, specifying the sub-field to update and its value for each field. This field provides a straightforward option when setting a sub-field. When multiple sub-fields or other operations are required, use `update`. Fields included in here cannot be included in `update`. */ - fields?: { [key: string]: any; }; + fields?: any; /** Additional issue history details. */ historyMetadata?: HistoryMetadata; /** Details of issue properties to be add or update. */ @@ -63952,7 +63892,7 @@ export interface IJiraRichTextField { } export class JiraRichTextInput implements IJiraRichTextInput { - adfValue?: { [key: string]: any; }; + adfValue?: any; constructor(data?: IJiraRichTextInput) { if (data) { @@ -63965,13 +63905,7 @@ export class JiraRichTextInput implements IJiraRichTextInput { init(_data?: any) { if (_data) { - if (_data["adfValue"]) { - this.adfValue = {} as any; - for (let key in _data["adfValue"]) { - if (_data["adfValue"].hasOwnProperty(key)) - (this.adfValue as any)![key] = _data["adfValue"][key]; - } - } + this.adfValue = _data["adfValue"]; } } @@ -63984,19 +63918,13 @@ export class JiraRichTextInput implements IJiraRichTextInput { toJSON(data?: any) { data = typeof data === 'object' ? data : {}; - if (this.adfValue) { - data["adfValue"] = {}; - for (let key in this.adfValue) { - if (this.adfValue.hasOwnProperty(key)) - (data["adfValue"] as any)[key] = (this.adfValue as any)[key]; - } - } + data["adfValue"] = this.adfValue; return data; } } export interface IJiraRichTextInput { - adfValue?: { [key: string]: any; }; + adfValue?: any; } export class JiraSelectedOptionField implements IJiraSelectedOptionField { diff --git a/src/NSwag.CodeGeneration.TypeScript.Tests/Snapshots/JIRA_OpenAPI_Axios.verified.txt b/src/NSwag.CodeGeneration.TypeScript.Tests/Snapshots/JIRA_OpenAPI_Axios.verified.txt index 183c6b56ff..2d88b4cc5c 100644 --- a/src/NSwag.CodeGeneration.TypeScript.Tests/Snapshots/JIRA_OpenAPI_Axios.verified.txt +++ b/src/NSwag.CodeGeneration.TypeScript.Tests/Snapshots/JIRA_OpenAPI_Axios.verified.txt @@ -59041,7 +59041,7 @@ export interface IDeprecatedWorkflow { export class DetailedErrorCollection implements IDetailedErrorCollection { /** Map of objects representing additional details for an error */ - details?: { [key: string]: any; }; + details?: any; /** The list of error messages produced by this operation. For example, "input parameter 'key' must be provided" */ errorMessages?: string[]; /** The list of errors by parameter returned by the operation. For example,"projectKey": "Project keys must start with an uppercase letter, followed by one or more uppercase alphanumeric characters." */ @@ -59058,13 +59058,7 @@ export class DetailedErrorCollection implements IDetailedErrorCollection { init(_data?: any) { if (_data) { - if (_data["details"]) { - this.details = {} as any; - for (let key in _data["details"]) { - if (_data["details"].hasOwnProperty(key)) - (this.details as any)![key] = _data["details"][key]; - } - } + this.details = _data["details"]; if (Array.isArray(_data["errorMessages"])) { this.errorMessages = [] as any; for (let item of _data["errorMessages"]) @@ -59089,13 +59083,7 @@ export class DetailedErrorCollection implements IDetailedErrorCollection { toJSON(data?: any) { data = typeof data === 'object' ? data : {}; - if (this.details) { - data["details"] = {}; - for (let key in this.details) { - if (this.details.hasOwnProperty(key)) - (data["details"] as any)[key] = (this.details as any)[key]; - } - } + data["details"] = this.details; if (Array.isArray(this.errorMessages)) { data["errorMessages"] = []; for (let item of this.errorMessages) @@ -59114,7 +59102,7 @@ export class DetailedErrorCollection implements IDetailedErrorCollection { export interface IDetailedErrorCollection { /** Map of objects representing additional details for an error */ - details?: { [key: string]: any; }; + details?: any; /** The list of error messages produced by this operation. For example, "input parameter 'key' must be provided" */ errorMessages?: string[]; /** The list of errors by parameter returned by the operation. For example,"projectKey": "Project keys must start with an uppercase letter, followed by one or more uppercase alphanumeric characters." */ @@ -64643,7 +64631,7 @@ export class IssueBean implements IIssueBean { readonly editmeta?: IssueUpdateMetadata; /** Expand options that include additional issue details in the response. */ readonly expand?: string; - fields?: { [key: string]: any; }; + fields?: any; fieldsToInclude?: IncludedFields; /** The ID of the issue. */ readonly id?: string; @@ -64680,13 +64668,7 @@ export class IssueBean implements IIssueBean { (this as any).changelog = _data["changelog"] ? PageOfChangelogs.fromJS(_data["changelog"]) : undefined as any; (this as any).editmeta = _data["editmeta"] ? IssueUpdateMetadata.fromJS(_data["editmeta"]) : undefined as any; (this as any).expand = _data["expand"]; - if (_data["fields"]) { - this.fields = {} as any; - for (let key in _data["fields"]) { - if (_data["fields"].hasOwnProperty(key)) - (this.fields as any)![key] = _data["fields"][key]; - } - } + this.fields = _data["fields"]; this.fieldsToInclude = _data["fieldsToInclude"] ? IncludedFields.fromJS(_data["fieldsToInclude"]) : undefined as any; (this as any).id = _data["id"]; (this as any).key = _data["key"]; @@ -64747,13 +64729,7 @@ export class IssueBean implements IIssueBean { data["changelog"] = this.changelog ? this.changelog.toJSON() : undefined as any; data["editmeta"] = this.editmeta ? this.editmeta.toJSON() : undefined as any; data["expand"] = this.expand; - if (this.fields) { - data["fields"] = {}; - for (let key in this.fields) { - if (this.fields.hasOwnProperty(key)) - (data["fields"] as any)[key] = (this.fields as any)[key]; - } - } + data["fields"] = this.fields; data["fieldsToInclude"] = this.fieldsToInclude ? this.fieldsToInclude.toJSON() : undefined as any; data["id"] = this.id; data["key"] = this.key; @@ -64811,7 +64787,7 @@ export interface IIssueBean { editmeta?: IssueUpdateMetadata; /** Expand options that include additional issue details in the response. */ expand?: string; - fields?: { [key: string]: any; }; + fields?: any; fieldsToInclude?: IncludedFields; /** The ID of the issue. */ id?: string; @@ -65873,7 +65849,7 @@ export class IssueFieldOption implements IIssueFieldOption { /** The unique identifier for the option. This is only unique within the select field's set of options. */ id!: number; /** The properties of the object, as arbitrary key-value pairs. These properties can be searched using JQL, if the extractions (see [Issue Field Option Property Index](https://developer.atlassian.com/cloud/jira/platform/modules/issue-field-option-property-index/)) are defined in the descriptor for the issue field module. */ - properties?: { [key: string]: any; }; + properties?: any; /** The option's name, which is displayed in Jira. */ value!: string; @@ -65890,13 +65866,7 @@ export class IssueFieldOption implements IIssueFieldOption { if (_data) { this.config = _data["config"] ? IssueFieldOptionConfiguration.fromJS(_data["config"]) : undefined as any; this.id = _data["id"]; - if (_data["properties"]) { - this.properties = {} as any; - for (let key in _data["properties"]) { - if (_data["properties"].hasOwnProperty(key)) - (this.properties as any)![key] = _data["properties"][key]; - } - } + this.properties = _data["properties"]; this.value = _data["value"]; } } @@ -65912,13 +65882,7 @@ export class IssueFieldOption implements IIssueFieldOption { data = typeof data === 'object' ? data : {}; data["config"] = this.config ? this.config.toJSON() : undefined as any; data["id"] = this.id; - if (this.properties) { - data["properties"] = {}; - for (let key in this.properties) { - if (this.properties.hasOwnProperty(key)) - (data["properties"] as any)[key] = (this.properties as any)[key]; - } - } + data["properties"] = this.properties; data["value"] = this.value; return data; } @@ -65930,7 +65894,7 @@ export interface IIssueFieldOption { /** The unique identifier for the option. This is only unique within the select field's set of options. */ id: number; /** The properties of the object, as arbitrary key-value pairs. These properties can be searched using JQL, if the extractions (see [Issue Field Option Property Index](https://developer.atlassian.com/cloud/jira/platform/modules/issue-field-option-property-index/)) are defined in the descriptor for the issue field module. */ - properties?: { [key: string]: any; }; + properties?: any; /** The option's name, which is displayed in Jira. */ value: string; } @@ -65992,7 +65956,7 @@ export interface IIssueFieldOptionConfiguration { export class IssueFieldOptionCreateBean implements IIssueFieldOptionCreateBean { config?: IssueFieldOptionConfiguration; /** The properties of the option as arbitrary key-value pairs. These properties can be searched using JQL, if the extractions (see https://developer.atlassian.com/cloud/jira/platform/modules/issue-field-option-property-index/) are defined in the descriptor for the issue field module. */ - properties?: { [key: string]: any; }; + properties?: any; /** The option's name, which is displayed in Jira. */ value!: string; @@ -66014,13 +65978,7 @@ export class IssueFieldOptionCreateBean implements IIssueFieldOptionCreateBean { this[property] = _data[property]; } this.config = _data["config"] ? IssueFieldOptionConfiguration.fromJS(_data["config"]) : undefined as any; - if (_data["properties"]) { - this.properties = {} as any; - for (let key in _data["properties"]) { - if (_data["properties"].hasOwnProperty(key)) - (this.properties as any)![key] = _data["properties"][key]; - } - } + this.properties = _data["properties"]; this.value = _data["value"]; } } @@ -66039,13 +65997,7 @@ export class IssueFieldOptionCreateBean implements IIssueFieldOptionCreateBean { data[property] = this[property]; } data["config"] = this.config ? this.config.toJSON() : undefined as any; - if (this.properties) { - data["properties"] = {}; - for (let key in this.properties) { - if (this.properties.hasOwnProperty(key)) - (data["properties"] as any)[key] = (this.properties as any)[key]; - } - } + data["properties"] = this.properties; data["value"] = this.value; return data; } @@ -66054,7 +66006,7 @@ export class IssueFieldOptionCreateBean implements IIssueFieldOptionCreateBean { export interface IIssueFieldOptionCreateBean { config?: IssueFieldOptionConfiguration; /** The properties of the option as arbitrary key-value pairs. These properties can be searched using JQL, if the extractions (see https://developer.atlassian.com/cloud/jira/platform/modules/issue-field-option-property-index/) are defined in the descriptor for the issue field module. */ - properties?: { [key: string]: any; }; + properties?: any; /** The option's name, which is displayed in Jira. */ value: string; @@ -69098,7 +69050,7 @@ export interface IIssueTypesWorkflowMapping { /** Details of an issue update request. */ export class IssueUpdateDetails implements IIssueUpdateDetails { /** List of issue screen fields to update, specifying the sub-field to update and its value for each field. This field provides a straightforward option when setting a sub-field. When multiple sub-fields or other operations are required, use `update`. Fields included in here cannot be included in `update`. */ - fields?: { [key: string]: any; }; + fields?: any; /** Additional issue history details. */ historyMetadata?: HistoryMetadata; /** Details of issue properties to be add or update. */ @@ -69125,13 +69077,7 @@ export class IssueUpdateDetails implements IIssueUpdateDetails { if (_data.hasOwnProperty(property)) this[property] = _data[property]; } - if (_data["fields"]) { - this.fields = {} as any; - for (let key in _data["fields"]) { - if (_data["fields"].hasOwnProperty(key)) - (this.fields as any)![key] = _data["fields"][key]; - } - } + this.fields = _data["fields"]; this.historyMetadata = _data["historyMetadata"] ? HistoryMetadata.fromJS(_data["historyMetadata"]) : undefined as any; if (Array.isArray(_data["properties"])) { this.properties = [] as any; @@ -69162,13 +69108,7 @@ export class IssueUpdateDetails implements IIssueUpdateDetails { if (this.hasOwnProperty(property)) data[property] = this[property]; } - if (this.fields) { - data["fields"] = {}; - for (let key in this.fields) { - if (this.fields.hasOwnProperty(key)) - (data["fields"] as any)[key] = (this.fields as any)[key]; - } - } + data["fields"] = this.fields; data["historyMetadata"] = this.historyMetadata ? this.historyMetadata.toJSON() : undefined as any; if (Array.isArray(this.properties)) { data["properties"] = []; @@ -69190,7 +69130,7 @@ export class IssueUpdateDetails implements IIssueUpdateDetails { /** Details of an issue update request. */ export interface IIssueUpdateDetails { /** List of issue screen fields to update, specifying the sub-field to update and its value for each field. This field provides a straightforward option when setting a sub-field. When multiple sub-fields or other operations are required, use `update`. Fields included in here cannot be included in `update`. */ - fields?: { [key: string]: any; }; + fields?: any; /** Additional issue history details. */ historyMetadata?: HistoryMetadata; /** Details of issue properties to be add or update. */ @@ -72222,7 +72162,7 @@ export interface IJiraRichTextField { } export class JiraRichTextInput implements IJiraRichTextInput { - adfValue?: { [key: string]: any; }; + adfValue?: any; constructor(data?: IJiraRichTextInput) { if (data) { @@ -72235,13 +72175,7 @@ export class JiraRichTextInput implements IJiraRichTextInput { init(_data?: any) { if (_data) { - if (_data["adfValue"]) { - this.adfValue = {} as any; - for (let key in _data["adfValue"]) { - if (_data["adfValue"].hasOwnProperty(key)) - (this.adfValue as any)![key] = _data["adfValue"][key]; - } - } + this.adfValue = _data["adfValue"]; } } @@ -72254,19 +72188,13 @@ export class JiraRichTextInput implements IJiraRichTextInput { toJSON(data?: any) { data = typeof data === 'object' ? data : {}; - if (this.adfValue) { - data["adfValue"] = {}; - for (let key in this.adfValue) { - if (this.adfValue.hasOwnProperty(key)) - (data["adfValue"] as any)[key] = (this.adfValue as any)[key]; - } - } + data["adfValue"] = this.adfValue; return data; } } export interface IJiraRichTextInput { - adfValue?: { [key: string]: any; }; + adfValue?: any; } export class JiraSelectedOptionField implements IJiraSelectedOptionField { diff --git a/src/NSwag.CodeGeneration.TypeScript.Tests/Snapshots/JIRA_OpenAPI_Fetch.verified.txt b/src/NSwag.CodeGeneration.TypeScript.Tests/Snapshots/JIRA_OpenAPI_Fetch.verified.txt index 84df8e8865..e04e1266d7 100644 --- a/src/NSwag.CodeGeneration.TypeScript.Tests/Snapshots/JIRA_OpenAPI_Fetch.verified.txt +++ b/src/NSwag.CodeGeneration.TypeScript.Tests/Snapshots/JIRA_OpenAPI_Fetch.verified.txt @@ -50756,7 +50756,7 @@ export interface IDeprecatedWorkflow { export class DetailedErrorCollection implements IDetailedErrorCollection { /** Map of objects representing additional details for an error */ - details?: { [key: string]: any; }; + details?: any; /** The list of error messages produced by this operation. For example, "input parameter 'key' must be provided" */ errorMessages?: string[]; /** The list of errors by parameter returned by the operation. For example,"projectKey": "Project keys must start with an uppercase letter, followed by one or more uppercase alphanumeric characters." */ @@ -50773,13 +50773,7 @@ export class DetailedErrorCollection implements IDetailedErrorCollection { init(_data?: any) { if (_data) { - if (_data["details"]) { - this.details = {} as any; - for (let key in _data["details"]) { - if (_data["details"].hasOwnProperty(key)) - (this.details as any)![key] = _data["details"][key]; - } - } + this.details = _data["details"]; if (Array.isArray(_data["errorMessages"])) { this.errorMessages = [] as any; for (let item of _data["errorMessages"]) @@ -50804,13 +50798,7 @@ export class DetailedErrorCollection implements IDetailedErrorCollection { toJSON(data?: any) { data = typeof data === 'object' ? data : {}; - if (this.details) { - data["details"] = {}; - for (let key in this.details) { - if (this.details.hasOwnProperty(key)) - (data["details"] as any)[key] = (this.details as any)[key]; - } - } + data["details"] = this.details; if (Array.isArray(this.errorMessages)) { data["errorMessages"] = []; for (let item of this.errorMessages) @@ -50829,7 +50817,7 @@ export class DetailedErrorCollection implements IDetailedErrorCollection { export interface IDetailedErrorCollection { /** Map of objects representing additional details for an error */ - details?: { [key: string]: any; }; + details?: any; /** The list of error messages produced by this operation. For example, "input parameter 'key' must be provided" */ errorMessages?: string[]; /** The list of errors by parameter returned by the operation. For example,"projectKey": "Project keys must start with an uppercase letter, followed by one or more uppercase alphanumeric characters." */ @@ -56358,7 +56346,7 @@ export class IssueBean implements IIssueBean { readonly editmeta?: IssueUpdateMetadata; /** Expand options that include additional issue details in the response. */ readonly expand?: string; - fields?: { [key: string]: any; }; + fields?: any; fieldsToInclude?: IncludedFields; /** The ID of the issue. */ readonly id?: string; @@ -56395,13 +56383,7 @@ export class IssueBean implements IIssueBean { (this as any).changelog = _data["changelog"] ? PageOfChangelogs.fromJS(_data["changelog"]) : undefined as any; (this as any).editmeta = _data["editmeta"] ? IssueUpdateMetadata.fromJS(_data["editmeta"]) : undefined as any; (this as any).expand = _data["expand"]; - if (_data["fields"]) { - this.fields = {} as any; - for (let key in _data["fields"]) { - if (_data["fields"].hasOwnProperty(key)) - (this.fields as any)![key] = _data["fields"][key]; - } - } + this.fields = _data["fields"]; this.fieldsToInclude = _data["fieldsToInclude"] ? IncludedFields.fromJS(_data["fieldsToInclude"]) : undefined as any; (this as any).id = _data["id"]; (this as any).key = _data["key"]; @@ -56462,13 +56444,7 @@ export class IssueBean implements IIssueBean { data["changelog"] = this.changelog ? this.changelog.toJSON() : undefined as any; data["editmeta"] = this.editmeta ? this.editmeta.toJSON() : undefined as any; data["expand"] = this.expand; - if (this.fields) { - data["fields"] = {}; - for (let key in this.fields) { - if (this.fields.hasOwnProperty(key)) - (data["fields"] as any)[key] = (this.fields as any)[key]; - } - } + data["fields"] = this.fields; data["fieldsToInclude"] = this.fieldsToInclude ? this.fieldsToInclude.toJSON() : undefined as any; data["id"] = this.id; data["key"] = this.key; @@ -56526,7 +56502,7 @@ export interface IIssueBean { editmeta?: IssueUpdateMetadata; /** Expand options that include additional issue details in the response. */ expand?: string; - fields?: { [key: string]: any; }; + fields?: any; fieldsToInclude?: IncludedFields; /** The ID of the issue. */ id?: string; @@ -57588,7 +57564,7 @@ export class IssueFieldOption implements IIssueFieldOption { /** The unique identifier for the option. This is only unique within the select field's set of options. */ id!: number; /** The properties of the object, as arbitrary key-value pairs. These properties can be searched using JQL, if the extractions (see [Issue Field Option Property Index](https://developer.atlassian.com/cloud/jira/platform/modules/issue-field-option-property-index/)) are defined in the descriptor for the issue field module. */ - properties?: { [key: string]: any; }; + properties?: any; /** The option's name, which is displayed in Jira. */ value!: string; @@ -57605,13 +57581,7 @@ export class IssueFieldOption implements IIssueFieldOption { if (_data) { this.config = _data["config"] ? IssueFieldOptionConfiguration.fromJS(_data["config"]) : undefined as any; this.id = _data["id"]; - if (_data["properties"]) { - this.properties = {} as any; - for (let key in _data["properties"]) { - if (_data["properties"].hasOwnProperty(key)) - (this.properties as any)![key] = _data["properties"][key]; - } - } + this.properties = _data["properties"]; this.value = _data["value"]; } } @@ -57627,13 +57597,7 @@ export class IssueFieldOption implements IIssueFieldOption { data = typeof data === 'object' ? data : {}; data["config"] = this.config ? this.config.toJSON() : undefined as any; data["id"] = this.id; - if (this.properties) { - data["properties"] = {}; - for (let key in this.properties) { - if (this.properties.hasOwnProperty(key)) - (data["properties"] as any)[key] = (this.properties as any)[key]; - } - } + data["properties"] = this.properties; data["value"] = this.value; return data; } @@ -57645,7 +57609,7 @@ export interface IIssueFieldOption { /** The unique identifier for the option. This is only unique within the select field's set of options. */ id: number; /** The properties of the object, as arbitrary key-value pairs. These properties can be searched using JQL, if the extractions (see [Issue Field Option Property Index](https://developer.atlassian.com/cloud/jira/platform/modules/issue-field-option-property-index/)) are defined in the descriptor for the issue field module. */ - properties?: { [key: string]: any; }; + properties?: any; /** The option's name, which is displayed in Jira. */ value: string; } @@ -57707,7 +57671,7 @@ export interface IIssueFieldOptionConfiguration { export class IssueFieldOptionCreateBean implements IIssueFieldOptionCreateBean { config?: IssueFieldOptionConfiguration; /** The properties of the option as arbitrary key-value pairs. These properties can be searched using JQL, if the extractions (see https://developer.atlassian.com/cloud/jira/platform/modules/issue-field-option-property-index/) are defined in the descriptor for the issue field module. */ - properties?: { [key: string]: any; }; + properties?: any; /** The option's name, which is displayed in Jira. */ value!: string; @@ -57729,13 +57693,7 @@ export class IssueFieldOptionCreateBean implements IIssueFieldOptionCreateBean { this[property] = _data[property]; } this.config = _data["config"] ? IssueFieldOptionConfiguration.fromJS(_data["config"]) : undefined as any; - if (_data["properties"]) { - this.properties = {} as any; - for (let key in _data["properties"]) { - if (_data["properties"].hasOwnProperty(key)) - (this.properties as any)![key] = _data["properties"][key]; - } - } + this.properties = _data["properties"]; this.value = _data["value"]; } } @@ -57754,13 +57712,7 @@ export class IssueFieldOptionCreateBean implements IIssueFieldOptionCreateBean { data[property] = this[property]; } data["config"] = this.config ? this.config.toJSON() : undefined as any; - if (this.properties) { - data["properties"] = {}; - for (let key in this.properties) { - if (this.properties.hasOwnProperty(key)) - (data["properties"] as any)[key] = (this.properties as any)[key]; - } - } + data["properties"] = this.properties; data["value"] = this.value; return data; } @@ -57769,7 +57721,7 @@ export class IssueFieldOptionCreateBean implements IIssueFieldOptionCreateBean { export interface IIssueFieldOptionCreateBean { config?: IssueFieldOptionConfiguration; /** The properties of the option as arbitrary key-value pairs. These properties can be searched using JQL, if the extractions (see https://developer.atlassian.com/cloud/jira/platform/modules/issue-field-option-property-index/) are defined in the descriptor for the issue field module. */ - properties?: { [key: string]: any; }; + properties?: any; /** The option's name, which is displayed in Jira. */ value: string; @@ -60813,7 +60765,7 @@ export interface IIssueTypesWorkflowMapping { /** Details of an issue update request. */ export class IssueUpdateDetails implements IIssueUpdateDetails { /** List of issue screen fields to update, specifying the sub-field to update and its value for each field. This field provides a straightforward option when setting a sub-field. When multiple sub-fields or other operations are required, use `update`. Fields included in here cannot be included in `update`. */ - fields?: { [key: string]: any; }; + fields?: any; /** Additional issue history details. */ historyMetadata?: HistoryMetadata; /** Details of issue properties to be add or update. */ @@ -60840,13 +60792,7 @@ export class IssueUpdateDetails implements IIssueUpdateDetails { if (_data.hasOwnProperty(property)) this[property] = _data[property]; } - if (_data["fields"]) { - this.fields = {} as any; - for (let key in _data["fields"]) { - if (_data["fields"].hasOwnProperty(key)) - (this.fields as any)![key] = _data["fields"][key]; - } - } + this.fields = _data["fields"]; this.historyMetadata = _data["historyMetadata"] ? HistoryMetadata.fromJS(_data["historyMetadata"]) : undefined as any; if (Array.isArray(_data["properties"])) { this.properties = [] as any; @@ -60877,13 +60823,7 @@ export class IssueUpdateDetails implements IIssueUpdateDetails { if (this.hasOwnProperty(property)) data[property] = this[property]; } - if (this.fields) { - data["fields"] = {}; - for (let key in this.fields) { - if (this.fields.hasOwnProperty(key)) - (data["fields"] as any)[key] = (this.fields as any)[key]; - } - } + data["fields"] = this.fields; data["historyMetadata"] = this.historyMetadata ? this.historyMetadata.toJSON() : undefined as any; if (Array.isArray(this.properties)) { data["properties"] = []; @@ -60905,7 +60845,7 @@ export class IssueUpdateDetails implements IIssueUpdateDetails { /** Details of an issue update request. */ export interface IIssueUpdateDetails { /** List of issue screen fields to update, specifying the sub-field to update and its value for each field. This field provides a straightforward option when setting a sub-field. When multiple sub-fields or other operations are required, use `update`. Fields included in here cannot be included in `update`. */ - fields?: { [key: string]: any; }; + fields?: any; /** Additional issue history details. */ historyMetadata?: HistoryMetadata; /** Details of issue properties to be add or update. */ @@ -63937,7 +63877,7 @@ export interface IJiraRichTextField { } export class JiraRichTextInput implements IJiraRichTextInput { - adfValue?: { [key: string]: any; }; + adfValue?: any; constructor(data?: IJiraRichTextInput) { if (data) { @@ -63950,13 +63890,7 @@ export class JiraRichTextInput implements IJiraRichTextInput { init(_data?: any) { if (_data) { - if (_data["adfValue"]) { - this.adfValue = {} as any; - for (let key in _data["adfValue"]) { - if (_data["adfValue"].hasOwnProperty(key)) - (this.adfValue as any)![key] = _data["adfValue"][key]; - } - } + this.adfValue = _data["adfValue"]; } } @@ -63969,19 +63903,13 @@ export class JiraRichTextInput implements IJiraRichTextInput { toJSON(data?: any) { data = typeof data === 'object' ? data : {}; - if (this.adfValue) { - data["adfValue"] = {}; - for (let key in this.adfValue) { - if (this.adfValue.hasOwnProperty(key)) - (data["adfValue"] as any)[key] = (this.adfValue as any)[key]; - } - } + data["adfValue"] = this.adfValue; return data; } } export interface IJiraRichTextInput { - adfValue?: { [key: string]: any; }; + adfValue?: any; } export class JiraSelectedOptionField implements IJiraSelectedOptionField { diff --git a/src/NSwag.CodeGeneration.TypeScript.Tests/Snapshots/JIRA_OpenAPI_JQueryCallbacks.verified.txt b/src/NSwag.CodeGeneration.TypeScript.Tests/Snapshots/JIRA_OpenAPI_JQueryCallbacks.verified.txt index c309b92704..7a63ea963f 100644 --- a/src/NSwag.CodeGeneration.TypeScript.Tests/Snapshots/JIRA_OpenAPI_JQueryCallbacks.verified.txt +++ b/src/NSwag.CodeGeneration.TypeScript.Tests/Snapshots/JIRA_OpenAPI_JQueryCallbacks.verified.txt @@ -61351,7 +61351,7 @@ export interface IDeprecatedWorkflow { export class DetailedErrorCollection implements IDetailedErrorCollection { /** Map of objects representing additional details for an error */ - details?: { [key: string]: any; }; + details?: any; /** The list of error messages produced by this operation. For example, "input parameter 'key' must be provided" */ errorMessages?: string[]; /** The list of errors by parameter returned by the operation. For example,"projectKey": "Project keys must start with an uppercase letter, followed by one or more uppercase alphanumeric characters." */ @@ -61368,13 +61368,7 @@ export class DetailedErrorCollection implements IDetailedErrorCollection { init(_data?: any) { if (_data) { - if (_data["details"]) { - this.details = {} as any; - for (let key in _data["details"]) { - if (_data["details"].hasOwnProperty(key)) - (this.details as any)![key] = _data["details"][key]; - } - } + this.details = _data["details"]; if (Array.isArray(_data["errorMessages"])) { this.errorMessages = [] as any; for (let item of _data["errorMessages"]) @@ -61399,13 +61393,7 @@ export class DetailedErrorCollection implements IDetailedErrorCollection { toJSON(data?: any) { data = typeof data === 'object' ? data : {}; - if (this.details) { - data["details"] = {}; - for (let key in this.details) { - if (this.details.hasOwnProperty(key)) - (data["details"] as any)[key] = (this.details as any)[key]; - } - } + data["details"] = this.details; if (Array.isArray(this.errorMessages)) { data["errorMessages"] = []; for (let item of this.errorMessages) @@ -61424,7 +61412,7 @@ export class DetailedErrorCollection implements IDetailedErrorCollection { export interface IDetailedErrorCollection { /** Map of objects representing additional details for an error */ - details?: { [key: string]: any; }; + details?: any; /** The list of error messages produced by this operation. For example, "input parameter 'key' must be provided" */ errorMessages?: string[]; /** The list of errors by parameter returned by the operation. For example,"projectKey": "Project keys must start with an uppercase letter, followed by one or more uppercase alphanumeric characters." */ @@ -66953,7 +66941,7 @@ export class IssueBean implements IIssueBean { readonly editmeta?: IssueUpdateMetadata; /** Expand options that include additional issue details in the response. */ readonly expand?: string; - fields?: { [key: string]: any; }; + fields?: any; fieldsToInclude?: IncludedFields; /** The ID of the issue. */ readonly id?: string; @@ -66990,13 +66978,7 @@ export class IssueBean implements IIssueBean { (this as any).changelog = _data["changelog"] ? PageOfChangelogs.fromJS(_data["changelog"]) : undefined as any; (this as any).editmeta = _data["editmeta"] ? IssueUpdateMetadata.fromJS(_data["editmeta"]) : undefined as any; (this as any).expand = _data["expand"]; - if (_data["fields"]) { - this.fields = {} as any; - for (let key in _data["fields"]) { - if (_data["fields"].hasOwnProperty(key)) - (this.fields as any)![key] = _data["fields"][key]; - } - } + this.fields = _data["fields"]; this.fieldsToInclude = _data["fieldsToInclude"] ? IncludedFields.fromJS(_data["fieldsToInclude"]) : undefined as any; (this as any).id = _data["id"]; (this as any).key = _data["key"]; @@ -67057,13 +67039,7 @@ export class IssueBean implements IIssueBean { data["changelog"] = this.changelog ? this.changelog.toJSON() : undefined as any; data["editmeta"] = this.editmeta ? this.editmeta.toJSON() : undefined as any; data["expand"] = this.expand; - if (this.fields) { - data["fields"] = {}; - for (let key in this.fields) { - if (this.fields.hasOwnProperty(key)) - (data["fields"] as any)[key] = (this.fields as any)[key]; - } - } + data["fields"] = this.fields; data["fieldsToInclude"] = this.fieldsToInclude ? this.fieldsToInclude.toJSON() : undefined as any; data["id"] = this.id; data["key"] = this.key; @@ -67121,7 +67097,7 @@ export interface IIssueBean { editmeta?: IssueUpdateMetadata; /** Expand options that include additional issue details in the response. */ expand?: string; - fields?: { [key: string]: any; }; + fields?: any; fieldsToInclude?: IncludedFields; /** The ID of the issue. */ id?: string; @@ -68183,7 +68159,7 @@ export class IssueFieldOption implements IIssueFieldOption { /** The unique identifier for the option. This is only unique within the select field's set of options. */ id!: number; /** The properties of the object, as arbitrary key-value pairs. These properties can be searched using JQL, if the extractions (see [Issue Field Option Property Index](https://developer.atlassian.com/cloud/jira/platform/modules/issue-field-option-property-index/)) are defined in the descriptor for the issue field module. */ - properties?: { [key: string]: any; }; + properties?: any; /** The option's name, which is displayed in Jira. */ value!: string; @@ -68200,13 +68176,7 @@ export class IssueFieldOption implements IIssueFieldOption { if (_data) { this.config = _data["config"] ? IssueFieldOptionConfiguration.fromJS(_data["config"]) : undefined as any; this.id = _data["id"]; - if (_data["properties"]) { - this.properties = {} as any; - for (let key in _data["properties"]) { - if (_data["properties"].hasOwnProperty(key)) - (this.properties as any)![key] = _data["properties"][key]; - } - } + this.properties = _data["properties"]; this.value = _data["value"]; } } @@ -68222,13 +68192,7 @@ export class IssueFieldOption implements IIssueFieldOption { data = typeof data === 'object' ? data : {}; data["config"] = this.config ? this.config.toJSON() : undefined as any; data["id"] = this.id; - if (this.properties) { - data["properties"] = {}; - for (let key in this.properties) { - if (this.properties.hasOwnProperty(key)) - (data["properties"] as any)[key] = (this.properties as any)[key]; - } - } + data["properties"] = this.properties; data["value"] = this.value; return data; } @@ -68240,7 +68204,7 @@ export interface IIssueFieldOption { /** The unique identifier for the option. This is only unique within the select field's set of options. */ id: number; /** The properties of the object, as arbitrary key-value pairs. These properties can be searched using JQL, if the extractions (see [Issue Field Option Property Index](https://developer.atlassian.com/cloud/jira/platform/modules/issue-field-option-property-index/)) are defined in the descriptor for the issue field module. */ - properties?: { [key: string]: any; }; + properties?: any; /** The option's name, which is displayed in Jira. */ value: string; } @@ -68302,7 +68266,7 @@ export interface IIssueFieldOptionConfiguration { export class IssueFieldOptionCreateBean implements IIssueFieldOptionCreateBean { config?: IssueFieldOptionConfiguration; /** The properties of the option as arbitrary key-value pairs. These properties can be searched using JQL, if the extractions (see https://developer.atlassian.com/cloud/jira/platform/modules/issue-field-option-property-index/) are defined in the descriptor for the issue field module. */ - properties?: { [key: string]: any; }; + properties?: any; /** The option's name, which is displayed in Jira. */ value!: string; @@ -68324,13 +68288,7 @@ export class IssueFieldOptionCreateBean implements IIssueFieldOptionCreateBean { this[property] = _data[property]; } this.config = _data["config"] ? IssueFieldOptionConfiguration.fromJS(_data["config"]) : undefined as any; - if (_data["properties"]) { - this.properties = {} as any; - for (let key in _data["properties"]) { - if (_data["properties"].hasOwnProperty(key)) - (this.properties as any)![key] = _data["properties"][key]; - } - } + this.properties = _data["properties"]; this.value = _data["value"]; } } @@ -68349,13 +68307,7 @@ export class IssueFieldOptionCreateBean implements IIssueFieldOptionCreateBean { data[property] = this[property]; } data["config"] = this.config ? this.config.toJSON() : undefined as any; - if (this.properties) { - data["properties"] = {}; - for (let key in this.properties) { - if (this.properties.hasOwnProperty(key)) - (data["properties"] as any)[key] = (this.properties as any)[key]; - } - } + data["properties"] = this.properties; data["value"] = this.value; return data; } @@ -68364,7 +68316,7 @@ export class IssueFieldOptionCreateBean implements IIssueFieldOptionCreateBean { export interface IIssueFieldOptionCreateBean { config?: IssueFieldOptionConfiguration; /** The properties of the option as arbitrary key-value pairs. These properties can be searched using JQL, if the extractions (see https://developer.atlassian.com/cloud/jira/platform/modules/issue-field-option-property-index/) are defined in the descriptor for the issue field module. */ - properties?: { [key: string]: any; }; + properties?: any; /** The option's name, which is displayed in Jira. */ value: string; @@ -71408,7 +71360,7 @@ export interface IIssueTypesWorkflowMapping { /** Details of an issue update request. */ export class IssueUpdateDetails implements IIssueUpdateDetails { /** List of issue screen fields to update, specifying the sub-field to update and its value for each field. This field provides a straightforward option when setting a sub-field. When multiple sub-fields or other operations are required, use `update`. Fields included in here cannot be included in `update`. */ - fields?: { [key: string]: any; }; + fields?: any; /** Additional issue history details. */ historyMetadata?: HistoryMetadata; /** Details of issue properties to be add or update. */ @@ -71435,13 +71387,7 @@ export class IssueUpdateDetails implements IIssueUpdateDetails { if (_data.hasOwnProperty(property)) this[property] = _data[property]; } - if (_data["fields"]) { - this.fields = {} as any; - for (let key in _data["fields"]) { - if (_data["fields"].hasOwnProperty(key)) - (this.fields as any)![key] = _data["fields"][key]; - } - } + this.fields = _data["fields"]; this.historyMetadata = _data["historyMetadata"] ? HistoryMetadata.fromJS(_data["historyMetadata"]) : undefined as any; if (Array.isArray(_data["properties"])) { this.properties = [] as any; @@ -71472,13 +71418,7 @@ export class IssueUpdateDetails implements IIssueUpdateDetails { if (this.hasOwnProperty(property)) data[property] = this[property]; } - if (this.fields) { - data["fields"] = {}; - for (let key in this.fields) { - if (this.fields.hasOwnProperty(key)) - (data["fields"] as any)[key] = (this.fields as any)[key]; - } - } + data["fields"] = this.fields; data["historyMetadata"] = this.historyMetadata ? this.historyMetadata.toJSON() : undefined as any; if (Array.isArray(this.properties)) { data["properties"] = []; @@ -71500,7 +71440,7 @@ export class IssueUpdateDetails implements IIssueUpdateDetails { /** Details of an issue update request. */ export interface IIssueUpdateDetails { /** List of issue screen fields to update, specifying the sub-field to update and its value for each field. This field provides a straightforward option when setting a sub-field. When multiple sub-fields or other operations are required, use `update`. Fields included in here cannot be included in `update`. */ - fields?: { [key: string]: any; }; + fields?: any; /** Additional issue history details. */ historyMetadata?: HistoryMetadata; /** Details of issue properties to be add or update. */ @@ -74532,7 +74472,7 @@ export interface IJiraRichTextField { } export class JiraRichTextInput implements IJiraRichTextInput { - adfValue?: { [key: string]: any; }; + adfValue?: any; constructor(data?: IJiraRichTextInput) { if (data) { @@ -74545,13 +74485,7 @@ export class JiraRichTextInput implements IJiraRichTextInput { init(_data?: any) { if (_data) { - if (_data["adfValue"]) { - this.adfValue = {} as any; - for (let key in _data["adfValue"]) { - if (_data["adfValue"].hasOwnProperty(key)) - (this.adfValue as any)![key] = _data["adfValue"][key]; - } - } + this.adfValue = _data["adfValue"]; } } @@ -74564,19 +74498,13 @@ export class JiraRichTextInput implements IJiraRichTextInput { toJSON(data?: any) { data = typeof data === 'object' ? data : {}; - if (this.adfValue) { - data["adfValue"] = {}; - for (let key in this.adfValue) { - if (this.adfValue.hasOwnProperty(key)) - (data["adfValue"] as any)[key] = (this.adfValue as any)[key]; - } - } + data["adfValue"] = this.adfValue; return data; } } export interface IJiraRichTextInput { - adfValue?: { [key: string]: any; }; + adfValue?: any; } export class JiraSelectedOptionField implements IJiraSelectedOptionField { diff --git a/src/NSwag.CodeGeneration.TypeScript.Tests/Snapshots/JIRA_OpenAPI_JQueryPromises.verified.txt b/src/NSwag.CodeGeneration.TypeScript.Tests/Snapshots/JIRA_OpenAPI_JQueryPromises.verified.txt index 8ade677afb..11da11a842 100644 --- a/src/NSwag.CodeGeneration.TypeScript.Tests/Snapshots/JIRA_OpenAPI_JQueryPromises.verified.txt +++ b/src/NSwag.CodeGeneration.TypeScript.Tests/Snapshots/JIRA_OpenAPI_JQueryPromises.verified.txt @@ -62529,7 +62529,7 @@ export interface IDeprecatedWorkflow { export class DetailedErrorCollection implements IDetailedErrorCollection { /** Map of objects representing additional details for an error */ - details?: { [key: string]: any; }; + details?: any; /** The list of error messages produced by this operation. For example, "input parameter 'key' must be provided" */ errorMessages?: string[]; /** The list of errors by parameter returned by the operation. For example,"projectKey": "Project keys must start with an uppercase letter, followed by one or more uppercase alphanumeric characters." */ @@ -62546,13 +62546,7 @@ export class DetailedErrorCollection implements IDetailedErrorCollection { init(_data?: any) { if (_data) { - if (_data["details"]) { - this.details = {} as any; - for (let key in _data["details"]) { - if (_data["details"].hasOwnProperty(key)) - (this.details as any)![key] = _data["details"][key]; - } - } + this.details = _data["details"]; if (Array.isArray(_data["errorMessages"])) { this.errorMessages = [] as any; for (let item of _data["errorMessages"]) @@ -62577,13 +62571,7 @@ export class DetailedErrorCollection implements IDetailedErrorCollection { toJSON(data?: any) { data = typeof data === 'object' ? data : {}; - if (this.details) { - data["details"] = {}; - for (let key in this.details) { - if (this.details.hasOwnProperty(key)) - (data["details"] as any)[key] = (this.details as any)[key]; - } - } + data["details"] = this.details; if (Array.isArray(this.errorMessages)) { data["errorMessages"] = []; for (let item of this.errorMessages) @@ -62602,7 +62590,7 @@ export class DetailedErrorCollection implements IDetailedErrorCollection { export interface IDetailedErrorCollection { /** Map of objects representing additional details for an error */ - details?: { [key: string]: any; }; + details?: any; /** The list of error messages produced by this operation. For example, "input parameter 'key' must be provided" */ errorMessages?: string[]; /** The list of errors by parameter returned by the operation. For example,"projectKey": "Project keys must start with an uppercase letter, followed by one or more uppercase alphanumeric characters." */ @@ -68131,7 +68119,7 @@ export class IssueBean implements IIssueBean { readonly editmeta?: IssueUpdateMetadata; /** Expand options that include additional issue details in the response. */ readonly expand?: string; - fields?: { [key: string]: any; }; + fields?: any; fieldsToInclude?: IncludedFields; /** The ID of the issue. */ readonly id?: string; @@ -68168,13 +68156,7 @@ export class IssueBean implements IIssueBean { (this as any).changelog = _data["changelog"] ? PageOfChangelogs.fromJS(_data["changelog"]) : undefined as any; (this as any).editmeta = _data["editmeta"] ? IssueUpdateMetadata.fromJS(_data["editmeta"]) : undefined as any; (this as any).expand = _data["expand"]; - if (_data["fields"]) { - this.fields = {} as any; - for (let key in _data["fields"]) { - if (_data["fields"].hasOwnProperty(key)) - (this.fields as any)![key] = _data["fields"][key]; - } - } + this.fields = _data["fields"]; this.fieldsToInclude = _data["fieldsToInclude"] ? IncludedFields.fromJS(_data["fieldsToInclude"]) : undefined as any; (this as any).id = _data["id"]; (this as any).key = _data["key"]; @@ -68235,13 +68217,7 @@ export class IssueBean implements IIssueBean { data["changelog"] = this.changelog ? this.changelog.toJSON() : undefined as any; data["editmeta"] = this.editmeta ? this.editmeta.toJSON() : undefined as any; data["expand"] = this.expand; - if (this.fields) { - data["fields"] = {}; - for (let key in this.fields) { - if (this.fields.hasOwnProperty(key)) - (data["fields"] as any)[key] = (this.fields as any)[key]; - } - } + data["fields"] = this.fields; data["fieldsToInclude"] = this.fieldsToInclude ? this.fieldsToInclude.toJSON() : undefined as any; data["id"] = this.id; data["key"] = this.key; @@ -68299,7 +68275,7 @@ export interface IIssueBean { editmeta?: IssueUpdateMetadata; /** Expand options that include additional issue details in the response. */ expand?: string; - fields?: { [key: string]: any; }; + fields?: any; fieldsToInclude?: IncludedFields; /** The ID of the issue. */ id?: string; @@ -69361,7 +69337,7 @@ export class IssueFieldOption implements IIssueFieldOption { /** The unique identifier for the option. This is only unique within the select field's set of options. */ id!: number; /** The properties of the object, as arbitrary key-value pairs. These properties can be searched using JQL, if the extractions (see [Issue Field Option Property Index](https://developer.atlassian.com/cloud/jira/platform/modules/issue-field-option-property-index/)) are defined in the descriptor for the issue field module. */ - properties?: { [key: string]: any; }; + properties?: any; /** The option's name, which is displayed in Jira. */ value!: string; @@ -69378,13 +69354,7 @@ export class IssueFieldOption implements IIssueFieldOption { if (_data) { this.config = _data["config"] ? IssueFieldOptionConfiguration.fromJS(_data["config"]) : undefined as any; this.id = _data["id"]; - if (_data["properties"]) { - this.properties = {} as any; - for (let key in _data["properties"]) { - if (_data["properties"].hasOwnProperty(key)) - (this.properties as any)![key] = _data["properties"][key]; - } - } + this.properties = _data["properties"]; this.value = _data["value"]; } } @@ -69400,13 +69370,7 @@ export class IssueFieldOption implements IIssueFieldOption { data = typeof data === 'object' ? data : {}; data["config"] = this.config ? this.config.toJSON() : undefined as any; data["id"] = this.id; - if (this.properties) { - data["properties"] = {}; - for (let key in this.properties) { - if (this.properties.hasOwnProperty(key)) - (data["properties"] as any)[key] = (this.properties as any)[key]; - } - } + data["properties"] = this.properties; data["value"] = this.value; return data; } @@ -69418,7 +69382,7 @@ export interface IIssueFieldOption { /** The unique identifier for the option. This is only unique within the select field's set of options. */ id: number; /** The properties of the object, as arbitrary key-value pairs. These properties can be searched using JQL, if the extractions (see [Issue Field Option Property Index](https://developer.atlassian.com/cloud/jira/platform/modules/issue-field-option-property-index/)) are defined in the descriptor for the issue field module. */ - properties?: { [key: string]: any; }; + properties?: any; /** The option's name, which is displayed in Jira. */ value: string; } @@ -69480,7 +69444,7 @@ export interface IIssueFieldOptionConfiguration { export class IssueFieldOptionCreateBean implements IIssueFieldOptionCreateBean { config?: IssueFieldOptionConfiguration; /** The properties of the option as arbitrary key-value pairs. These properties can be searched using JQL, if the extractions (see https://developer.atlassian.com/cloud/jira/platform/modules/issue-field-option-property-index/) are defined in the descriptor for the issue field module. */ - properties?: { [key: string]: any; }; + properties?: any; /** The option's name, which is displayed in Jira. */ value!: string; @@ -69502,13 +69466,7 @@ export class IssueFieldOptionCreateBean implements IIssueFieldOptionCreateBean { this[property] = _data[property]; } this.config = _data["config"] ? IssueFieldOptionConfiguration.fromJS(_data["config"]) : undefined as any; - if (_data["properties"]) { - this.properties = {} as any; - for (let key in _data["properties"]) { - if (_data["properties"].hasOwnProperty(key)) - (this.properties as any)![key] = _data["properties"][key]; - } - } + this.properties = _data["properties"]; this.value = _data["value"]; } } @@ -69527,13 +69485,7 @@ export class IssueFieldOptionCreateBean implements IIssueFieldOptionCreateBean { data[property] = this[property]; } data["config"] = this.config ? this.config.toJSON() : undefined as any; - if (this.properties) { - data["properties"] = {}; - for (let key in this.properties) { - if (this.properties.hasOwnProperty(key)) - (data["properties"] as any)[key] = (this.properties as any)[key]; - } - } + data["properties"] = this.properties; data["value"] = this.value; return data; } @@ -69542,7 +69494,7 @@ export class IssueFieldOptionCreateBean implements IIssueFieldOptionCreateBean { export interface IIssueFieldOptionCreateBean { config?: IssueFieldOptionConfiguration; /** The properties of the option as arbitrary key-value pairs. These properties can be searched using JQL, if the extractions (see https://developer.atlassian.com/cloud/jira/platform/modules/issue-field-option-property-index/) are defined in the descriptor for the issue field module. */ - properties?: { [key: string]: any; }; + properties?: any; /** The option's name, which is displayed in Jira. */ value: string; @@ -72586,7 +72538,7 @@ export interface IIssueTypesWorkflowMapping { /** Details of an issue update request. */ export class IssueUpdateDetails implements IIssueUpdateDetails { /** List of issue screen fields to update, specifying the sub-field to update and its value for each field. This field provides a straightforward option when setting a sub-field. When multiple sub-fields or other operations are required, use `update`. Fields included in here cannot be included in `update`. */ - fields?: { [key: string]: any; }; + fields?: any; /** Additional issue history details. */ historyMetadata?: HistoryMetadata; /** Details of issue properties to be add or update. */ @@ -72613,13 +72565,7 @@ export class IssueUpdateDetails implements IIssueUpdateDetails { if (_data.hasOwnProperty(property)) this[property] = _data[property]; } - if (_data["fields"]) { - this.fields = {} as any; - for (let key in _data["fields"]) { - if (_data["fields"].hasOwnProperty(key)) - (this.fields as any)![key] = _data["fields"][key]; - } - } + this.fields = _data["fields"]; this.historyMetadata = _data["historyMetadata"] ? HistoryMetadata.fromJS(_data["historyMetadata"]) : undefined as any; if (Array.isArray(_data["properties"])) { this.properties = [] as any; @@ -72650,13 +72596,7 @@ export class IssueUpdateDetails implements IIssueUpdateDetails { if (this.hasOwnProperty(property)) data[property] = this[property]; } - if (this.fields) { - data["fields"] = {}; - for (let key in this.fields) { - if (this.fields.hasOwnProperty(key)) - (data["fields"] as any)[key] = (this.fields as any)[key]; - } - } + data["fields"] = this.fields; data["historyMetadata"] = this.historyMetadata ? this.historyMetadata.toJSON() : undefined as any; if (Array.isArray(this.properties)) { data["properties"] = []; @@ -72678,7 +72618,7 @@ export class IssueUpdateDetails implements IIssueUpdateDetails { /** Details of an issue update request. */ export interface IIssueUpdateDetails { /** List of issue screen fields to update, specifying the sub-field to update and its value for each field. This field provides a straightforward option when setting a sub-field. When multiple sub-fields or other operations are required, use `update`. Fields included in here cannot be included in `update`. */ - fields?: { [key: string]: any; }; + fields?: any; /** Additional issue history details. */ historyMetadata?: HistoryMetadata; /** Details of issue properties to be add or update. */ @@ -75710,7 +75650,7 @@ export interface IJiraRichTextField { } export class JiraRichTextInput implements IJiraRichTextInput { - adfValue?: { [key: string]: any; }; + adfValue?: any; constructor(data?: IJiraRichTextInput) { if (data) { @@ -75723,13 +75663,7 @@ export class JiraRichTextInput implements IJiraRichTextInput { init(_data?: any) { if (_data) { - if (_data["adfValue"]) { - this.adfValue = {} as any; - for (let key in _data["adfValue"]) { - if (_data["adfValue"].hasOwnProperty(key)) - (this.adfValue as any)![key] = _data["adfValue"][key]; - } - } + this.adfValue = _data["adfValue"]; } } @@ -75742,19 +75676,13 @@ export class JiraRichTextInput implements IJiraRichTextInput { toJSON(data?: any) { data = typeof data === 'object' ? data : {}; - if (this.adfValue) { - data["adfValue"] = {}; - for (let key in this.adfValue) { - if (this.adfValue.hasOwnProperty(key)) - (data["adfValue"] as any)[key] = (this.adfValue as any)[key]; - } - } + data["adfValue"] = this.adfValue; return data; } } export interface IJiraRichTextInput { - adfValue?: { [key: string]: any; }; + adfValue?: any; } export class JiraSelectedOptionField implements IJiraSelectedOptionField { diff --git a/src/NSwag.CodeGeneration.TypeScript.Tests/TypeScriptDiscriminatorTests.cs b/src/NSwag.CodeGeneration.TypeScript.Tests/TypeScriptDiscriminatorTests.cs index 62b638fddc..d9f307ff51 100644 --- a/src/NSwag.CodeGeneration.TypeScript.Tests/TypeScriptDiscriminatorTests.cs +++ b/src/NSwag.CodeGeneration.TypeScript.Tests/TypeScriptDiscriminatorTests.cs @@ -1,18 +1,17 @@ using Microsoft.AspNetCore.Mvc; -using Newtonsoft.Json; using NJsonSchema.CodeGeneration.TypeScript; using NSwag.Generation.WebApi; using System.Runtime.Serialization; -using NJsonSchema.NewtonsoftJson.Converters; +using NJsonSchema.Converters; using NJsonSchema; -using NJsonSchema.NewtonsoftJson.Generation; +using NJsonSchema.Generation; using NSwag.CodeGeneration.Tests; namespace NSwag.CodeGeneration.TypeScript.Tests { public class TypeScriptDiscriminatorTests { - [JsonConverter(typeof(JsonInheritanceConverter), "type")] + [JsonInheritanceConverter(typeof(Base), "type")] [KnownType(typeof(OneChild))] [KnownType(typeof(SecondChild))] public abstract class Base @@ -76,7 +75,7 @@ public async Task When_parameter_is_abstract_then_generate_union() // Arrange var generator = new WebApiOpenApiDocumentGenerator(new WebApiOpenApiDocumentGeneratorSettings { - SchemaSettings = new NewtonsoftJsonSchemaGeneratorSettings { SchemaType = SchemaType.Swagger2 } + SchemaSettings = new SystemTextJsonSchemaGeneratorSettings { SchemaType = SchemaType.Swagger2 } }); var document = await generator.GenerateForControllerAsync(); diff --git a/src/NSwag.CodeGeneration.TypeScript.Tests/TypeScriptOperationParameterTests.cs b/src/NSwag.CodeGeneration.TypeScript.Tests/TypeScriptOperationParameterTests.cs index e4b971b645..7e4ea8d5bc 100644 --- a/src/NSwag.CodeGeneration.TypeScript.Tests/TypeScriptOperationParameterTests.cs +++ b/src/NSwag.CodeGeneration.TypeScript.Tests/TypeScriptOperationParameterTests.cs @@ -1,7 +1,7 @@ using Microsoft.AspNetCore.Mvc; using NJsonSchema; using NJsonSchema.CodeGeneration.TypeScript; -using NJsonSchema.NewtonsoftJson.Generation; +using NJsonSchema.Generation; using NSwag.CodeGeneration.Tests; using NSwag.Generation.WebApi; @@ -33,7 +33,7 @@ public async Task When_parameter_is_nullable_and_ts20_then_it_is_a_union_type_wi // Arrange var generator = new WebApiOpenApiDocumentGenerator(new WebApiOpenApiDocumentGeneratorSettings { - SchemaSettings = new NewtonsoftJsonSchemaGeneratorSettings { SchemaType = SchemaType.Swagger2 } + SchemaSettings = new SystemTextJsonSchemaGeneratorSettings { SchemaType = SchemaType.Swagger2 } }); var document = await generator.GenerateForControllerAsync(); @@ -62,7 +62,7 @@ public async Task When_parameter_is_nullable_and_ts20_then_it_is_not_included_in // Arrange var generator = new WebApiOpenApiDocumentGenerator(new WebApiOpenApiDocumentGeneratorSettings { - SchemaSettings = new NewtonsoftJsonSchemaGeneratorSettings { SchemaType = SchemaType.Swagger2 } + SchemaSettings = new SystemTextJsonSchemaGeneratorSettings { SchemaType = SchemaType.Swagger2 } }); var document = await generator.GenerateForControllerAsync(); @@ -91,7 +91,7 @@ public async Task When_parameter_is_nullable_optional_and_ts20_then_it_is_a_unio // Arrange var generator = new WebApiOpenApiDocumentGenerator(new WebApiOpenApiDocumentGeneratorSettings { - SchemaSettings = new NewtonsoftJsonSchemaGeneratorSettings { SchemaType = SchemaType.Swagger2 } + SchemaSettings = new SystemTextJsonSchemaGeneratorSettings { SchemaType = SchemaType.Swagger2 } }); var document = await generator.GenerateForControllerAsync(); @@ -120,7 +120,7 @@ public async Task When_parameter_is_nullable_optional_and_ts20_then_it_is_not_in // Arrange var generator = new WebApiOpenApiDocumentGenerator(new WebApiOpenApiDocumentGeneratorSettings { - SchemaSettings = new NewtonsoftJsonSchemaGeneratorSettings { SchemaType = SchemaType.Swagger2 } + SchemaSettings = new SystemTextJsonSchemaGeneratorSettings { SchemaType = SchemaType.Swagger2 } }); var document = await generator.GenerateForControllerAsync(); diff --git a/src/NSwag.CodeGeneration.TypeScript/NSwag.CodeGeneration.TypeScript.csproj b/src/NSwag.CodeGeneration.TypeScript/NSwag.CodeGeneration.TypeScript.csproj index 781babd9e6..2aa2d0bb88 100644 --- a/src/NSwag.CodeGeneration.TypeScript/NSwag.CodeGeneration.TypeScript.csproj +++ b/src/NSwag.CodeGeneration.TypeScript/NSwag.CodeGeneration.TypeScript.csproj @@ -12,9 +12,11 @@ + + diff --git a/src/NSwag.CodeGeneration.TypeScript/TypeScriptClientGeneratorSettings.cs b/src/NSwag.CodeGeneration.TypeScript/TypeScriptClientGeneratorSettings.cs index 120570d54e..51033a635f 100644 --- a/src/NSwag.CodeGeneration.TypeScript/TypeScriptClientGeneratorSettings.cs +++ b/src/NSwag.CodeGeneration.TypeScript/TypeScriptClientGeneratorSettings.cs @@ -6,7 +6,7 @@ // Rico Suter, mail@rsuter.com //----------------------------------------------------------------------- -using Newtonsoft.Json; +using System.Text.Json.Serialization; using NJsonSchema; using NJsonSchema.CodeGeneration; using NJsonSchema.CodeGeneration.TypeScript; diff --git a/src/NSwag.CodeGeneration/ClientGeneratorBase.cs b/src/NSwag.CodeGeneration/ClientGeneratorBase.cs index 84db85b189..6e7b5ba1f6 100644 --- a/src/NSwag.CodeGeneration/ClientGeneratorBase.cs +++ b/src/NSwag.CodeGeneration/ClientGeneratorBase.cs @@ -6,7 +6,7 @@ // Rico Suter, mail@rsuter.com //----------------------------------------------------------------------- -using Newtonsoft.Json.Linq; +using System.Text.Json.Nodes; using NJsonSchema; using NJsonSchema.CodeGeneration; using NSwag.CodeGeneration.Models; diff --git a/src/NSwag.CodeGeneration/NSwag.CodeGeneration.csproj b/src/NSwag.CodeGeneration/NSwag.CodeGeneration.csproj index 7ad9d27e59..1f43c4e8cb 100644 --- a/src/NSwag.CodeGeneration/NSwag.CodeGeneration.csproj +++ b/src/NSwag.CodeGeneration/NSwag.CodeGeneration.csproj @@ -4,9 +4,11 @@ true + + diff --git a/src/NSwag.Commands/CodeGeneratorCollection.cs b/src/NSwag.Commands/CodeGeneratorCollection.cs index 280c5db82c..75e5aff876 100644 --- a/src/NSwag.Commands/CodeGeneratorCollection.cs +++ b/src/NSwag.Commands/CodeGeneratorCollection.cs @@ -1,4 +1,4 @@ -using Newtonsoft.Json; +using System.Text.Json.Serialization; using NSwag.Commands.CodeGeneration; namespace NSwag.Commands @@ -9,15 +9,18 @@ public class CodeGeneratorCollection #pragma warning restore CA1711 { /// Gets or sets the SwaggerToTypeScriptClientCommand. - [JsonProperty("OpenApiToTypeScriptClient", NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("OpenApiToTypeScriptClient")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public OpenApiToTypeScriptClientCommand OpenApiToTypeScriptClientCommand { get; set; } /// Gets or sets the SwaggerToCSharpClientCommand. - [JsonProperty("OpenApiToCSharpClient", NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("OpenApiToCSharpClient")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public OpenApiToCSharpClientCommand OpenApiToCSharpClientCommand { get; set; } /// Gets or sets the SwaggerToCSharpControllerCommand. - [JsonProperty("OpenApiToCSharpController", NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("OpenApiToCSharpController")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public OpenApiToCSharpControllerCommand OpenApiToCSharpControllerCommand { get; set; } /// Gets the items. diff --git a/src/NSwag.Commands/Commands/CodeGeneration/CodeGeneratorCommandBase.cs b/src/NSwag.Commands/Commands/CodeGeneration/CodeGeneratorCommandBase.cs index f926caca4c..4545aab9ea 100644 --- a/src/NSwag.Commands/Commands/CodeGeneration/CodeGeneratorCommandBase.cs +++ b/src/NSwag.Commands/Commands/CodeGeneration/CodeGeneratorCommandBase.cs @@ -7,7 +7,7 @@ //----------------------------------------------------------------------- using NConsole; -using Newtonsoft.Json; +using System.Text.Json.Serialization; using NSwag.CodeGeneration; namespace NSwag.Commands.CodeGeneration diff --git a/src/NSwag.Commands/Commands/CodeGeneration/JsonSchemaToCSharpCommand.cs b/src/NSwag.Commands/Commands/CodeGeneration/JsonSchemaToCSharpCommand.cs index 4a2216065b..9cdf75f53a 100644 --- a/src/NSwag.Commands/Commands/CodeGeneration/JsonSchemaToCSharpCommand.cs +++ b/src/NSwag.Commands/Commands/CodeGeneration/JsonSchemaToCSharpCommand.cs @@ -7,7 +7,7 @@ //----------------------------------------------------------------------- using NConsole; -using Newtonsoft.Json; +using System.Text.Json.Serialization; using NJsonSchema.CodeGeneration.CSharp; #pragma warning disable 1591 diff --git a/src/NSwag.Commands/Commands/CodeGeneration/JsonSchemaToOpenApiCommand.cs b/src/NSwag.Commands/Commands/CodeGeneration/JsonSchemaToOpenApiCommand.cs index e51106d1cb..7d34a73c06 100644 --- a/src/NSwag.Commands/Commands/CodeGeneration/JsonSchemaToOpenApiCommand.cs +++ b/src/NSwag.Commands/Commands/CodeGeneration/JsonSchemaToOpenApiCommand.cs @@ -1,6 +1,6 @@ using System.Text.RegularExpressions; using NConsole; -using Newtonsoft.Json; +using System.Text.Json.Serialization; using NJsonSchema; #pragma warning disable 1591 @@ -9,10 +9,10 @@ namespace NSwag.Commands.CodeGeneration { public class JsonSchemaToOpenApiCommand : OutputCommandBase { - [JsonProperty("name")] + [JsonPropertyName("name")] public string Name { get; set; } - [JsonProperty("schema")] + [JsonPropertyName("schema")] public string Schema { get; set; } public override async Task RunAsync(CommandLineProcessor processor, IConsoleHost host) diff --git a/src/NSwag.Commands/Commands/Generation/AspNetCore/AspNetCoreToOpenApiCommand.cs b/src/NSwag.Commands/Commands/Generation/AspNetCore/AspNetCoreToOpenApiCommand.cs index 8f47fb722f..3774cafc03 100644 --- a/src/NSwag.Commands/Commands/Generation/AspNetCore/AspNetCoreToOpenApiCommand.cs +++ b/src/NSwag.Commands/Commands/Generation/AspNetCore/AspNetCoreToOpenApiCommand.cs @@ -11,7 +11,7 @@ using System.Reflection; using Microsoft.Extensions.DependencyInjection; using NConsole; -using Newtonsoft.Json; +using System.Text.Json; using NSwag.Generation; #if NETCOREAPP || NETSTANDARD @@ -175,7 +175,7 @@ public override async Task RunAsync(CommandLineProcessor processor, ICon var commandFile = Path.GetTempFileName(); var outputFile = Path.GetTempFileName(); - File.WriteAllText(commandFile, JsonConvert.SerializeObject(this)); + File.WriteAllText(commandFile, JsonSerializer.Serialize(this)); cleanupFiles.Add(commandFile); cleanupFiles.Add(outputFile); diff --git a/src/NSwag.Commands/Commands/Generation/AspNetCore/AspNetCoreToOpenApiGeneratorCommandEntryPoint.cs b/src/NSwag.Commands/Commands/Generation/AspNetCore/AspNetCoreToOpenApiGeneratorCommandEntryPoint.cs index 7ac88ccdb3..e5a19e045c 100644 --- a/src/NSwag.Commands/Commands/Generation/AspNetCore/AspNetCoreToOpenApiGeneratorCommandEntryPoint.cs +++ b/src/NSwag.Commands/Commands/Generation/AspNetCore/AspNetCoreToOpenApiGeneratorCommandEntryPoint.cs @@ -7,7 +7,7 @@ //----------------------------------------------------------------------- using System.Reflection; -using Newtonsoft.Json; +using System.Text.Json; #pragma warning disable CS0618 @@ -18,7 +18,7 @@ internal sealed class AspNetCoreToOpenApiGeneratorCommandEntryPoint { public static void Process(string commandContent, string outputFile, string applicationName) { - var command = JsonConvert.DeserializeObject(commandContent); + var command = JsonSerializer.Deserialize(commandContent); var previousWorkingDirectory = command.ChangeWorkingDirectoryAndSetAspNetCoreEnvironment(); var assemblyName = new AssemblyName(applicationName); diff --git a/src/NSwag.Commands/Commands/Generation/FromDocumentCommand.cs b/src/NSwag.Commands/Commands/Generation/FromDocumentCommand.cs index 2c40caf472..218a87ab31 100644 --- a/src/NSwag.Commands/Commands/Generation/FromDocumentCommand.cs +++ b/src/NSwag.Commands/Commands/Generation/FromDocumentCommand.cs @@ -9,7 +9,7 @@ using System.ComponentModel; using System.Runtime.CompilerServices; using NConsole; -using Newtonsoft.Json; +using System.Text.Json.Serialization; namespace NSwag.Commands.Generation { @@ -20,7 +20,8 @@ public class FromDocumentCommand : OutputCommandBase, INotifyPropertyChanged private string _url = "http://redocly.github.io/redoc/openapi.yaml"; /// Gets or sets the input Swagger specification. - [JsonProperty("json", NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("json")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string Json { get => _json; @@ -32,7 +33,8 @@ public string Json } /// Gets or sets the input Swagger specification URL. - [JsonProperty("url", NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("url")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string Url { get => _url; diff --git a/src/NSwag.Commands/Commands/InputOutputCommandBase.cs b/src/NSwag.Commands/Commands/InputOutputCommandBase.cs index 66d832f49b..4e95037ebc 100644 --- a/src/NSwag.Commands/Commands/InputOutputCommandBase.cs +++ b/src/NSwag.Commands/Commands/InputOutputCommandBase.cs @@ -7,7 +7,7 @@ //----------------------------------------------------------------------- using NConsole; -using Newtonsoft.Json; +using System.Text.Json.Serialization; using NJsonSchema; #pragma warning disable 1591 diff --git a/src/NSwag.Commands/Commands/OutputCommandBase.cs b/src/NSwag.Commands/Commands/OutputCommandBase.cs index 86e9b36b82..beadcf57fb 100644 --- a/src/NSwag.Commands/Commands/OutputCommandBase.cs +++ b/src/NSwag.Commands/Commands/OutputCommandBase.cs @@ -7,18 +7,18 @@ //----------------------------------------------------------------------- using NConsole; -using Newtonsoft.Json; +using System.Text.Json.Serialization; namespace NSwag.Commands { public abstract class OutputCommandBase : IOutputCommand { [Argument(Name = "Output", IsRequired = false, Description = "The output file path (optional).")] - [JsonProperty("output", NullValueHandling = NullValueHandling.Include)] + [JsonPropertyName("output")] public string OutputFilePath { get; set; } [Argument(Name = "NewLineBehavior", IsRequired = false, Description = "The new line behavior (Auto (OS default), CRLF, LF).")] - [JsonProperty("newLineBehavior", NullValueHandling = NullValueHandling.Include)] + [JsonPropertyName("newLineBehavior")] public NewLineBehavior NewLineBehavior { get; set; } = NewLineBehavior.Auto; public abstract Task RunAsync(CommandLineProcessor processor, IConsoleHost host); diff --git a/src/NSwag.Commands/NSwagDocumentBase.cs b/src/NSwag.Commands/NSwagDocumentBase.cs index 7606f64017..c88fe89991 100644 --- a/src/NSwag.Commands/NSwagDocumentBase.cs +++ b/src/NSwag.Commands/NSwagDocumentBase.cs @@ -12,10 +12,9 @@ using System.Reflection; using System.Runtime.CompilerServices; using System.Text.RegularExpressions; -using Newtonsoft.Json; -using Newtonsoft.Json.Converters; -using Newtonsoft.Json.Linq; -using Newtonsoft.Json.Serialization; +using System.Text.Json; +using System.Text.Json.Nodes; +using System.Text.Json.Serialization; using NSwag.Commands.CodeGeneration; using NSwag.Commands.Generation; @@ -59,8 +58,8 @@ protected NSwagDocumentBase() public string DefaultVariables { get; set; } /// Gets or sets the selected swagger generator JSON. - [JsonProperty("DocumentGenerator")] - internal JObject SelectedSwaggerGeneratorRaw + [JsonPropertyName("documentGenerator")] + internal JsonObject SelectedSwaggerGeneratorRaw { get { @@ -68,21 +67,21 @@ internal JObject SelectedSwaggerGeneratorRaw .Replace("CommandBase", string.Empty) .Replace("Command", string.Empty); - return JObject.FromObject(new Dictionary + return System.Text.Json.JsonSerializer.SerializeToNode(new Dictionary { { key[0].ToString().ToLowerInvariant() + key.Substring(1), SelectedSwaggerGenerator } - }, JsonSerializer.Create(GetSerializerSettings())); + }, GetSerializerOptions())?.AsObject(); } set { - var generatorProperty = value.Properties().First(); - var key = generatorProperty.Name + "Command"; + var generatorProperty = value.First(); + var key = generatorProperty.Key + "Command"; var collectionProperty = SwaggerGenerators.GetType().GetRuntimeProperty(key[0].ToString().ToUpperInvariant() + key.Substring(1)); var generator = collectionProperty.GetValue(SwaggerGenerators); - var newGenerator = (IOutputCommand)JsonConvert.DeserializeObject(generatorProperty.Value.ToString(), generator.GetType(), GetSerializerSettings()); + var newGenerator = (IOutputCommand)System.Text.Json.JsonSerializer.Deserialize(generatorProperty.Value.ToJsonString(), generator.GetType(), GetSerializerOptions()); collectionProperty.SetValue(SwaggerGenerators, newGenerator); SelectedSwaggerGenerator = newGenerator; } @@ -93,7 +92,7 @@ internal JObject SelectedSwaggerGeneratorRaw public OpenApiGeneratorCollection SwaggerGenerators { get; } = new OpenApiGeneratorCollection(); /// Gets the code generators. - [JsonProperty("CodeGenerators")] + [JsonPropertyName("codeGenerators")] public CodeGeneratorCollection CodeGenerators { get; } = new CodeGeneratorCollection(); /// Gets or sets the path. @@ -128,7 +127,7 @@ public string Name /// Gets a value indicating whether the document is dirty (has any changes). [JsonIgnore] - public bool IsDirty => _latestData != JsonConvert.SerializeObject(this, Formatting.Indented, GetSerializerSettings()); + public bool IsDirty => _latestData != System.Text.Json.JsonSerializer.Serialize(this, GetSerializerOptions()); /// Gets the selected Swagger generator. [JsonIgnore] @@ -150,7 +149,7 @@ protected static TDocument Create() { var document = new TDocument(); document.Path = "Untitled"; - document._latestData = JsonConvert.SerializeObject(document, Formatting.Indented, GetSerializerSettings()); + document._latestData = System.Text.Json.JsonSerializer.Serialize(document, GetSerializerOptions()); return document; } @@ -178,10 +177,10 @@ protected static async Task LoadAsync( data = data.Replace("$(" + p.Key + ")", EscapeJsonString(p.Value)); } - var obj = JObject.Parse(data); - if (obj["defaultVariables"] != null) + var obj = JsonNode.Parse(data)?.AsObject(); + if (obj != null && obj["defaultVariables"] != null) { - var defaultVariables = obj["defaultVariables"].Value(); + var defaultVariables = obj["defaultVariables"].GetValue(); foreach (var p in ConvertVariables(defaultVariables)) { data = data.Replace("$(" + p.Key + ")", EscapeJsonString(p.Value)); @@ -207,8 +206,8 @@ private static TDocument LoadDocument(string filePath, string data) public static TDocument FromJson(string filePath, string data) where TDocument : NSwagDocumentBase, new() { - var settings = GetSerializerSettings(); - var document = JsonConvert.DeserializeObject(data, settings); + var options = GetSerializerOptions(); + var document = System.Text.Json.JsonSerializer.Deserialize(data, options); if (filePath != null) { @@ -216,7 +215,7 @@ public static TDocument FromJson(string filePath, string data) document.ConvertToAbsolutePaths(); } - document._latestData = JsonConvert.SerializeObject(document, Formatting.Indented, GetSerializerSettings()); + document._latestData = System.Text.Json.JsonSerializer.Serialize(document, GetSerializerOptions()); return document; } @@ -226,7 +225,7 @@ public static TDocument FromJson(string filePath, string data) public Task SaveAsync() { File.WriteAllText(Path, ToJsonWithRelativePaths()); - _latestData = JsonConvert.SerializeObject(this, Formatting.Indented, GetSerializerSettings()); + _latestData = System.Text.Json.JsonSerializer.Serialize(this, GetSerializerOptions()); return Task.CompletedTask; } @@ -249,7 +248,7 @@ public string ToJsonWithRelativePaths() /// The JSON data. public string ToJson() { - return JsonConvert.SerializeObject(this, Formatting.Indented, GetSerializerSettings()); + return System.Text.Json.JsonSerializer.Serialize(this, GetSerializerOptions()); } /// Generates the with the currently selected generator. @@ -263,8 +262,8 @@ private static string EscapeJsonString(string value) { if (!string.IsNullOrEmpty(value)) { - value = JsonConvert.ToString(value); - return value.Substring(1, value.Length - 2); + var serialized = System.Text.Json.JsonSerializer.Serialize(value); + return serialized.Substring(1, serialized.Length - 2); } return string.Empty; @@ -285,19 +284,15 @@ private static Dictionary ConvertVariables(string variables) } } - private static JsonSerializerSettings GetSerializerSettings() + private static readonly JsonSerializerOptions SerializerOptions = new JsonSerializerOptions { - return new JsonSerializerSettings - { - DefaultValueHandling = DefaultValueHandling.Include, - NullValueHandling = NullValueHandling.Include, - ContractResolver = new CamelCasePropertyNamesContractResolver(), - Converters = - [ - new StringEnumConverter() - ] - }; - } + DefaultIgnoreCondition = JsonIgnoreCondition.Never, + PropertyNamingPolicy = JsonNamingPolicy.CamelCase, + Converters = { new JsonStringEnumConverter() }, + WriteIndented = true + }; + + private static JsonSerializerOptions GetSerializerOptions() => SerializerOptions; private void ConvertToAbsolutePaths() { @@ -520,56 +515,58 @@ private static string TransformLegacyDocument(string data, out bool saveFile) if (data.Contains("\"SelectedSwaggerGenerator\"")) { - var obj = JsonConvert.DeserializeObject(data); - var selectedSwaggerGenerator = obj["SelectedSwaggerGenerator"].Value(); + var obj = JsonNode.Parse(data)?.AsObject(); + if (obj == null) return data; + + var selectedSwaggerGenerator = obj["SelectedSwaggerGenerator"]?.GetValue() ?? 0; if (selectedSwaggerGenerator == 0) // swagger url/data { - obj["swaggerGenerator"] = new JObject + obj["swaggerGenerator"] = new JsonObject { { - "fromSwagger", new JObject + "fromSwagger", new JsonObject { - {"url", obj["InputSwaggerUrl"]}, - {"json", string.IsNullOrEmpty(obj["InputSwaggerUrl"].Value()) ? obj["InputSwagger"] : null} + {"url", obj["InputSwaggerUrl"]?.DeepClone()}, + {"json", string.IsNullOrEmpty(obj["InputSwaggerUrl"]?.GetValue()) ? obj["InputSwagger"]?.DeepClone() : null} } } }; } if (selectedSwaggerGenerator == 1) // web api { - obj["swaggerGenerator"] = new JObject + obj["swaggerGenerator"] = new JsonObject { - {"webApiToSwagger", obj["WebApiToSwaggerCommand"]} + {"webApiToSwagger", obj["WebApiToSwaggerCommand"]?.DeepClone()} }; } if (selectedSwaggerGenerator == 2) // json schema { - obj["swaggerGenerator"] = new JObject + obj["swaggerGenerator"] = new JsonObject { { - "jsonSchemaToSwagger", new JObject + "jsonSchemaToSwagger", new JsonObject { - {"schema", obj["InputJsonSchema"]}, + {"schema", obj["InputJsonSchema"]?.DeepClone()}, } } }; } if (selectedSwaggerGenerator == 3) // types { - obj["swaggerGenerator"] = new JObject + obj["swaggerGenerator"] = new JsonObject { - {"assemblyTypeToSwagger", obj["AssemblyTypeToSwaggerCommand"]} + {"assemblyTypeToSwagger", obj["AssemblyTypeToSwaggerCommand"]?.DeepClone()} }; } - obj["codeGenerators"] = new JObject + obj["codeGenerators"] = new JsonObject { - {"swaggerToTypeScriptClient", obj["SwaggerToTypeScriptCommand"]}, - {"swaggerToCSharpClient", obj["SwaggerToCSharpClientCommand"]}, - {"swaggerToCSharpController", obj["SwaggerToCSharpControllerGenerator"]} + {"swaggerToTypeScriptClient", obj["SwaggerToTypeScriptCommand"]?.DeepClone()}, + {"swaggerToCSharpClient", obj["SwaggerToCSharpClientCommand"]?.DeepClone()}, + {"swaggerToCSharpController", obj["SwaggerToCSharpControllerGenerator"]?.DeepClone()} }; - data = obj.ToString().Replace("\"OutputFilePath\"", "\"output\""); + data = obj.ToJsonString().Replace("\"OutputFilePath\"", "\"output\""); saveFile = true; } diff --git a/src/NSwag.Commands/OpenApiGeneratorCollection.cs b/src/NSwag.Commands/OpenApiGeneratorCollection.cs index 9b98efbae4..de2e173217 100644 --- a/src/NSwag.Commands/OpenApiGeneratorCollection.cs +++ b/src/NSwag.Commands/OpenApiGeneratorCollection.cs @@ -1,4 +1,4 @@ -using Newtonsoft.Json; +using System.Text.Json.Serialization; using NSwag.Commands.CodeGeneration; using NSwag.Commands.Generation; using NSwag.Commands.Generation.AspNetCore; diff --git a/src/NSwag.Core.Tests/NSwag.Core.Tests.csproj b/src/NSwag.Core.Tests/NSwag.Core.Tests.csproj index a4e3604afa..f5a8a283ec 100644 --- a/src/NSwag.Core.Tests/NSwag.Core.Tests.csproj +++ b/src/NSwag.Core.Tests/NSwag.Core.Tests.csproj @@ -11,6 +11,7 @@ + all diff --git a/src/NSwag.Core.Tests/Serialization/OpenApiDocumentSnapshotTests.cs b/src/NSwag.Core.Tests/Serialization/OpenApiDocumentSnapshotTests.cs new file mode 100644 index 0000000000..ac0b962467 --- /dev/null +++ b/src/NSwag.Core.Tests/Serialization/OpenApiDocumentSnapshotTests.cs @@ -0,0 +1,413 @@ +using NJsonSchema; + +namespace NSwag.Core.Tests.Serialization; + +public class OpenApiDocumentSnapshotTests +{ + [Fact] + public async Task Snapshot_MinimalDocument_Swagger2() + { + // Arrange + var document = new OpenApiDocument(); + document.Info.Title = "My API"; + document.Info.Version = "1.0.0"; + + // Act + var json = document.ToJson(SchemaType.Swagger2, true); + + // Assert + await Verify(json).UseDirectory("Snapshots"); + } + + [Fact] + public async Task Snapshot_MinimalDocument_OpenApi3() + { + // Arrange + var document = new OpenApiDocument(); + document.Info.Title = "My API"; + document.Info.Version = "1.0.0"; + + // Act + var json = document.ToJson(SchemaType.OpenApi3, true); + + // Assert + await Verify(json).UseDirectory("Snapshots"); + } + + [Fact] + public async Task Snapshot_FullDocument_OpenApi3() + { + // Arrange + var document = new OpenApiDocument(); + document.Info.Title = "Pet Store API"; + document.Info.Version = "2.0.0"; + document.Info.Description = "A sample API for managing pets"; + document.Info.TermsOfService = "https://example.com/terms"; + document.Info.Contact = new OpenApiContact + { + Name = "API Support", + Email = "support@example.com", + Url = "https://example.com/support" + }; + document.Info.License = new OpenApiLicense + { + Name = "MIT", + Url = "https://opensource.org/licenses/MIT" + }; + + document.Servers.Add(new OpenApiServer + { + Url = "https://api.example.com/v2", + Description = "Production server" + }); + document.Servers.Add(new OpenApiServer + { + Url = "https://staging.example.com/v2", + Description = "Staging server" + }); + + var schema = new JsonSchema + { + Type = JsonObjectType.Object, + }; + schema.Properties["id"] = new JsonSchemaProperty + { + Type = JsonObjectType.Integer, + Description = "The pet ID" + }; + schema.Properties["name"] = new JsonSchemaProperty + { + Type = JsonObjectType.String, + Description = "The pet name" + }; + + document.Definitions["Pet"] = schema; + + var getOperation = new OpenApiOperation + { + Summary = "List all pets", + OperationId = "listPets", + Description = "Returns all pets from the system" + }; + getOperation.Tags.Add("pets"); + getOperation.Parameters.Add(new OpenApiParameter + { + Name = "limit", + Kind = OpenApiParameterKind.Query, + Description = "Maximum number of pets to return", + IsRequired = false, + Schema = new JsonSchema { Type = JsonObjectType.Integer } + }); + getOperation.Responses.Add("200", new OpenApiResponse + { + Description = "A list of pets" + }); + + var postOperation = new OpenApiOperation + { + Summary = "Create a pet", + OperationId = "createPet", + Description = "Creates a new pet in the store", + RequestBody = new OpenApiRequestBody + { + Name = "body", + Description = "Pet to add to the store", + IsRequired = true, + Content = + { + { + "application/json", + new OpenApiMediaType + { + Schema = new JsonSchema { Reference = schema } + } + } + } + } + }; + postOperation.Tags.Add("pets"); + postOperation.Responses.Add("201", new OpenApiResponse + { + Description = "Pet created" + }); + + var pathItem = new OpenApiPathItem + { + { OpenApiOperationMethod.Get, getOperation }, + { OpenApiOperationMethod.Post, postOperation } + }; + document.Paths["/pets"] = pathItem; + + // Act + var json = document.ToJson(SchemaType.OpenApi3, true); + + // Assert + await Verify(json).UseDirectory("Snapshots"); + } + + [Fact] + public async Task Snapshot_DocumentWithSecurity_OpenApi3() + { + // Arrange + var document = new OpenApiDocument(); + document.Info.Title = "Secured API"; + document.Info.Version = "1.0.0"; + + document.SecurityDefinitions.Add("bearerAuth", new OpenApiSecurityScheme + { + Type = OpenApiSecuritySchemeType.Http, + Scheme = "bearer", + BearerFormat = "JWT", + Description = "JWT Bearer token authentication" + }); + + document.SecurityDefinitions.Add("apiKey", new OpenApiSecurityScheme + { + Type = OpenApiSecuritySchemeType.ApiKey, + Name = "X-API-Key", + In = OpenApiSecurityApiKeyLocation.Header, + Description = "API key authentication" + }); + + document.SecurityDefinitions.Add("oauth2", new OpenApiSecurityScheme + { + Type = OpenApiSecuritySchemeType.OAuth2, + Description = "OAuth2 authentication", + Flows = new OpenApiOAuthFlows + { + AuthorizationCode = new OpenApiOAuthFlow + { + AuthorizationUrl = "https://example.com/oauth/authorize", + TokenUrl = "https://example.com/oauth/token", + Scopes = + { + { "read:pets", "Read pets" }, + { "write:pets", "Write pets" } + } + } + } + }); + + var getOperation = new OpenApiOperation + { + Summary = "Get secured data", + OperationId = "getSecuredData" + }; + getOperation.Responses.Add("200", new OpenApiResponse + { + Description = "Success" + }); + + var pathItem = new OpenApiPathItem + { + { OpenApiOperationMethod.Get, getOperation } + }; + document.Paths["/secured"] = pathItem; + + // Act + var json = document.ToJson(SchemaType.OpenApi3, true); + + // Assert + await Verify(json).UseDirectory("Snapshots"); + } + + [Fact] + public async Task Snapshot_RoundTrip_ComplexDocument() + { + // Arrange + var inputJson = @"{ + ""openapi"": ""3.0.0"", + ""info"": { + ""title"": ""Complex API"", + ""description"": ""A complex API for testing round-trip serialization"", + ""version"": ""3.1.0"", + ""contact"": { + ""name"": ""Developer"", + ""email"": ""dev@example.com"" + } + }, + ""servers"": [ + { + ""url"": ""https://api.example.com"", + ""description"": ""Production"" + } + ], + ""paths"": { + ""/items"": { + ""get"": { + ""tags"": [""items""], + ""summary"": ""List items"", + ""operationId"": ""listItems"", + ""parameters"": [ + { + ""name"": ""offset"", + ""in"": ""query"", + ""description"": ""Number of items to skip"", + ""schema"": { + ""type"": ""integer"", + ""default"": 0 + } + }, + { + ""name"": ""limit"", + ""in"": ""query"", + ""description"": ""Max number of items to return"", + ""schema"": { + ""type"": ""integer"", + ""default"": 20 + } + } + ], + ""responses"": { + ""200"": { + ""description"": ""A list of items"", + ""content"": { + ""application/json"": { + ""schema"": { + ""type"": ""array"", + ""items"": { + ""$ref"": ""#/components/schemas/Item"" + } + } + } + } + }, + ""400"": { + ""description"": ""Bad request"" + } + } + }, + ""post"": { + ""tags"": [""items""], + ""summary"": ""Create item"", + ""operationId"": ""createItem"", + ""requestBody"": { + ""required"": true, + ""content"": { + ""application/json"": { + ""schema"": { + ""$ref"": ""#/components/schemas/Item"" + } + } + } + }, + ""responses"": { + ""201"": { + ""description"": ""Item created"" + } + } + } + }, + ""/items/{id}"": { + ""get"": { + ""tags"": [""items""], + ""summary"": ""Get item by ID"", + ""operationId"": ""getItem"", + ""parameters"": [ + { + ""name"": ""id"", + ""in"": ""path"", + ""required"": true, + ""description"": ""The item ID"", + ""schema"": { + ""type"": ""string"" + } + } + ], + ""responses"": { + ""200"": { + ""description"": ""The item"", + ""content"": { + ""application/json"": { + ""schema"": { + ""$ref"": ""#/components/schemas/Item"" + } + } + } + }, + ""404"": { + ""description"": ""Item not found"" + } + } + } + } + }, + ""components"": { + ""schemas"": { + ""Item"": { + ""type"": ""object"", + ""required"": [""name""], + ""properties"": { + ""id"": { + ""type"": ""string"", + ""description"": ""The item ID"" + }, + ""name"": { + ""type"": ""string"", + ""description"": ""The item name"" + }, + ""tags"": { + ""type"": ""array"", + ""items"": { + ""type"": ""string"" + } + } + } + } + } + } +}"; + + // Act + var document = await OpenApiDocument.FromJsonAsync(inputJson, null, SchemaType.OpenApi3); + var json = document.ToJson(SchemaType.OpenApi3, true); + + // Assert + await Verify(json).UseDirectory("Snapshots"); + } + + [Fact] + public async Task Snapshot_ParameterRequired_RoundTrip() + { + // Arrange: document with both required=true and required=false (omitted) parameters + var inputJson = @"{ + ""openapi"": ""3.0.0"", + ""info"": { ""title"": ""Test"", ""version"": ""1.0.0"" }, + ""paths"": { + ""/test"": { + ""get"": { + ""operationId"": ""test"", + ""parameters"": [ + { + ""name"": ""requiredParam"", + ""in"": ""path"", + ""required"": true, + ""schema"": { ""type"": ""string"" } + }, + { + ""name"": ""optionalParam"", + ""in"": ""query"", + ""schema"": { ""type"": ""string"" } + } + ], + ""responses"": { + ""200"": { ""description"": ""OK"" } + } + } + } + } +}"; + + // Act + var document = await OpenApiDocument.FromJsonAsync(inputJson, null, SchemaType.OpenApi3); + var json = document.ToJson(SchemaType.OpenApi3, true); + + // Assert: required=true should be present, required=false should be omitted (OpenAPI default) + var reparsed = await OpenApiDocument.FromJsonAsync(json, null, SchemaType.OpenApi3); + var pathParams = reparsed.Paths["/test"]["get"].Parameters; + Assert.True(pathParams.First(p => p.Name == "requiredParam").IsRequired); + Assert.False(pathParams.First(p => p.Name == "optionalParam").IsRequired); + Assert.Contains("\"required\": true", json); + } + +} diff --git a/src/NSwag.Core.Tests/Serialization/Snapshots/OpenApiDocumentSnapshotTests.Snapshot_DocumentWithSecurity_OpenApi3.verified.txt b/src/NSwag.Core.Tests/Serialization/Snapshots/OpenApiDocumentSnapshotTests.Snapshot_DocumentWithSecurity_OpenApi3.verified.txt new file mode 100644 index 0000000000..c4cd65e5d1 --- /dev/null +++ b/src/NSwag.Core.Tests/Serialization/Snapshots/OpenApiDocumentSnapshotTests.Snapshot_DocumentWithSecurity_OpenApi3.verified.txt @@ -0,0 +1,50 @@ +{ + "openapi": "3.0.0", + "info": { + "title": "Secured API", + "version": "1.0.0" + }, + "paths": { + "/secured": { + "get": { + "summary": "Get secured data", + "operationId": "getSecuredData", + "responses": { + "200": { + "description": "Success" + } + } + } + } + }, + "components": { + "securitySchemes": { + "bearerAuth": { + "type": "http", + "description": "JWT Bearer token authentication", + "scheme": "bearer", + "bearerFormat": "JWT" + }, + "apiKey": { + "type": "apiKey", + "description": "API key authentication", + "name": "X-API-Key", + "in": "header" + }, + "oauth2": { + "type": "oauth2", + "description": "OAuth2 authentication", + "flows": { + "authorizationCode": { + "authorizationUrl": "https://example.com/oauth/authorize", + "tokenUrl": "https://example.com/oauth/token", + "scopes": { + "read:pets": "Read pets", + "write:pets": "Write pets" + } + } + } + } + } + } +} \ No newline at end of file diff --git a/src/NSwag.Core.Tests/Serialization/Snapshots/OpenApiDocumentSnapshotTests.Snapshot_FullDocument_OpenApi3.verified.txt b/src/NSwag.Core.Tests/Serialization/Snapshots/OpenApiDocumentSnapshotTests.Snapshot_FullDocument_OpenApi3.verified.txt new file mode 100644 index 0000000000..b4165b9bd7 --- /dev/null +++ b/src/NSwag.Core.Tests/Serialization/Snapshots/OpenApiDocumentSnapshotTests.Snapshot_FullDocument_OpenApi3.verified.txt @@ -0,0 +1,97 @@ +{ + "openapi": "3.0.0", + "info": { + "title": "Pet Store API", + "description": "A sample API for managing pets", + "termsOfService": "https://example.com/terms", + "contact": { + "name": "API Support", + "url": "https://example.com/support", + "email": "support@example.com" + }, + "license": { + "name": "MIT", + "url": "https://opensource.org/licenses/MIT" + }, + "version": "2.0.0" + }, + "servers": [ + { + "url": "https://api.example.com/v2", + "description": "Production server" + }, + { + "url": "https://staging.example.com/v2", + "description": "Staging server" + } + ], + "paths": { + "/pets": { + "get": { + "tags": [ + "pets" + ], + "summary": "List all pets", + "description": "Returns all pets from the system", + "operationId": "listPets", + "parameters": [ + { + "name": "limit", + "in": "query", + "description": "Maximum number of pets to return", + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "description": "A list of pets" + } + } + }, + "post": { + "tags": [ + "pets" + ], + "summary": "Create a pet", + "description": "Creates a new pet in the store", + "operationId": "createPet", + "requestBody": { + "x-name": "body", + "description": "Pet to add to the store", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Pet" + } + } + }, + "required": true + }, + "responses": { + "201": { + "description": "Pet created" + } + } + } + } + }, + "components": { + "schemas": { + "Pet": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "description": "The pet ID" + }, + "name": { + "type": "string", + "description": "The pet name" + } + } + } + } + } +} \ No newline at end of file diff --git a/src/NSwag.Core.Tests/Serialization/Snapshots/OpenApiDocumentSnapshotTests.Snapshot_MinimalDocument_OpenApi3.verified.txt b/src/NSwag.Core.Tests/Serialization/Snapshots/OpenApiDocumentSnapshotTests.Snapshot_MinimalDocument_OpenApi3.verified.txt new file mode 100644 index 0000000000..88fae98976 --- /dev/null +++ b/src/NSwag.Core.Tests/Serialization/Snapshots/OpenApiDocumentSnapshotTests.Snapshot_MinimalDocument_OpenApi3.verified.txt @@ -0,0 +1,8 @@ +{ + "openapi": "3.0.0", + "info": { + "title": "My API", + "version": "1.0.0" + }, + "components": {} +} \ No newline at end of file diff --git a/src/NSwag.Core.Tests/Serialization/Snapshots/OpenApiDocumentSnapshotTests.Snapshot_MinimalDocument_Swagger2.verified.txt b/src/NSwag.Core.Tests/Serialization/Snapshots/OpenApiDocumentSnapshotTests.Snapshot_MinimalDocument_Swagger2.verified.txt new file mode 100644 index 0000000000..78fe5b2dbb --- /dev/null +++ b/src/NSwag.Core.Tests/Serialization/Snapshots/OpenApiDocumentSnapshotTests.Snapshot_MinimalDocument_Swagger2.verified.txt @@ -0,0 +1,7 @@ +{ + "swagger": "2.0", + "info": { + "title": "My API", + "version": "1.0.0" + } +} \ No newline at end of file diff --git a/src/NSwag.Core.Tests/Serialization/Snapshots/OpenApiDocumentSnapshotTests.Snapshot_RoundTrip_ComplexDocument.verified.txt b/src/NSwag.Core.Tests/Serialization/Snapshots/OpenApiDocumentSnapshotTests.Snapshot_RoundTrip_ComplexDocument.verified.txt new file mode 100644 index 0000000000..0bfead3b44 --- /dev/null +++ b/src/NSwag.Core.Tests/Serialization/Snapshots/OpenApiDocumentSnapshotTests.Snapshot_RoundTrip_ComplexDocument.verified.txt @@ -0,0 +1,150 @@ +{ + "openapi": "3.0.0", + "info": { + "title": "Complex API", + "description": "A complex API for testing round-trip serialization", + "contact": { + "name": "Developer", + "email": "dev@example.com" + }, + "version": "3.1.0" + }, + "servers": [ + { + "url": "https://api.example.com", + "description": "Production" + } + ], + "paths": { + "/items": { + "get": { + "tags": [ + "items" + ], + "summary": "List items", + "operationId": "listItems", + "parameters": [ + { + "name": "offset", + "in": "query", + "description": "Number of items to skip", + "schema": { + "type": "integer", + "default": 0 + } + }, + { + "name": "limit", + "in": "query", + "description": "Max number of items to return", + "schema": { + "type": "integer", + "default": 20 + } + } + ], + "responses": { + "200": { + "description": "A list of items", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Item" + } + } + } + } + }, + "400": { + "description": "Bad request" + } + } + }, + "post": { + "tags": [ + "items" + ], + "summary": "Create item", + "operationId": "createItem", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Item" + } + } + }, + "required": true + }, + "responses": { + "201": { + "description": "Item created" + } + } + } + }, + "/items/{id}": { + "get": { + "tags": [ + "items" + ], + "summary": "Get item by ID", + "operationId": "getItem", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "The item ID", + "schema": { + "type": "string" + }, + "required": true + } + ], + "responses": { + "200": { + "description": "The item", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Item" + } + } + } + }, + "404": { + "description": "Item not found" + } + } + } + } + }, + "components": { + "schemas": { + "Item": { + "type": "object", + "required": [ + "name" + ], + "properties": { + "id": { + "type": "string", + "description": "The item ID" + }, + "name": { + "type": "string", + "description": "The item name" + }, + "tags": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + } + } +} \ No newline at end of file diff --git a/src/NSwag.Core.Yaml.Tests/YamlDocumentTests.cs b/src/NSwag.Core.Yaml.Tests/YamlDocumentTests.cs index 6d09c0b636..4f857c3cd5 100644 --- a/src/NSwag.Core.Yaml.Tests/YamlDocumentTests.cs +++ b/src/NSwag.Core.Yaml.Tests/YamlDocumentTests.cs @@ -1,4 +1,5 @@ -using Newtonsoft.Json.Linq; +using System.Text.Json.Nodes; +using NJsonSchema; using Xunit; namespace NSwag.Core.Yaml.Tests @@ -52,7 +53,7 @@ public async Task When_yaml_with_custom_property_is_loaded_then_document_is_not_ // Assert Assert.NotNull(document); - Assert.Equal("bar", document.Paths.First().Value.ExtensionData["x-swagger-router-controller"]); + Assert.Equal("bar", document.Paths.First().Value.ExtensionData["x-swagger-router-controller"]?.ToString()); Assert.Contains("x-swagger-router-controller: bar", yaml); } @@ -79,9 +80,55 @@ public async Task When_yaml_with_custom_property_which_is_an_object_is_loaded_th // Assert Assert.NotNull(document); - Assert.Equal(JObject.Parse(@"{""bar"": ""baz""}"), document.Paths.First().Value.ExtensionData["x-swagger-router-controller"]); + var extensionData = document.Paths.First().Value.ExtensionData["x-swagger-router-controller"]; + var jsonObj = extensionData is JsonObject jo ? jo : JsonNode.Parse(extensionData.ToString())?.AsObject(); + Assert.Equal("baz", jsonObj["bar"]?.GetValue()); Assert.Equal("baz", document.Paths.First().Value["get"].Responses["200"].Description); Assert.Contains("bar: baz", yaml); } + + [Fact] + public async Task When_yaml_openapi3_is_loaded_then_roundtrip_preserves_structure() + { + // Arrange + var yaml = @"openapi: 3.0.0 +info: + title: Test API + version: 1.0.0 +paths: + /items: + get: + operationId: listItems + parameters: + - name: id + in: path + required: true + schema: + type: string + - name: filter + in: query + schema: + type: string + responses: + '200': + description: A list of items"; + + // Act + var document = await OpenApiYamlDocument.FromYamlAsync(yaml, null, SchemaType.OpenApi3); + var roundTrippedYaml = document.ToYaml(); + var document2 = await OpenApiYamlDocument.FromYamlAsync(roundTrippedYaml, null, SchemaType.OpenApi3); + + // Assert + Assert.Equal("Test API", document2.Info.Title); + Assert.Equal("1.0.0", document2.Info.Version); + + var parameters = document2.Paths["/items"]["get"].Parameters; + Assert.Equal(2, parameters.Count); + Assert.True(parameters.First(p => p.Name == "id").IsRequired); + Assert.False(parameters.First(p => p.Name == "filter").IsRequired); + + Assert.Contains("operationId: listItems", roundTrippedYaml); + Assert.Contains("required: true", roundTrippedYaml); + } } } \ No newline at end of file diff --git a/src/NSwag.Core.Yaml/NSwag.Core.Yaml.csproj b/src/NSwag.Core.Yaml/NSwag.Core.Yaml.csproj index c915339949..10791210fd 100644 --- a/src/NSwag.Core.Yaml/NSwag.Core.Yaml.csproj +++ b/src/NSwag.Core.Yaml/NSwag.Core.Yaml.csproj @@ -4,9 +4,11 @@ NSwag + + diff --git a/src/NSwag.Core.Yaml/OpenApiYamlDocument.cs b/src/NSwag.Core.Yaml/OpenApiYamlDocument.cs index 71166a9a83..db33786baf 100644 --- a/src/NSwag.Core.Yaml/OpenApiYamlDocument.cs +++ b/src/NSwag.Core.Yaml/OpenApiYamlDocument.cs @@ -7,8 +7,7 @@ //----------------------------------------------------------------------- using System.Dynamic; -using Newtonsoft.Json; -using Newtonsoft.Json.Converters; +using System.Text.Json.Nodes; using NJsonSchema; using NJsonSchema.Generation; using NJsonSchema.Infrastructure; @@ -104,7 +103,9 @@ public static Task FromYamlAsync(TextReader data, string docume public static async Task FromYamlAsync(TextReader data, string documentPath, SchemaType expectedSchemaType, Func referenceResolverFactory, CancellationToken cancellationToken = default) { - var deserializer = new DeserializerBuilder().Build(); + var deserializer = new DeserializerBuilder() + .WithAttemptingUnquotedStringTypeDeserialization() + .Build(); var yamlObject = deserializer.Deserialize(data); var serializer = new SerializerBuilder() .JsonCompatible() @@ -121,11 +122,11 @@ public static async Task FromYamlAsync(TextReader data, string public static string ToYaml(this OpenApiDocument document) { var json = document.ToJson(); - var expConverter = new ExpandoObjectConverter(); - dynamic deserializedObject = JsonConvert.DeserializeObject(json, expConverter); + var jsonNode = JsonNode.Parse(json); + var expandoObject = ConvertJsonNodeToExpandoObject(jsonNode); var serializer = new Serializer(); - return serializer.Serialize(deserializedObject); + return serializer.Serialize(expandoObject); } /// Creates a Swagger specification from a JSON file. @@ -148,6 +149,33 @@ public static async Task FromUrlAsync(string url, CancellationT return await FromYamlAsync(data, url, cancellationToken).ConfigureAwait(false); } + private static object ConvertJsonNodeToExpandoObject(JsonNode node) + { + if (node is JsonObject jsonObject) + { + var expando = new ExpandoObject(); + var dict = (IDictionary)expando; + foreach (var property in jsonObject) + { + dict[property.Key] = property.Value != null ? ConvertJsonNodeToExpandoObject(property.Value) : null; + } + return expando; + } + else if (node is JsonArray jsonArray) + { + return jsonArray.Select(item => item != null ? ConvertJsonNodeToExpandoObject(item) : null).ToList(); + } + else if (node is JsonValue jsonValue) + { + if (jsonValue.TryGetValue(out var boolValue)) return boolValue; + if (jsonValue.TryGetValue(out var longValue)) return longValue; + if (jsonValue.TryGetValue(out var doubleValue)) return doubleValue; + if (jsonValue.TryGetValue(out var stringValue)) return stringValue; + return node.ToJsonString(); + } + return null; + } + private static Func CreateReferenceResolverFactory() { return document => diff --git a/src/NSwag.Core/Converters/EnumMemberStringEnumConverter.cs b/src/NSwag.Core/Converters/EnumMemberStringEnumConverter.cs new file mode 100644 index 0000000000..03ad3308e9 --- /dev/null +++ b/src/NSwag.Core/Converters/EnumMemberStringEnumConverter.cs @@ -0,0 +1,37 @@ +using System.Reflection; +using System.Runtime.Serialization; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace NSwag.Converters +{ + /// A JSON string enum converter that respects [EnumMember(Value = "...")] attributes + /// for custom serialization names. Wraps JsonStringEnumConverter with a naming policy. + internal class EnumMemberStringEnumConverter : JsonStringEnumConverter where T : struct, Enum + { + public EnumMemberStringEnumConverter() : base(new EnumMemberNamingPolicy()) + { + } + + private sealed class EnumMemberNamingPolicy : JsonNamingPolicy + { + private readonly Dictionary _nameMap; + + public EnumMemberNamingPolicy() + { + _nameMap = new Dictionary(); + foreach (var field in typeof(T).GetFields(BindingFlags.Public | BindingFlags.Static)) + { + var attribute = field.GetCustomAttribute(); + if (attribute?.Value != null) + { + _nameMap[field.Name] = attribute.Value; + } + } + } + + public override string ConvertName(string name) => + _nameMap.TryGetValue(name, out var mapped) ? mapped : name; + } + } +} diff --git a/src/NSwag.Core/Converters/OpenApiParameterJsonConverter.cs b/src/NSwag.Core/Converters/OpenApiParameterJsonConverter.cs new file mode 100644 index 0000000000..ff1558f5d2 --- /dev/null +++ b/src/NSwag.Core/Converters/OpenApiParameterJsonConverter.cs @@ -0,0 +1,100 @@ +//----------------------------------------------------------------------- +// +// Copyright (c) Rico Suter. All rights reserved. +// +// https://github.com/RicoSuter/NSwag/blob/master/LICENSE.md +// Rico Suter, mail@rsuter.com +//----------------------------------------------------------------------- + +using System.Text.Json; +using System.Text.Json.Nodes; +using System.Text.Json.Serialization; + +namespace NSwag.Converters +{ + /// Custom JSON converter for OpenApiParameter that resolves the property name + /// collision between OpenApiParameter.IsRequired (bool) and the inherited + /// JsonSchema.RequiredPropertiesRaw (string[]), which both map to "required" in JSON. + /// + /// In OpenAPI, "required" on a parameter is always a boolean. In JSON Schema, "required" + /// is an array of property names. This converter inspects the JSON value type and routes + /// it to the correct property. + /// + internal class OpenApiParameterJsonConverter : JsonConverter + { + public override OpenApiParameter Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + var node = JsonNode.Parse(ref reader); + if (node is not JsonObject obj) + { + return null; + } + + // Extract the boolean "required" before deserialization. + // If "required" is a bool, it's the parameter's IsRequired property. + // If "required" is an array, leave it for JsonSchema.RequiredPropertiesRaw. + bool? isRequired = null; + if (obj.TryGetPropertyValue("required", out var requiredNode)) + { + if (requiredNode is JsonValue jsonValue && jsonValue.TryGetValue(out var boolValue)) + { + isRequired = boolValue; + obj.Remove("required"); + } + // If it's an array, leave it — RequiredPropertiesRaw will handle it + } + + // Deserialize without this converter to avoid recursion + var optionsWithout = GetOrCreateOptionsWithout(options); + var parameter = obj.Deserialize(optionsWithout); + + if (parameter != null && isRequired.HasValue) + { + parameter.IsRequired = isRequired.Value; + } + + return parameter; + } + + public override void Write(Utf8JsonWriter writer, OpenApiParameter value, JsonSerializerOptions options) + { + // Delegate to stripped options for the base serialization (the + // SchemaSerializationConverter in the full options handles property filtering + // for OpenApiParameter via its property-by-property Write method, but this + // attribute converter takes precedence, so we serialize without it and manually + // add the "required" boolean). + var optionsWithout = GetOrCreateOptionsWithout(options); + var node = JsonSerializer.SerializeToNode(value, optionsWithout); + + if (node is JsonObject obj) + { + // Add the "required" boolean (IsRequired is [JsonIgnore], so it's not in the node) + if (value.IsRequired) + { + obj["required"] = true; + } + } + + node?.WriteTo(writer); + } + + private static readonly System.Collections.Concurrent.ConcurrentDictionary _cache = new(); + + private static JsonSerializerOptions GetOrCreateOptionsWithout(JsonSerializerOptions options) + { + return _cache.GetOrAdd(options, static parentOptions => + { + var newOptions = new JsonSerializerOptions(parentOptions); + // Remove the OpenApiParameterJsonConverter from converters list + for (var i = newOptions.Converters.Count - 1; i >= 0; i--) + { + if (newOptions.Converters[i] is OpenApiParameterJsonConverter) + { + newOptions.Converters.RemoveAt(i); + } + } + return newOptions; + }); + } + } +} diff --git a/src/NSwag.Core/JsonExpectedSchema.cs b/src/NSwag.Core/JsonExpectedSchema.cs index d6f980d086..dcacc474bc 100644 --- a/src/NSwag.Core/JsonExpectedSchema.cs +++ b/src/NSwag.Core/JsonExpectedSchema.cs @@ -6,7 +6,7 @@ // Rico Suter, mail@rsuter.com //----------------------------------------------------------------------- -using Newtonsoft.Json; +using System.Text.Json.Serialization; using NJsonSchema; namespace NSwag @@ -15,11 +15,13 @@ namespace NSwag public class JsonExpectedSchema { /// Gets or sets the description. - [JsonProperty(PropertyName = "description", DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("description")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public string Description { get; set; } /// Gets or sets the schema. - [JsonProperty(PropertyName = "schema", DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("schema")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public JsonSchema Schema { get; set; } } } \ No newline at end of file diff --git a/src/NSwag.Core/NSwag.Core.csproj b/src/NSwag.Core/NSwag.Core.csproj index 63affeb277..da594d9645 100644 --- a/src/NSwag.Core/NSwag.Core.csproj +++ b/src/NSwag.Core/NSwag.Core.csproj @@ -6,9 +6,11 @@ true + + diff --git a/src/NSwag.Core/OpenApiCallback.cs b/src/NSwag.Core/OpenApiCallback.cs index 44ede1afd9..74469345be 100644 --- a/src/NSwag.Core/OpenApiCallback.cs +++ b/src/NSwag.Core/OpenApiCallback.cs @@ -6,9 +6,9 @@ // Rico Suter, mail@rsuter.com //----------------------------------------------------------------------- -using Newtonsoft.Json; -using NJsonSchema.References; using System.Collections; +using System.Text.Json.Serialization; +using NJsonSchema.References; namespace NSwag { diff --git a/src/NSwag.Core/OpenApiComponents.cs b/src/NSwag.Core/OpenApiComponents.cs index 722d6b26a4..2e49cc7747 100644 --- a/src/NSwag.Core/OpenApiComponents.cs +++ b/src/NSwag.Core/OpenApiComponents.cs @@ -7,7 +7,7 @@ //----------------------------------------------------------------------- using System.Collections.Specialized; -using Newtonsoft.Json; +using System.Text.Json.Serialization; using NJsonSchema; using NSwag.Collections; @@ -16,6 +16,21 @@ namespace NSwag /// Container for reusable components (OpenAPI only). public class OpenApiComponents { + /// Parameterless constructor for System.Text.Json deserialization (Populate mode). + [System.Text.Json.Serialization.JsonConstructor] + internal OpenApiComponents() + { + Schemas = new Dictionary(); + RequestBodies = new Dictionary(); + Responses = new Dictionary(); + Parameters = new Dictionary(); + Examples = new Dictionary(); + Headers = new Dictionary(); + SecuritySchemes = new Dictionary(); + Links = new Dictionary(); + Callbacks = new Dictionary(); + } + /// /// public OpenApiComponents(OpenApiDocument document) @@ -122,39 +137,48 @@ public OpenApiComponents(OpenApiDocument document) } /// Gets or sets the types. - [JsonProperty(PropertyName = "schemas", DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("schemas")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public IDictionary Schemas { get; } /// Gets or sets the responses which can be used for all operations. - [JsonProperty(PropertyName = "requestBodies", DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("requestBodies")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public IDictionary RequestBodies { get; } /// Gets or sets the responses which can be used for all operations. - [JsonProperty(PropertyName = "responses", DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("responses")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public IDictionary Responses { get; } /// Gets or sets the parameters which can be used for all operations. - [JsonProperty(PropertyName = "parameters", DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("parameters")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public IDictionary Parameters { get; } /// Gets or sets the headers. - [JsonProperty(PropertyName = "examples", DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("examples")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public IDictionary Examples { get; set; } /// Gets or sets the types. - [JsonProperty(PropertyName = "headers", DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("headers")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public IDictionary Headers { get; } /// Gets or sets the security definitions. - [JsonProperty(PropertyName = "securitySchemes", DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("securitySchemes")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public IDictionary SecuritySchemes { get; } /// Gets or sets the security definitions. - [JsonProperty(PropertyName = "links", DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("links")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public IDictionary Links { get; } /// Gets or sets the security definitions. - [JsonProperty(PropertyName = "callbacks", DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("callbacks")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public IDictionary Callbacks { get; } } } \ No newline at end of file diff --git a/src/NSwag.Core/OpenApiContact.cs b/src/NSwag.Core/OpenApiContact.cs index 54aa2e9478..a2cb6cc4dc 100644 --- a/src/NSwag.Core/OpenApiContact.cs +++ b/src/NSwag.Core/OpenApiContact.cs @@ -6,7 +6,7 @@ // Rico Suter, mail@rsuter.com //----------------------------------------------------------------------- -using Newtonsoft.Json; +using System.Text.Json.Serialization; using NJsonSchema; namespace NSwag @@ -15,15 +15,18 @@ namespace NSwag public class OpenApiContact : JsonExtensionObject { /// Gets or sets the name. - [JsonProperty(PropertyName = "name", DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate)] + [JsonPropertyName("name")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public string Name { get; set; } /// Gets or sets the contact URL. - [JsonProperty(PropertyName = "url", DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate)] + [JsonPropertyName("url")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public string Url { get; set; } /// Gets or sets the contact email. - [JsonProperty(PropertyName = "email", DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate)] + [JsonPropertyName("email")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public string Email { get; set; } } } \ No newline at end of file diff --git a/src/NSwag.Core/OpenApiDocument.Serialization.cs b/src/NSwag.Core/OpenApiDocument.Serialization.cs index fac06d9a16..c36219a192 100644 --- a/src/NSwag.Core/OpenApiDocument.Serialization.cs +++ b/src/NSwag.Core/OpenApiDocument.Serialization.cs @@ -6,111 +6,135 @@ // Rico Suter, mail@rsuter.com //----------------------------------------------------------------------- -using Newtonsoft.Json; -using Newtonsoft.Json.Converters; -using NJsonSchema; -using NJsonSchema.Infrastructure; using System.Collections.ObjectModel; using System.Collections.Specialized; +using System.Text.Json.Serialization; +using NJsonSchema; +using NJsonSchema.Infrastructure; namespace NSwag { public partial class OpenApiDocument { - private static readonly Lazy Swagger2ContractResolver = - new Lazy(() => CreateJsonSerializerContractResolver(SchemaType.Swagger2)); + private static readonly Lazy Swagger2Converter = + new Lazy(() => CreateSchemaSerializationConverter(SchemaType.Swagger2)); - private static readonly Lazy OpenApi3ContractResolver = - new Lazy(() => CreateJsonSerializerContractResolver(SchemaType.OpenApi3)); + private static readonly Lazy OpenApi3Converter = + new Lazy(() => CreateSchemaSerializationConverter(SchemaType.OpenApi3)); - /// Creates the serializer contract resolver based on the . + /// Creates the schema serialization converter based on the . /// The schema type. - /// The settings. - public static PropertyRenameAndIgnoreSerializerContractResolver GetJsonSerializerContractResolver(SchemaType schemaType) + /// The converter. + public static SchemaSerializationConverter GetSchemaSerializationConverter(SchemaType schemaType) { if (schemaType == SchemaType.Swagger2) { - return Swagger2ContractResolver.Value; + return Swagger2Converter.Value; } else if (schemaType == SchemaType.OpenApi3) { - return OpenApi3ContractResolver.Value; + return OpenApi3Converter.Value; } throw new ArgumentException("The schema type '" + schemaType + "' is not supported."); } - private static PropertyRenameAndIgnoreSerializerContractResolver CreateJsonSerializerContractResolver(SchemaType schemaType) + private static SchemaSerializationConverter CreateSchemaSerializationConverter(SchemaType schemaType) { - var resolver = JsonSchema.CreateJsonSerializerContractResolver(schemaType); + var converter = JsonSchema.CreateSchemaSerializationConverter(schemaType); + + // Add custom converter for OpenApiParameter to handle the "required" property + // collision between OpenApiParameter.IsRequired (bool) and JsonSchema.RequiredPropertiesRaw (string[]). + converter.AddConverter(new Converters.OpenApiParameterJsonConverter()); + + // Register types so the SchemaSerializationConverter handles their serialization + // (needed for empty collection stripping and schema-version-aware property filtering). + converter.IgnoreProperty(typeof(OpenApiComponents)); + converter.IgnoreProperty(typeof(OpenApiServer)); + converter.IgnoreProperty(typeof(OpenApiOAuthFlows)); + converter.IgnoreProperty(typeof(OpenApiOAuthFlow)); + converter.IgnoreProperty(typeof(OpenApiMediaType)); + converter.IgnoreProperty(typeof(OpenApiLink)); + converter.IgnoreProperty(typeof(OpenApiRequestBody)); + converter.IgnoreProperty(typeof(OpenApiEncoding)); + converter.IgnoreProperty(typeof(OpenApiInfo)); + converter.IgnoreProperty(typeof(OpenApiTag)); + converter.IgnoreProperty(typeof(OpenApiExample)); + converter.IgnoreProperty(typeof(OpenApiExternalDocumentation)); + converter.IgnoreProperty(typeof(OpenApiContact)); + converter.IgnoreProperty(typeof(OpenApiLicense)); + converter.IgnoreProperty(typeof(OpenApiServerVariable)); + converter.IgnoreProperty(typeof(OpenApiHeader)); + // Note: OpenApiCallback and OpenApiPathItem implement IDictionary and have their own + // serialization logic, so they're not registered with the SchemaSerializationConverter. if (schemaType == SchemaType.Swagger2) { - resolver.IgnoreProperty(typeof(OpenApiDocument), "openapi"); - resolver.IgnoreProperty(typeof(OpenApiDocument), "servers"); - resolver.IgnoreProperty(typeof(OpenApiParameter), "title"); + converter.IgnoreProperty(typeof(OpenApiDocument), "openapi"); + converter.IgnoreProperty(typeof(OpenApiDocument), "servers"); + converter.IgnoreProperty(typeof(OpenApiParameter), "title"); // TODO: Use rename for not mapped properties! - resolver.IgnoreProperty(typeof(OpenApiPathItem), "summary"); - resolver.IgnoreProperty(typeof(OpenApiPathItem), "description"); - resolver.IgnoreProperty(typeof(OpenApiPathItem), "servers"); + converter.IgnoreProperty(typeof(OpenApiPathItem), "summary"); + converter.IgnoreProperty(typeof(OpenApiPathItem), "description"); + converter.IgnoreProperty(typeof(OpenApiPathItem), "servers"); - resolver.IgnoreProperty(typeof(OpenApiOperation), "callbacks"); - resolver.IgnoreProperty(typeof(OpenApiOperation), "servers"); - resolver.IgnoreProperty(typeof(OpenApiOperation), "requestBody"); + converter.IgnoreProperty(typeof(OpenApiOperation), "callbacks"); + converter.IgnoreProperty(typeof(OpenApiOperation), "servers"); + converter.IgnoreProperty(typeof(OpenApiOperation), "requestBody"); - resolver.IgnoreProperty(typeof(OpenApiDocument), "components"); - resolver.IgnoreProperty(typeof(OpenApiParameter), "examples"); - resolver.IgnoreProperty(typeof(OpenApiParameter), "x-position"); + converter.IgnoreProperty(typeof(OpenApiDocument), "components"); + converter.IgnoreProperty(typeof(OpenApiParameter), "examples"); + converter.IgnoreProperty(typeof(OpenApiParameter), "x-position"); - resolver.IgnoreProperty(typeof(OpenApiResponse), "content"); - resolver.IgnoreProperty(typeof(OpenApiResponse), "links"); + converter.IgnoreProperty(typeof(OpenApiResponse), "content"); + converter.IgnoreProperty(typeof(OpenApiResponse), "links"); - resolver.IgnoreProperty(typeof(OpenApiSecurityScheme), "scheme"); - resolver.IgnoreProperty(typeof(OpenApiSecurityScheme), "bearerFormat"); - resolver.IgnoreProperty(typeof(OpenApiSecurityScheme), "openIdConnectUrl"); - resolver.IgnoreProperty(typeof(OpenApiSecurityScheme), "flows"); + converter.IgnoreProperty(typeof(OpenApiSecurityScheme), "scheme"); + converter.IgnoreProperty(typeof(OpenApiSecurityScheme), "bearerFormat"); + converter.IgnoreProperty(typeof(OpenApiSecurityScheme), "openIdConnectUrl"); + converter.IgnoreProperty(typeof(OpenApiSecurityScheme), "flows"); } else if (schemaType == SchemaType.OpenApi3) { - resolver.IgnoreProperty(typeof(OpenApiDocument), "swagger"); + converter.IgnoreProperty(typeof(OpenApiDocument), "swagger"); - resolver.IgnoreProperty(typeof(OpenApiDocument), "host"); - resolver.IgnoreProperty(typeof(OpenApiDocument), "basePath"); - resolver.IgnoreProperty(typeof(OpenApiDocument), "schemes"); + converter.IgnoreProperty(typeof(OpenApiDocument), "host"); + converter.IgnoreProperty(typeof(OpenApiDocument), "basePath"); + converter.IgnoreProperty(typeof(OpenApiDocument), "schemes"); - resolver.IgnoreProperty(typeof(OpenApiDocument), "consumes"); - resolver.IgnoreProperty(typeof(OpenApiDocument), "produces"); + converter.IgnoreProperty(typeof(OpenApiDocument), "consumes"); + converter.IgnoreProperty(typeof(OpenApiDocument), "produces"); - resolver.IgnoreProperty(typeof(OpenApiOperation), "schemes"); - resolver.IgnoreProperty(typeof(OpenApiOperation), "consumes"); - resolver.IgnoreProperty(typeof(OpenApiOperation), "produces"); + converter.IgnoreProperty(typeof(OpenApiOperation), "schemes"); + converter.IgnoreProperty(typeof(OpenApiOperation), "consumes"); + converter.IgnoreProperty(typeof(OpenApiOperation), "produces"); - //resolver.IgnoreProperty(typeof(SwaggerParameter), "x-nullable"); + //converter.IgnoreProperty(typeof(SwaggerParameter), "x-nullable"); - //resolver.IgnoreProperty(typeof(SwaggerResponse), "consumes"); => TODO map to response.content - //resolver.IgnoreProperty(typeof(SwaggerResponse), "produces"); + //converter.IgnoreProperty(typeof(SwaggerResponse), "consumes"); => TODO map to response.content + //converter.IgnoreProperty(typeof(SwaggerResponse), "produces"); - resolver.IgnoreProperty(typeof(OpenApiDocument), "definitions"); - resolver.IgnoreProperty(typeof(OpenApiDocument), "parameters"); - resolver.IgnoreProperty(typeof(OpenApiDocument), "responses"); - resolver.IgnoreProperty(typeof(OpenApiDocument), "securityDefinitions"); + converter.IgnoreProperty(typeof(OpenApiDocument), "definitions"); + converter.IgnoreProperty(typeof(OpenApiDocument), "parameters"); + converter.IgnoreProperty(typeof(OpenApiDocument), "responses"); + converter.IgnoreProperty(typeof(OpenApiDocument), "securityDefinitions"); - resolver.IgnoreProperty(typeof(OpenApiResponse), "schema"); - resolver.IgnoreProperty(typeof(OpenApiResponse), "examples"); - resolver.IgnoreProperty(typeof(OpenApiResponse), "x-nullable"); + converter.IgnoreProperty(typeof(OpenApiResponse), "schema"); + converter.IgnoreProperty(typeof(OpenApiResponse), "examples"); + converter.IgnoreProperty(typeof(OpenApiResponse), "x-nullable"); - resolver.IgnoreProperty(typeof(OpenApiSecurityScheme), "flow"); - resolver.IgnoreProperty(typeof(OpenApiSecurityScheme), "authorizationUrl"); - resolver.IgnoreProperty(typeof(OpenApiSecurityScheme), "tokenUrl"); - resolver.IgnoreProperty(typeof(OpenApiSecurityScheme), "scopes"); + converter.IgnoreProperty(typeof(OpenApiSecurityScheme), "flow"); + converter.IgnoreProperty(typeof(OpenApiSecurityScheme), "authorizationUrl"); + converter.IgnoreProperty(typeof(OpenApiSecurityScheme), "tokenUrl"); + converter.IgnoreProperty(typeof(OpenApiSecurityScheme), "scopes"); } else { throw new ArgumentException("The given schema type is not supported."); } - return resolver; + return converter; } private ObservableCollection _schemes = []; @@ -118,7 +142,9 @@ private static PropertyRenameAndIgnoreSerializerContractResolver CreateJsonSeria internal List _produces = []; /// Gets or sets the host (name or ip) serving the API (Swagger only). - [JsonProperty(PropertyName = "host", Order = 5, DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate)] + [JsonPropertyName("host")] + [JsonPropertyOrder(5)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public string Host { get => Servers?.FirstOrDefault()?.Url?.Replace("http://", "").Replace("https://", "").Split('/')[0]; @@ -126,7 +152,9 @@ public string Host } /// Gets or sets the base path on which the API is served, which is relative to the . - [JsonProperty(PropertyName = "basePath", Order = 6, DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate)] + [JsonPropertyName("basePath")] + [JsonPropertyOrder(6)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public string BasePath { get @@ -138,7 +166,9 @@ public string BasePath } /// Gets or sets the schemes. - [JsonProperty(PropertyName = "schemes", Order = 7, DefaultValueHandling = DefaultValueHandling.Ignore, ItemConverterType = typeof(StringEnumConverter))] + [JsonPropertyName("schemes")] + [JsonPropertyOrder(7)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public ICollection Schemes { get @@ -187,7 +217,9 @@ private void UpdateServers(ICollection schemes, string host, stri } /// Gets or sets a list of MIME types the operation can consume. - [JsonProperty(PropertyName = "consumes", Order = 8, DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("consumes")] + [JsonPropertyOrder(8)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public ICollection Consumes { get => _consumes; @@ -195,7 +227,9 @@ public ICollection Consumes } /// Gets or sets a list of MIME types the operation can produce. - [JsonProperty(PropertyName = "produces", Order = 9, DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("produces")] + [JsonPropertyOrder(9)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public ICollection Produces { get => _produces; @@ -203,19 +237,27 @@ public ICollection Produces } /// Gets or sets the types (Swagger only). - [JsonProperty(PropertyName = "definitions", Order = 13, DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("definitions")] + [JsonPropertyOrder(13)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public IDictionary Definitions => Components.Schemas; /// Gets or sets the parameters which can be used for all operations (Swagger only). - [JsonProperty(PropertyName = "parameters", Order = 14, DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("parameters")] + [JsonPropertyOrder(14)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public IDictionary Parameters => Components.Parameters; /// Gets or sets the responses which can be used for all operations (Swagger only). - [JsonProperty(PropertyName = "responses", Order = 15, DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("responses")] + [JsonPropertyOrder(15)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public IDictionary Responses => Components.Responses; /// Gets or sets the security definitions (Swagger only). - [JsonProperty(PropertyName = "securityDefinitions", Order = 16, DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("securityDefinitions")] + [JsonPropertyOrder(16)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public IDictionary SecurityDefinitions => Components.SecuritySchemes; } } diff --git a/src/NSwag.Core/OpenApiDocument.cs b/src/NSwag.Core/OpenApiDocument.cs index 6be5e3a0b7..526209fbb8 100644 --- a/src/NSwag.Core/OpenApiDocument.cs +++ b/src/NSwag.Core/OpenApiDocument.cs @@ -8,8 +8,8 @@ using System.Collections.Specialized; using System.Reflection; +using System.Text.Json.Serialization; using System.Text.RegularExpressions; -using Newtonsoft.Json; using NJsonSchema; using NJsonSchema.Generation; using NJsonSchema.Infrastructure; @@ -64,39 +64,58 @@ public OpenApiDocument() public string DocumentPath { get; set; } /// Gets or sets the Swagger generator information. - [JsonProperty(PropertyName = "x-generator", Order = 1, DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate)] + [JsonPropertyName("x-generator")] + [JsonPropertyOrder(1)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public string Generator { get; set; } /// Gets or sets the Swagger specification version being used (Swagger only). - [JsonProperty(PropertyName = "swagger", Order = 2, DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate)] + [JsonPropertyName("swagger")] + [JsonPropertyOrder(2)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public string Swagger { get; set; } /// Gets or sets the OpenAPI specification version being used (OpenAPI only). - [JsonProperty(PropertyName = "openapi", Order = 3, DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate)] + [JsonPropertyName("openapi")] + [JsonPropertyOrder(3)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public string OpenApi { get; set; } /// Gets or sets the metadata about the API. - [JsonProperty(PropertyName = "info", Order = 4, DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate)] + [JsonPropertyName("info")] + [JsonPropertyOrder(4)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public OpenApiInfo Info { get; set; } /// Gets or sets the servers (OpenAPI only). - [JsonProperty(PropertyName = "servers", Order = 10, DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate)] + [JsonPropertyName("servers")] + [JsonPropertyOrder(10)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public ICollection Servers { get; private set; } = []; /// Gets or sets the operations. - [JsonProperty(PropertyName = "paths", Order = 11, DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("paths")] + [JsonPropertyOrder(11)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public IDictionary Paths => _paths; /// Gets or sets the components. - [JsonProperty(PropertyName = "components", Order = 12, DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("components")] + [JsonPropertyOrder(12)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + [JsonObjectCreationHandling(JsonObjectCreationHandling.Populate)] public OpenApiComponents Components { get; } /// Gets or sets a security description. - [JsonProperty(PropertyName = "security", Order = 17, DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("security")] + [JsonPropertyOrder(17)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public ICollection Security { get; set; } = []; /// Gets or sets the description. - [JsonProperty(PropertyName = "tags", Order = 18, DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("tags")] + [JsonPropertyOrder(18)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public IList Tags { get; set; } = []; /// Gets the base URL of the web service. @@ -104,7 +123,9 @@ public OpenApiDocument() public string BaseUrl => Servers?.FirstOrDefault(s => s.IsValid)?.Url ?? ""; /// Gets or sets the external documentation. - [JsonProperty(PropertyName = "externalDocs", Order = 19, DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("externalDocs")] + [JsonPropertyOrder(19)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public OpenApiExternalDocumentation ExternalDocumentation { get; set; } /// Converts the Swagger specification to JSON. @@ -120,19 +141,19 @@ public string ToJson() [Obsolete("Do not use this method but only ToJson(). Use the correct generator settings to generate a document in the correct format.")] public string ToJson(SchemaType schemaType) { - return ToJson(schemaType, Formatting.Indented); + return ToJson(schemaType, true); } /// Converts the description object to JSON. /// The schema type. - /// The formatting. + /// Whether to write indented JSON. /// The JSON string. - public string ToJson(SchemaType schemaType, Formatting formatting) + public string ToJson(SchemaType schemaType, bool writeIndented) { GenerateOperationIds(); - var contractResolver = GetJsonSerializerContractResolver(schemaType); - return JsonSchemaSerialization.ToJson(this, schemaType, contractResolver, formatting); + var converter = GetSchemaSerializationConverter(schemaType); + return JsonSchemaSerialization.ToJson(this, schemaType, converter, writeIndented); } /// Creates a Swagger specification from a JSON string. @@ -200,7 +221,7 @@ public static async Task FromJsonAsync(string data, string docu throw new NotSupportedException("The schema type JsonSchema is not supported."); } - var contractResolver = GetJsonSerializerContractResolver(expectedSchemaType); + var converter = GetSchemaSerializationConverter(expectedSchemaType); return await JsonSchemaSerialization.FromJsonAsync(data, expectedSchemaType, documentPath, document => { document.SchemaType = expectedSchemaType; @@ -213,7 +234,7 @@ public static async Task FromJsonAsync(string data, string docu var schemaResolver = new OpenApiSchemaResolver(document, new SystemTextJsonSchemaGeneratorSettings()); return new JsonReferenceResolver(schemaResolver); } - }, contractResolver, cancellationToken).ConfigureAwait(false); + }, converter, cancellationToken).ConfigureAwait(false); } /// Creates a Swagger specification from a JSON file. diff --git a/src/NSwag.Core/OpenApiEncoding.cs b/src/NSwag.Core/OpenApiEncoding.cs index 177d93bc05..8d0f40c943 100644 --- a/src/NSwag.Core/OpenApiEncoding.cs +++ b/src/NSwag.Core/OpenApiEncoding.cs @@ -6,7 +6,7 @@ // Rico Suter, mail@rsuter.com //----------------------------------------------------------------------- -using Newtonsoft.Json; +using System.Text.Json.Serialization; namespace NSwag { @@ -14,23 +14,28 @@ namespace NSwag public class OpenApiEncoding { /// Gets or sets the encoding type. - [JsonProperty(PropertyName = "encodingType", DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("encodingType")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public string EncodingType { get; set; } /// Gets or sets the headers. - [JsonProperty(PropertyName = "headers", DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("headers")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public OpenApiHeaders Headers { get; } = []; /// Gets or sets the encoding type. - [JsonProperty(PropertyName = "style", DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("style")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public string Style { get; set; } /// Gets or sets a value indicating whether values of type array or object generate separate parameters for each value of the array, or key-value-pair of the map. - [JsonProperty(PropertyName = "explode", DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("explode")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool Explode { get; set; } /// Gets or sets a value indicating whether the parameter value should allow reserved characters, as defined by RFC3986. - [JsonProperty(PropertyName = "allowReserved", DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("allowReserved")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool AllowReserved { get; set; } } } \ No newline at end of file diff --git a/src/NSwag.Core/OpenApiExample.cs b/src/NSwag.Core/OpenApiExample.cs index 997f68bc3b..d7c875eb6a 100644 --- a/src/NSwag.Core/OpenApiExample.cs +++ b/src/NSwag.Core/OpenApiExample.cs @@ -6,7 +6,7 @@ // Rico Suter, mail@rsuter.com //----------------------------------------------------------------------- -using Newtonsoft.Json; +using System.Text.Json.Serialization; using NJsonSchema.References; namespace NSwag @@ -15,19 +15,23 @@ namespace NSwag public class OpenApiExample : JsonReferenceBase, IJsonReference { /// Gets or sets the example's description. - [JsonProperty(PropertyName = "summary", DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate)] + [JsonPropertyName("summary")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public string Summary { get; set; } /// Gets or sets the example's description. - [JsonProperty(PropertyName = "description", DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate)] + [JsonPropertyName("description")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public string Description { get; set; } /// Gets or sets the example's value. - [JsonProperty(PropertyName = "value", DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate)] + [JsonPropertyName("value")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public object Value { get; set; } /// Gets or sets the example's external value. - [JsonProperty(PropertyName = "externalValue", DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate)] + [JsonPropertyName("externalValue")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public string ExternalValue { get; set; } /// Gets the actual example, either this or the referenced example. diff --git a/src/NSwag.Core/OpenApiExternalDocumentation.cs b/src/NSwag.Core/OpenApiExternalDocumentation.cs index d6c12e8a68..feb6183516 100644 --- a/src/NSwag.Core/OpenApiExternalDocumentation.cs +++ b/src/NSwag.Core/OpenApiExternalDocumentation.cs @@ -6,7 +6,7 @@ // Rico Suter, mail@rsuter.com //----------------------------------------------------------------------- -using Newtonsoft.Json; +using System.Text.Json.Serialization; using NJsonSchema; namespace NSwag @@ -15,11 +15,13 @@ namespace NSwag public class OpenApiExternalDocumentation : JsonExtensionObject { /// Gets or sets the description. - [JsonProperty(PropertyName = "description", DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate)] + [JsonPropertyName("description")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public string Description { get; set; } /// Gets or sets the documentation URL. - [JsonProperty(PropertyName = "url", DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate)] + [JsonPropertyName("url")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public string Url { get; set; } } } \ No newline at end of file diff --git a/src/NSwag.Core/OpenApiInfo.cs b/src/NSwag.Core/OpenApiInfo.cs index b04f3fec91..0086cba7bd 100644 --- a/src/NSwag.Core/OpenApiInfo.cs +++ b/src/NSwag.Core/OpenApiInfo.cs @@ -6,7 +6,7 @@ // Rico Suter, mail@rsuter.com //----------------------------------------------------------------------- -using Newtonsoft.Json; +using System.Text.Json.Serialization; using NJsonSchema; namespace NSwag @@ -15,28 +15,35 @@ namespace NSwag public class OpenApiInfo : JsonExtensionObject { /// Gets or sets the title. - [JsonProperty(PropertyName = "title", Required = Required.Always, - DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate)] + [JsonPropertyName("title")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + [JsonRequired] public string Title { get; set; } = "Swagger specification"; /// Gets or sets the description. - [JsonProperty(PropertyName = "description", DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate)] + [JsonPropertyName("description")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public string Description { get; set; } /// Gets or sets the terms of service. - [JsonProperty(PropertyName = "termsOfService", DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate)] + [JsonPropertyName("termsOfService")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public string TermsOfService { get; set; } /// Gets or sets the contact information. - [JsonProperty(PropertyName = "contact", DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate)] + [JsonPropertyName("contact")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public OpenApiContact Contact { get; set; } /// Gets or sets the license information. - [JsonProperty(PropertyName = "license", DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate)] + [JsonPropertyName("license")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public OpenApiLicense License { get; set; } /// Gets or sets the API version. - [JsonProperty(PropertyName = "version", Required = Required.Always, DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate)] + [JsonPropertyName("version")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + [JsonRequired] public string Version { get; set; } = "1.0.0"; } } \ No newline at end of file diff --git a/src/NSwag.Core/OpenApiLicense.cs b/src/NSwag.Core/OpenApiLicense.cs index 6691df40d4..bac8fe24c3 100644 --- a/src/NSwag.Core/OpenApiLicense.cs +++ b/src/NSwag.Core/OpenApiLicense.cs @@ -6,7 +6,7 @@ // Rico Suter, mail@rsuter.com //----------------------------------------------------------------------- -using Newtonsoft.Json; +using System.Text.Json.Serialization; using NJsonSchema; namespace NSwag @@ -15,11 +15,13 @@ namespace NSwag public class OpenApiLicense : JsonExtensionObject { /// Gets or sets the name. - [JsonProperty(PropertyName = "name", DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate)] + [JsonPropertyName("name")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public string Name { get; set; } /// Gets or sets the license URL. - [JsonProperty(PropertyName = "url", DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate)] + [JsonPropertyName("url")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public string Url { get; set; } } } \ No newline at end of file diff --git a/src/NSwag.Core/OpenApiLink.cs b/src/NSwag.Core/OpenApiLink.cs index 6260608768..f8a58fcc17 100644 --- a/src/NSwag.Core/OpenApiLink.cs +++ b/src/NSwag.Core/OpenApiLink.cs @@ -6,7 +6,7 @@ // Rico Suter, mail@rsuter.com //----------------------------------------------------------------------- -using Newtonsoft.Json; +using System.Text.Json.Serialization; using NJsonSchema.References; namespace NSwag @@ -15,27 +15,33 @@ namespace NSwag public class OpenApiLink : JsonReferenceBase, IJsonReference { /// Gets or sets the example's description. - [JsonProperty(PropertyName = "operationRef", DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate)] + [JsonPropertyName("operationRef")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public string OperationRef { get; set; } /// Gets or sets the example's description. - [JsonProperty(PropertyName = "operationId", DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate)] + [JsonPropertyName("operationId")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public string OperationId { get; set; } /// Gets or sets the example's value. - [JsonProperty(PropertyName = "parameters", DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate)] + [JsonPropertyName("parameters")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public IDictionary Parameters { get; } = new Dictionary(); /// Gets or sets the example's external value. - [JsonProperty(PropertyName = "requestBody", DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate)] + [JsonPropertyName("requestBody")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public object RequestBody { get; set; } /// Gets or sets the example's external value. - [JsonProperty(PropertyName = "description", DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate)] + [JsonPropertyName("description")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public string Description { get; set; } /// Gets or sets the server. - [JsonProperty(PropertyName = "server", DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate)] + [JsonPropertyName("server")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public OpenApiServer Server { get; set; } /// Gets the actual link, either this or the referenced example. diff --git a/src/NSwag.Core/OpenApiMediaType.cs b/src/NSwag.Core/OpenApiMediaType.cs index 5445bb53f8..3181d7ddc5 100644 --- a/src/NSwag.Core/OpenApiMediaType.cs +++ b/src/NSwag.Core/OpenApiMediaType.cs @@ -6,7 +6,7 @@ // Rico Suter, mail@rsuter.com //----------------------------------------------------------------------- -using Newtonsoft.Json; +using System.Text.Json.Serialization; using NJsonSchema; namespace NSwag @@ -21,7 +21,8 @@ public class OpenApiMediaType internal OpenApiRequestBody Parent { get; set; } /// Gets or sets the schema. - [JsonProperty(PropertyName = "schema", DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("schema")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public JsonSchema Schema { get => _schema; @@ -33,7 +34,8 @@ public JsonSchema Schema } /// Gets or sets the example. - [JsonProperty(PropertyName = "example", DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate)] + [JsonPropertyName("example")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public object Example { get => _example; @@ -45,11 +47,13 @@ public object Example } /// Gets or sets the headers (OpenAPI only). - [JsonProperty(PropertyName = "examples", DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("examples")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public IDictionary Examples { get; internal set; } = new Dictionary(); /// Gets or sets the example's value. - [JsonProperty(PropertyName = "encoding", DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate)] + [JsonPropertyName("encoding")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public IDictionary Encoding { get; } = new Dictionary(); } } \ No newline at end of file diff --git a/src/NSwag.Core/OpenApiOAuth2Flow.cs b/src/NSwag.Core/OpenApiOAuth2Flow.cs index edf7b0ad9d..e08a30c164 100644 --- a/src/NSwag.Core/OpenApiOAuth2Flow.cs +++ b/src/NSwag.Core/OpenApiOAuth2Flow.cs @@ -1,4 +1,4 @@ -//----------------------------------------------------------------------- +//----------------------------------------------------------------------- // // Copyright (c) Rico Suter. All rights reserved. // @@ -7,13 +7,13 @@ //----------------------------------------------------------------------- using System.Runtime.Serialization; -using Newtonsoft.Json; -using Newtonsoft.Json.Converters; +using System.Text.Json.Serialization; +using NSwag.Converters; namespace NSwag { /// Enumeration of the OAuth2 flows. - [JsonConverter(typeof(StringEnumConverter))] + [JsonConverter(typeof(EnumMemberStringEnumConverter))] public enum OpenApiOAuth2Flow { /// An undefined flow. @@ -36,4 +36,4 @@ public enum OpenApiOAuth2Flow [EnumMember(Value = "accessCode")] AccessCode } -} \ No newline at end of file +} diff --git a/src/NSwag.Core/OpenApiOAuthFlow.cs b/src/NSwag.Core/OpenApiOAuthFlow.cs index a387f0dbef..62d25a2b11 100644 --- a/src/NSwag.Core/OpenApiOAuthFlow.cs +++ b/src/NSwag.Core/OpenApiOAuthFlow.cs @@ -6,7 +6,7 @@ // Rico Suter, mail@rsuter.com //----------------------------------------------------------------------- -using Newtonsoft.Json; +using System.Text.Json.Serialization; namespace NSwag { @@ -14,19 +14,23 @@ namespace NSwag public class OpenApiOAuthFlow { /// Gets or sets the authorization URL to be used for this flow. - [JsonProperty(PropertyName = "authorizationUrl", DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate)] + [JsonPropertyName("authorizationUrl")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public string AuthorizationUrl { get; set; } /// Gets or sets the token URL to be used for this flow. - [JsonProperty(PropertyName = "tokenUrl", DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate)] + [JsonPropertyName("tokenUrl")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public string TokenUrl { get; set; } /// Gets or sets the token URL to be used for this flow. - [JsonProperty(PropertyName = "refreshUrl", DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate)] + [JsonPropertyName("refreshUrl")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public string RefreshUrl { get; set; } /// Gets the available scopes for the OAuth2 security scheme. - [JsonProperty(PropertyName = "scopes", DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate, Required = Required.DisallowNull)] + [JsonPropertyName("scopes")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public IDictionary Scopes { get; set; } = new Dictionary(); } } \ No newline at end of file diff --git a/src/NSwag.Core/OpenApiOAuthFlows.cs b/src/NSwag.Core/OpenApiOAuthFlows.cs index 0cad85e3fd..ae6166c612 100644 --- a/src/NSwag.Core/OpenApiOAuthFlows.cs +++ b/src/NSwag.Core/OpenApiOAuthFlows.cs @@ -6,7 +6,7 @@ // Rico Suter, mail@rsuter.com //----------------------------------------------------------------------- -using Newtonsoft.Json; +using System.Text.Json.Serialization; namespace NSwag { @@ -14,19 +14,23 @@ namespace NSwag public class OpenApiOAuthFlows { /// Gets or sets the configuration for the OAuth Implicit Code flow. - [JsonProperty(PropertyName = "implicit", DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate)] + [JsonPropertyName("implicit")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public OpenApiOAuthFlow Implicit { get; set; } /// Gets or sets the configuration for the OAuth Resource Owner Password flow. - [JsonProperty(PropertyName = "password", DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate)] + [JsonPropertyName("password")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public OpenApiOAuthFlow Password { get; set; } /// Gets or sets the configuration for the OAuth Client Credentials flow. - [JsonProperty(PropertyName = "clientCredentials", DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate)] + [JsonPropertyName("clientCredentials")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public OpenApiOAuthFlow ClientCredentials { get; set; } /// Gets or sets the configuration for the OAuth Authorization Code flow. - [JsonProperty(PropertyName = "authorizationCode", DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate)] + [JsonPropertyName("authorizationCode")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public OpenApiOAuthFlow AuthorizationCode { get; set; } } } \ No newline at end of file diff --git a/src/NSwag.Core/OpenApiOperation.cs b/src/NSwag.Core/OpenApiOperation.cs index a7879a8452..fe8f653810 100644 --- a/src/NSwag.Core/OpenApiOperation.cs +++ b/src/NSwag.Core/OpenApiOperation.cs @@ -9,8 +9,7 @@ using System.Collections.ObjectModel; using System.Collections.Specialized; using System.Runtime.CompilerServices; -using Newtonsoft.Json; -using Newtonsoft.Json.Converters; +using System.Text.Json.Serialization; using NJsonSchema; using NJsonSchema.Infrastructure; using NSwag.Collections; @@ -72,31 +71,45 @@ public OpenApiOperation() public OpenApiPathItem Parent { get; internal set; } /// Gets or sets the tags. - [JsonProperty(PropertyName = "tags", Order = 1, DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("tags")] + [JsonPropertyOrder(1)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public List Tags { get; set; } /// Gets or sets the summary of the operation. - [JsonProperty(PropertyName = "summary", Order = 2, DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("summary")] + [JsonPropertyOrder(2)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public string Summary { get; set; } /// Gets or sets the long description of the operation. - [JsonProperty(PropertyName = "description", Order = 3, DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("description")] + [JsonPropertyOrder(3)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public string Description { get; set; } /// Gets or sets the external documentation. - [JsonProperty(PropertyName = "externalDocs", Order = 4, DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("externalDocs")] + [JsonPropertyOrder(4)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public OpenApiExternalDocumentation ExternalDocumentation { get; set; } /// Gets or sets the operation ID (unique name). - [JsonProperty(PropertyName = "operationId", Order = 5, DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("operationId")] + [JsonPropertyOrder(5)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public string OperationId { get; set; } /// Gets or sets a list of MIME types the operation can consume. - [JsonProperty(PropertyName = "consumes", Order = 6, DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("consumes")] + [JsonPropertyOrder(6)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public List Consumes { get; set; } /// Gets or sets a list of MIME types the operation can produce. - [JsonProperty(PropertyName = "produces", Order = 7, DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("produces")] + [JsonPropertyOrder(7)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public List Produces { get; set; } /// Gets or sets the parameters. @@ -104,7 +117,9 @@ public OpenApiOperation() public IList Parameters { get; } /// Gets or sets the request body (OpenAPI only). - [JsonProperty(PropertyName = "requestBody", Order = 9, DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("requestBody")] + [JsonPropertyOrder(9)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public OpenApiRequestBody RequestBody { get => _requestBody; @@ -145,27 +160,39 @@ internal IEnumerable GetActualParameters() private readonly record struct NameKindPair(string Name, OpenApiParameterKind ParameterKind); /// Gets or sets the HTTP Status Code/Response pairs. - [JsonProperty(PropertyName = "responses", Order = 10, Required = Required.Always, DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("responses")] + [JsonPropertyOrder(10)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public IDictionary Responses => _responses; /// Gets or sets the schemes. - [JsonProperty(PropertyName = "schemes", Order = 11, DefaultValueHandling = DefaultValueHandling.Ignore, ItemConverterType = typeof(StringEnumConverter))] + [JsonPropertyName("schemes")] + [JsonPropertyOrder(11)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public List Schemes { get; set; } /// Gets or sets the callbacks (OpenAPI only). - [JsonProperty(PropertyName = "callbacks", Order = 12, DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate)] + [JsonPropertyName("callbacks")] + [JsonPropertyOrder(12)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public IDictionary Callbacks { get; set; } = new Dictionary(); /// Gets or sets a value indicating whether the operation is deprecated. - [JsonProperty(PropertyName = "deprecated", Order = 13, DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("deprecated")] + [JsonPropertyOrder(13)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool IsDeprecated { get; set; } /// Gets or sets a security description. - [JsonProperty(PropertyName = "security", Order = 14, DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("security")] + [JsonPropertyOrder(14)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public ICollection Security { get; set; } /// Gets or sets the servers (OpenAPI only). - [JsonProperty(PropertyName = "servers", Order = 15, DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate)] + [JsonPropertyName("servers")] + [JsonPropertyOrder(15)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public ICollection Servers { get; set; } = []; /// Gets the list of MIME types the operation can consume, either from the operation or from the . @@ -285,16 +312,14 @@ public void TryAddConsumes(string mimeType) } /// Gets or sets the parameters. - [JsonProperty(PropertyName = "parameters", Order = 8, DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("parameters")] + [JsonPropertyOrder(8)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + [JsonInclude] internal IList ParametersRaw { get { - if (JsonSchemaSerialization.IsWriting) - { - return Parameters; - } - if (JsonSchemaSerialization.CurrentSchemaType != SchemaType.Swagger2) { return Parameters.Where(p => p.Kind != OpenApiParameterKind.Body).ToList(); @@ -304,6 +329,17 @@ internal IList ParametersRaw return Parameters; } } + set + { + Parameters.Clear(); + if (value != null) + { + foreach (var parameter in value) + { + Parameters.Add(parameter); + } + } + } } internal void UpdateRequestBody(OpenApiParameter parameter) diff --git a/src/NSwag.Core/OpenApiParameter.cs b/src/NSwag.Core/OpenApiParameter.cs index 2eb81c69c3..e22aabd0c3 100644 --- a/src/NSwag.Core/OpenApiParameter.cs +++ b/src/NSwag.Core/OpenApiParameter.cs @@ -6,10 +6,9 @@ // Rico Suter, mail@rsuter.com //----------------------------------------------------------------------- +using System.Text.Json.Serialization; using System.Text.RegularExpressions; -using Newtonsoft.Json; using NJsonSchema; - namespace NSwag { /// Describes an operation parameter. @@ -29,7 +28,8 @@ public class OpenApiParameter : JsonSchema [JsonIgnore] internal OpenApiOperation ParentOperation => Parent as OpenApiOperation; /// Gets or sets the name. - [JsonProperty(PropertyName = "name", DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate)] + [JsonPropertyName("name")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public string Name { get => _name; @@ -41,11 +41,13 @@ public string Name } /// Gets or sets a original name property x-originalName which is often used in code generation (default: null). - [JsonProperty(PropertyName = "x-originalName", DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate)] + [JsonPropertyName("x-originalName")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public string OriginalName { get; set; } /// Gets or sets the kind of the parameter. - [JsonProperty(PropertyName = "in", DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("in")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public OpenApiParameterKind Kind { get => _kind; @@ -57,7 +59,8 @@ public OpenApiParameterKind Kind } /// Gets or sets the style of the parameter (OpenAPI only). - [JsonProperty(PropertyName = "style", DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("style")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public OpenApiParameterStyle Style { get => _style; @@ -69,7 +72,8 @@ public OpenApiParameterStyle Style } /// Gets or sets the explode setting for the parameter (OpenAPI only). - [JsonProperty(PropertyName = "explode", DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("explode")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool? Explode { get => _explode; @@ -81,7 +85,7 @@ public bool? Explode } /// Gets or sets a value indicating whether the parameter is required (default: false). - [JsonProperty(PropertyName = "required", DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate)] + [JsonIgnore] public bool IsRequired { get => _isRequired; @@ -93,11 +97,13 @@ public bool IsRequired } /// Gets or sets a value indicating whether passing empty-valued parameters is allowed (default: false). - [JsonProperty(PropertyName = "allowEmptyValue", DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate)] + [JsonPropertyName("allowEmptyValue")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool AllowEmptyValue { get; set; } /// Gets or sets the description. - [JsonProperty("description", DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate)] + [JsonPropertyName("description")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public override string Description { get => base.Description; @@ -113,11 +119,13 @@ public override string Description public OpenApiParameter ActualParameter => Reference is OpenApiParameter ? (OpenApiParameter)Reference : this; /// Gets or sets the format of the array if type array is used. - [JsonProperty(PropertyName = "collectionFormat", DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("collectionFormat")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public OpenApiParameterCollectionFormat CollectionFormat { get; set; } /// Gets or sets the examples (OpenAPI only). - [JsonProperty(PropertyName = "examples", DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("examples")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public IDictionary Examples { get => _examples; @@ -129,7 +137,8 @@ public IDictionary Examples } /// Gets or sets the schema which is only available when == body. - [JsonProperty(PropertyName = "schema", DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate)] + [JsonPropertyName("schema")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public JsonSchema Schema { get => _schema; @@ -141,11 +150,13 @@ public JsonSchema Schema } /// Gets or sets the custom schema which is used when != body. - [JsonProperty(PropertyName = "x-schema", DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate)] + [JsonPropertyName("x-schema")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public JsonSchema CustomSchema { get; set; } /// Gets or sets the name. - [JsonProperty(PropertyName = "x-position", DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate)] + [JsonPropertyName("x-position")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public int? Position { get => _position; diff --git a/src/NSwag.Core/OpenApiParameterCollectionFormat.cs b/src/NSwag.Core/OpenApiParameterCollectionFormat.cs index 2bba2271df..20b1db1f97 100644 --- a/src/NSwag.Core/OpenApiParameterCollectionFormat.cs +++ b/src/NSwag.Core/OpenApiParameterCollectionFormat.cs @@ -7,13 +7,13 @@ //----------------------------------------------------------------------- using System.Runtime.Serialization; -using Newtonsoft.Json; -using Newtonsoft.Json.Converters; +using System.Text.Json.Serialization; +using NSwag.Converters; namespace NSwag { /// Defines the collectionFormat of a parameter. - [JsonConverter(typeof(StringEnumConverter))] + [JsonConverter(typeof(EnumMemberStringEnumConverter))] public enum OpenApiParameterCollectionFormat { /// An undefined format. @@ -40,4 +40,4 @@ public enum OpenApiParameterCollectionFormat [EnumMember(Value = "multi")] Multi } -} \ No newline at end of file +} diff --git a/src/NSwag.Core/OpenApiParameterKind.cs b/src/NSwag.Core/OpenApiParameterKind.cs index f4e3c69cc9..2ee25e5736 100644 --- a/src/NSwag.Core/OpenApiParameterKind.cs +++ b/src/NSwag.Core/OpenApiParameterKind.cs @@ -1,4 +1,4 @@ -//----------------------------------------------------------------------- +//----------------------------------------------------------------------- // // Copyright (c) Rico Suter. All rights reserved. // @@ -7,13 +7,13 @@ //----------------------------------------------------------------------- using System.Runtime.Serialization; -using Newtonsoft.Json; -using Newtonsoft.Json.Converters; +using System.Text.Json.Serialization; +using NSwag.Converters; namespace NSwag { /// Enumeration of the parameter kinds. - [JsonConverter(typeof(StringEnumConverter))] + [JsonConverter(typeof(EnumMemberStringEnumConverter))] public enum OpenApiParameterKind { /// An undefined kind. @@ -48,4 +48,4 @@ public enum OpenApiParameterKind [EnumMember(Value = "cookie")] Cookie, } -} \ No newline at end of file +} diff --git a/src/NSwag.Core/OpenApiParameterStyle.cs b/src/NSwag.Core/OpenApiParameterStyle.cs index e2f85cf682..a6ef73057c 100644 --- a/src/NSwag.Core/OpenApiParameterStyle.cs +++ b/src/NSwag.Core/OpenApiParameterStyle.cs @@ -1,11 +1,11 @@ -using System.Runtime.Serialization; -using Newtonsoft.Json; -using Newtonsoft.Json.Converters; +using System.Runtime.Serialization; +using System.Text.Json.Serialization; +using NSwag.Converters; namespace NSwag { /// Enumeration of the parameter kinds. - [JsonConverter(typeof(StringEnumConverter))] + [JsonConverter(typeof(EnumMemberStringEnumConverter))] public enum OpenApiParameterStyle { /// An undefined kind. @@ -54,4 +54,4 @@ public enum OpenApiParameterStyle [EnumMember(Value = "deepObject")] DeepObject, } -} \ No newline at end of file +} diff --git a/src/NSwag.Core/OpenApiPathItem.cs b/src/NSwag.Core/OpenApiPathItem.cs index 11bce0af18..6a1804f611 100644 --- a/src/NSwag.Core/OpenApiPathItem.cs +++ b/src/NSwag.Core/OpenApiPathItem.cs @@ -8,7 +8,9 @@ using System.Collections.ObjectModel; using System.Collections.Specialized; -using Newtonsoft.Json; +using System.Text.Json; +using System.Text.Json.Nodes; +using System.Text.Json.Serialization; using NJsonSchema.References; using NSwag.Collections; @@ -45,24 +47,28 @@ public OpenApiPathItem() public OpenApiPathItem ActualPathItem => Reference ?? this; /// Gets or sets the summary (OpenApi only). - [JsonProperty(PropertyName = "summary", DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate)] + [JsonPropertyName("summary")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public string Summary { get; set; } /// Gets or sets the description (OpenApi only). - [JsonProperty(PropertyName = "description", DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate)] + [JsonPropertyName("description")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public string Description { get; set; } /// Gets or sets the servers (OpenAPI only). - [JsonProperty(PropertyName = "servers", DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate)] + [JsonPropertyName("servers")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public ICollection Servers { get; set; } = []; /// Gets or sets the parameters. - [JsonProperty(PropertyName = "parameters", DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("parameters")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public ICollection Parameters { get; set; } = []; /// Gets or sets the extension data (i.e. additional properties which are not directly defined by the JSON object). [JsonExtensionData] - public IDictionary ExtensionData { get; set; } + public IDictionary ExtensionData { get; set; } #region Implementation of IJsonReferenceBase and IJsonReference @@ -73,7 +79,8 @@ public OpenApiPathItem() public string DocumentPath { get; set; } /// Gets or sets the type reference path ($ref). - [JsonProperty("$ref", DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate)] + [JsonPropertyName("$ref")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] string IJsonReferenceBase.ReferencePath { get; set; } /// Gets or sets the referenced object. @@ -108,106 +115,104 @@ IJsonReference IJsonReferenceBase.Reference #endregion // Needed to convert dictionary keys to lower case - internal sealed class OpenApiPathItemConverter : JsonConverter + internal sealed class OpenApiPathItemConverter : JsonConverter { - public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + public override OpenApiPathItem Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { - var operations = (OpenApiPathItem)value; - writer.WriteStartObject(); - - if (operations.Summary != null) - { - writer.WritePropertyName("summary"); - serializer.Serialize(writer, operations.Summary); - } - - if (operations.Description != null) - { - writer.WritePropertyName("description"); - serializer.Serialize(writer, operations.Description); - } - - if (operations.ExtensionData != null) + if (reader.TokenType == JsonTokenType.Null) { - foreach (var tuple in operations.ExtensionData) - { - writer.WritePropertyName(tuple.Key); - serializer.Serialize(writer, tuple.Value); - } - } - - if (operations.Parameters != null && operations.Parameters.Count > 0) - { - writer.WritePropertyName("parameters"); - serializer.Serialize(writer, operations.Parameters); - } - - if (operations.Servers != null && operations.Servers.Count > 0) - { - writer.WritePropertyName("servers"); - serializer.Serialize(writer, operations.Servers); - } - - foreach (var pair in operations) - { - writer.WritePropertyName(pair.Key.ToString().ToLowerInvariant()); - serializer.Serialize(writer, pair.Value); + return null; } - writer.WriteEndObject(); - } - - public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) - { - if (reader.TokenType == JsonToken.Null) + if (reader.TokenType != JsonTokenType.StartObject) { - return null; + throw new JsonException("Expected StartObject token."); } - var operations = new OpenApiPathItem(); - while (reader.Read() && reader.TokenType == JsonToken.PropertyName) + var pathItem = new OpenApiPathItem(); + while (reader.Read() && reader.TokenType == JsonTokenType.PropertyName) { - var propertyName = reader.Value.ToString(); + var propertyName = reader.GetString(); reader.Read(); if (propertyName == "summary") { - operations.Summary = serializer.Deserialize(reader); + pathItem.Summary = reader.GetString(); } else if (propertyName == "description") { - operations.Description = serializer.Deserialize(reader); + pathItem.Description = reader.GetString(); } else if (propertyName == "parameters") { - operations.Parameters = serializer.Deserialize>(reader); + pathItem.Parameters = JsonSerializer.Deserialize>(ref reader, options); } else if (propertyName == "servers") { - operations.Servers = serializer.Deserialize>(reader); + pathItem.Servers = JsonSerializer.Deserialize>(ref reader, options); } else if (propertyName.StartsWith("x-", StringComparison.OrdinalIgnoreCase)) { - operations.ExtensionData ??= new Dictionary(); - operations.ExtensionData[propertyName] = serializer.Deserialize(reader); + pathItem.ExtensionData ??= new Dictionary(); + pathItem.ExtensionData[propertyName] = JsonNode.Parse(ref reader); } else if (propertyName.Contains("$ref")) { - string refPath = serializer.Deserialize(reader).ToString(); - ((IJsonReferenceBase)operations).ReferencePath = refPath; + var referencePath = reader.GetString(); + ((IJsonReferenceBase)pathItem).ReferencePath = referencePath; } else { - var value = serializer.Deserialize(reader); - operations.Add(propertyName, value); + var operation = JsonSerializer.Deserialize(ref reader, options); + pathItem.Add(propertyName, operation); } } - return operations; + + return pathItem; } - public override bool CanConvert(Type objectType) + public override void Write(Utf8JsonWriter writer, OpenApiPathItem value, JsonSerializerOptions options) { - return objectType == typeof(OpenApiPathItem); + writer.WriteStartObject(); + + if (value.Summary != null) + { + writer.WriteString("summary", value.Summary); + } + + if (value.Description != null) + { + writer.WriteString("description", value.Description); + } + + if (value.ExtensionData != null) + { + foreach (var entry in value.ExtensionData) + { + writer.WritePropertyName(entry.Key); + JsonSerializer.Serialize(writer, entry.Value, options); + } + } + + if (value.Parameters != null && value.Parameters.Count > 0) + { + writer.WritePropertyName("parameters"); + JsonSerializer.Serialize(writer, value.Parameters, options); + } + + if (value.Servers != null && value.Servers.Count > 0) + { + writer.WritePropertyName("servers"); + JsonSerializer.Serialize(writer, value.Servers, options); + } + + foreach (var pair in value) + { + writer.WritePropertyName(pair.Key.ToLowerInvariant()); + JsonSerializer.Serialize(writer, pair.Value, options); + } + + writer.WriteEndObject(); } } } diff --git a/src/NSwag.Core/OpenApiRequestBody.cs b/src/NSwag.Core/OpenApiRequestBody.cs index a6efe8bacd..88c007a226 100644 --- a/src/NSwag.Core/OpenApiRequestBody.cs +++ b/src/NSwag.Core/OpenApiRequestBody.cs @@ -7,7 +7,7 @@ //----------------------------------------------------------------------- using System.Collections.Specialized; -using Newtonsoft.Json; +using System.Text.Json.Serialization; using NJsonSchema.References; using NSwag.Collections; @@ -68,7 +68,8 @@ public override OpenApiRequestBody Reference public OpenApiRequestBody ActualRequestBody => Reference ?? this; /// Gets or sets the name. - [JsonProperty(PropertyName = "x-name", DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate)] + [JsonPropertyName("x-name")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public string Name { get => _name; @@ -80,7 +81,8 @@ public string Name } /// Gets or sets the description. - [JsonProperty(PropertyName = "description", DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate)] + [JsonPropertyName("description")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public string Description { get => _description; @@ -92,11 +94,13 @@ public string Description } /// Gets or sets the descriptions of potential response payloads (OpenApi only). - [JsonProperty(PropertyName = "content", DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("content")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public IDictionary Content => _content; /// Gets or sets the example's external value. - [JsonProperty(PropertyName = "required", DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate)] + [JsonPropertyName("required")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool IsRequired { get => _isRequired; @@ -108,7 +112,8 @@ public bool IsRequired } /// Gets or sets the name. - [JsonProperty(PropertyName = "x-position", DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate)] + [JsonPropertyName("x-position")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public int? Position { get => _position; diff --git a/src/NSwag.Core/OpenApiResponse.cs b/src/NSwag.Core/OpenApiResponse.cs index cfd6cae394..03ede3e0c7 100644 --- a/src/NSwag.Core/OpenApiResponse.cs +++ b/src/NSwag.Core/OpenApiResponse.cs @@ -7,8 +7,8 @@ //----------------------------------------------------------------------- using System.Runtime.CompilerServices; +using System.Text.Json.Serialization; using System.Text.RegularExpressions; -using Newtonsoft.Json; using NJsonSchema; using NJsonSchema.References; @@ -34,32 +34,44 @@ public class OpenApiResponse : JsonReferenceBase, IJsonReferenc public OpenApiResponse ActualResponse => Reference ?? this; /// Gets or sets the response's description. - [JsonProperty(PropertyName = "description", Order = 1)] + [JsonPropertyName("description")] + [JsonPropertyOrder(1)] public string Description { get; set; } = ""; /// Gets or sets the headers. - [JsonProperty(PropertyName = "headers", Order = 3, DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("headers")] + [JsonPropertyOrder(3)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public OpenApiHeaders Headers { get; } = []; /// Sets a value indicating whether the response can be null (use IsNullable() to get a parameter's nullability). /// The Swagger spec does not support null in schemas, see https://github.com/OAI/OpenAPI-Specification/issues/229 - [JsonProperty(PropertyName = "x-nullable", DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate)] + [JsonPropertyName("x-nullable")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool? IsNullableRaw { internal get; set; } /// Gets or sets the expected child schemas of the base schema (can be used for generating enhanced typings/documentation). - [JsonProperty(PropertyName = "x-expectedSchemas", Order = 7, DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate)] + [JsonPropertyName("x-expectedSchemas")] + [JsonPropertyOrder(7)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public ICollection ExpectedSchemas { get; set; } /// Gets or sets the descriptions of potential response payloads (OpenApi only). - [JsonProperty(PropertyName = "content", Order = 4, DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("content")] + [JsonPropertyOrder(4)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public IDictionary Content => _content; /// Gets or sets the links that can be followed from the response (OpenApi only). - [JsonProperty(PropertyName = "links", Order = 5, DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("links")] + [JsonPropertyOrder(5)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public IDictionary Links { get; } = new Dictionary(); /// Gets or sets the response schema (Swagger only). - [JsonProperty(PropertyName = "schema", Order = 2, DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("schema")] + [JsonPropertyOrder(2)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public JsonSchema Schema { get => _content.FirstOrDefault(static c => c.Value.Schema != null).Value?.Schema; @@ -67,7 +79,9 @@ public JsonSchema Schema } /// Gets or sets the headers (Swagger only). - [JsonProperty(PropertyName = "examples", Order = 6, DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("examples")] + [JsonPropertyOrder(6)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public object Examples { get => _content.FirstOrDefault(static c => c.Value.Example != null).Value?.Example; diff --git a/src/NSwag.Core/OpenApiSchema.cs b/src/NSwag.Core/OpenApiSchema.cs index a5c75a218e..d59b1d5612 100644 --- a/src/NSwag.Core/OpenApiSchema.cs +++ b/src/NSwag.Core/OpenApiSchema.cs @@ -1,4 +1,4 @@ -//----------------------------------------------------------------------- +//----------------------------------------------------------------------- // // Copyright (c) Rico Suter. All rights reserved. // @@ -7,10 +7,13 @@ //----------------------------------------------------------------------- using System.Runtime.Serialization; +using System.Text.Json.Serialization; +using NSwag.Converters; namespace NSwag { /// The enumeration of Swagger protocol schemes. + [JsonConverter(typeof(EnumMemberStringEnumConverter))] public enum OpenApiSchema { /// An undefined schema. @@ -33,4 +36,4 @@ public enum OpenApiSchema [EnumMember(Value = "wss")] Wss } -} \ No newline at end of file +} diff --git a/src/NSwag.Core/OpenApiSecurityApiKeyLocation.cs b/src/NSwag.Core/OpenApiSecurityApiKeyLocation.cs index 8a1a10988d..e3e36f3dd2 100644 --- a/src/NSwag.Core/OpenApiSecurityApiKeyLocation.cs +++ b/src/NSwag.Core/OpenApiSecurityApiKeyLocation.cs @@ -7,10 +7,13 @@ //----------------------------------------------------------------------- using System.Runtime.Serialization; +using System.Text.Json.Serialization; +using NSwag.Converters; namespace NSwag { /// Specifies the location of the API Key. + [JsonConverter(typeof(EnumMemberStringEnumConverter))] public enum OpenApiSecurityApiKeyLocation { /// The API key kind is not defined. @@ -28,4 +31,4 @@ public enum OpenApiSecurityApiKeyLocation [EnumMember(Value = "cookie")] Cookie } -} \ No newline at end of file +} diff --git a/src/NSwag.Core/OpenApiSecurityScheme.cs b/src/NSwag.Core/OpenApiSecurityScheme.cs index 50f8206fde..ac78ca93cb 100644 --- a/src/NSwag.Core/OpenApiSecurityScheme.cs +++ b/src/NSwag.Core/OpenApiSecurityScheme.cs @@ -6,8 +6,7 @@ // Rico Suter, mail@rsuter.com //----------------------------------------------------------------------- -using Newtonsoft.Json; -using Newtonsoft.Json.Converters; +using System.Text.Json.Serialization; using NJsonSchema; using NJsonSchema.Infrastructure; @@ -39,37 +38,46 @@ public OpenApiSecuritySchemeType Type } /// Gets or sets the short description for security scheme. - [JsonProperty(PropertyName = "description", DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate)] + [JsonPropertyName("description")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public string Description { get; set; } /// Gets or sets the name of the header or query parameter to be used to transmit the API key. - [JsonProperty(PropertyName = "name", DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate)] + [JsonPropertyName("name")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public string Name { get; set; } /// Gets or sets the type of the API key. - [JsonProperty(PropertyName = "in", DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate)] - [JsonConverter(typeof(StringEnumConverter))] + [JsonPropertyName("in")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public OpenApiSecurityApiKeyLocation In { get; set; } /// Gets or sets name of the HTTP Authorization scheme to be used in the Authorization header as defined in RFC7235 (OpenAPI only). - [JsonProperty(PropertyName = "scheme", DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate)] + [JsonPropertyName("scheme")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public string Scheme { get; set; } /// Gets or sets a hint to the client to identify how the bearer token is formatted. Bearer tokens are /// usually generated by an authorization server, so this information is primarily for documentation purposes (OpenAPI only). - [JsonProperty(PropertyName = "bearerFormat", DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate)] + [JsonPropertyName("bearerFormat")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public string BearerFormat { get; set; } /// Gets or sets the OpenId Connect URL to discover OAuth2 configuration values (OpenAPI only). - [JsonProperty(PropertyName = "openIdConnectUrl", DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate)] + [JsonPropertyName("openIdConnectUrl")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public string OpenIdConnectUrl { get; set; } /// Gets or sets the configuration information for the supported flow types (OpenAPI only). - [JsonProperty(PropertyName = "flows", DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate)] + [JsonPropertyName("flows")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public OpenApiOAuthFlows Flows { get; set; } - [JsonProperty(PropertyName = "type", Required = Required.Always, DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate, Order = -10)] - [JsonConverter(typeof(StringEnumConverter))] + [JsonPropertyName("type")] + [JsonRequired] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + [JsonPropertyOrder(-10)] + [JsonInclude] internal OpenApiSecuritySchemeType TypeRaw { get @@ -107,7 +115,8 @@ internal OpenApiSecuritySchemeType TypeRaw // Swagger only: /// Gets or sets the used by the OAuth2 security scheme (Swagger only). - [JsonProperty(PropertyName = "flow", DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate)] + [JsonPropertyName("flow")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public OpenApiOAuth2Flow Flow { get @@ -141,7 +150,8 @@ public OpenApiOAuth2Flow Flow } /// Gets or sets the authorization URL to be used for this flow (Swagger only). - [JsonProperty(PropertyName = "authorizationUrl", DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate)] + [JsonPropertyName("authorizationUrl")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public string AuthorizationUrl { get => GetFlow(Flow)?.AuthorizationUrl; @@ -149,7 +159,8 @@ public string AuthorizationUrl } /// Gets or sets the token URL to be used for this flow (Swagger only). - [JsonProperty(PropertyName = "tokenUrl", DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate)] + [JsonPropertyName("tokenUrl")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public string TokenUrl { get => GetFlow(Flow)?.TokenUrl; @@ -157,7 +168,8 @@ public string TokenUrl } /// Gets the available scopes for the OAuth2 security scheme (Swagger only). - [JsonProperty(PropertyName = "scopes", DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate)] + [JsonPropertyName("scopes")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public IDictionary Scopes { get => GetFlow(Flow)?.Scopes; diff --git a/src/NSwag.Core/OpenApiSecuritySchemeType.cs b/src/NSwag.Core/OpenApiSecuritySchemeType.cs index f47f3adf47..e3919e7a35 100644 --- a/src/NSwag.Core/OpenApiSecuritySchemeType.cs +++ b/src/NSwag.Core/OpenApiSecuritySchemeType.cs @@ -7,10 +7,13 @@ //----------------------------------------------------------------------- using System.Runtime.Serialization; +using System.Text.Json.Serialization; +using NSwag.Converters; namespace NSwag { /// + [JsonConverter(typeof(EnumMemberStringEnumConverter))] public enum OpenApiSecuritySchemeType { /// The security scheme is not defined. @@ -36,4 +39,4 @@ public enum OpenApiSecuritySchemeType [EnumMember(Value = "openIdConnect")] OpenIdConnect, } -} \ No newline at end of file +} diff --git a/src/NSwag.Core/OpenApiServer.cs b/src/NSwag.Core/OpenApiServer.cs index c48ee87ef1..5159597163 100644 --- a/src/NSwag.Core/OpenApiServer.cs +++ b/src/NSwag.Core/OpenApiServer.cs @@ -6,7 +6,7 @@ // Rico Suter, mail@rsuter.com //----------------------------------------------------------------------- -using Newtonsoft.Json; +using System.Text.Json.Serialization; namespace NSwag { @@ -14,15 +14,18 @@ namespace NSwag public class OpenApiServer { /// Gets or sets the URL of the server. - [JsonProperty(PropertyName = "url", DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate)] + [JsonPropertyName("url")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public string Url { get; set; } /// Gets or sets the description of the server. - [JsonProperty(PropertyName = "description", DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate)] + [JsonPropertyName("description")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public string Description { get; set; } /// Gets or sets the variables of the server. - [JsonProperty(PropertyName = "variables", DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate)] + [JsonPropertyName("variables")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public IDictionary Variables { get; } = new Dictionary(); /// Gets a value indicating whether the server description is valid. diff --git a/src/NSwag.Core/OpenApiServerVariable.cs b/src/NSwag.Core/OpenApiServerVariable.cs index 436c6de2eb..c22a7d9087 100644 --- a/src/NSwag.Core/OpenApiServerVariable.cs +++ b/src/NSwag.Core/OpenApiServerVariable.cs @@ -6,7 +6,7 @@ // Rico Suter, mail@rsuter.com //----------------------------------------------------------------------- -using Newtonsoft.Json; +using System.Text.Json.Serialization; namespace NSwag { @@ -14,15 +14,18 @@ namespace NSwag public class OpenApiServerVariable { /// Gets or sets the enum of the server. - [JsonProperty(PropertyName = "enum", DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate)] + [JsonPropertyName("enum")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public ICollection Enum { get; } = []; /// Gets or sets the variables of the server. - [JsonProperty(PropertyName = "default", DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate)] + [JsonPropertyName("default")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public string Default { get; set; } /// Gets or sets the description of the server. - [JsonProperty(PropertyName = "description", DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate)] + [JsonPropertyName("description")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public string Description { get; set; } } } \ No newline at end of file diff --git a/src/NSwag.Core/OpenApiTag.cs b/src/NSwag.Core/OpenApiTag.cs index fed037f497..a7613a06a3 100644 --- a/src/NSwag.Core/OpenApiTag.cs +++ b/src/NSwag.Core/OpenApiTag.cs @@ -6,7 +6,7 @@ // Rico Suter, mail@rsuter.com //----------------------------------------------------------------------- -using Newtonsoft.Json; +using System.Text.Json.Serialization; using NJsonSchema; namespace NSwag @@ -15,15 +15,18 @@ namespace NSwag public class OpenApiTag : JsonExtensionObject { /// Gets or sets the name. - [JsonProperty(PropertyName = "name", DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("name")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public string Name { get; set; } /// Gets or sets the description. - [JsonProperty(PropertyName = "description", DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("description")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public string Description { get; set; } /// Gets or sets the external documentation. - [JsonProperty(PropertyName = "externalDocs", DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("externalDocs")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public OpenApiExternalDocumentation ExternalDocumentation { get; set; } } } \ No newline at end of file diff --git a/src/NSwag.Generation.AspNetCore.Tests/NSwag.Generation.AspNetCore.Tests.csproj b/src/NSwag.Generation.AspNetCore.Tests/NSwag.Generation.AspNetCore.Tests.csproj index 883039a182..b462678fb0 100644 --- a/src/NSwag.Generation.AspNetCore.Tests/NSwag.Generation.AspNetCore.Tests.csproj +++ b/src/NSwag.Generation.AspNetCore.Tests/NSwag.Generation.AspNetCore.Tests.csproj @@ -24,6 +24,7 @@ + diff --git a/src/NSwag.Generation.AspNetCore.Tests/Parameters/QueryParametersTests.cs b/src/NSwag.Generation.AspNetCore.Tests/Parameters/QueryParametersTests.cs index f0ff2f19b2..056091fe95 100644 --- a/src/NSwag.Generation.AspNetCore.Tests/Parameters/QueryParametersTests.cs +++ b/src/NSwag.Generation.AspNetCore.Tests/Parameters/QueryParametersTests.cs @@ -1,5 +1,4 @@ -using Newtonsoft.Json.Linq; -using NJsonSchema; +using NJsonSchema; using NJsonSchema.NewtonsoftJson.Generation; using NSwag.Generation.AspNetCore.Tests.Web.Controllers.Parameters; @@ -31,7 +30,7 @@ public async Task When_complex_query_parameters_are_nullable_and_set_to_null_the Assert.Equal(2, operation.ActualParameters.Count); Assert.Equal("Bar.", operation.ActualParameters[0].Description); - Assert.Equal(JToken.Parse("42"), operation.ActualParameters[0].Example); + Assert.Equal("42", operation.ActualParameters[0].Example?.ToString()); Assert.Equal("Baz.", operation.ActualParameters[operation.ActualParameters.Count - 1].Description); } diff --git a/src/NSwag.Generation.AspNetCore.Tests/Parameters/Snapshots/FormDataTests.WhenOperationHasFormDataComplex_ThenItIsInRequestBody.verified.txt b/src/NSwag.Generation.AspNetCore.Tests/Parameters/Snapshots/FormDataTests.WhenOperationHasFormDataComplex_ThenItIsInRequestBody.verified.txt index e35bd969e4..db8e8a9c33 100644 --- a/src/NSwag.Generation.AspNetCore.Tests/Parameters/Snapshots/FormDataTests.WhenOperationHasFormDataComplex_ThenItIsInRequestBody.verified.txt +++ b/src/NSwag.Generation.AspNetCore.Tests/Parameters/Snapshots/FormDataTests.WhenOperationHasFormDataComplex_ThenItIsInRequestBody.verified.txt @@ -95,11 +95,11 @@ { "name": "caseId", "in": "path", - "required": true, "schema": { "type": "string" }, - "x-position": 1 + "x-position": 1, + "required": true } ], "requestBody": { diff --git a/src/NSwag.Generation.AspNetCore.Tests/Parameters/Snapshots/FormDataTests.WhenOperationHasFormDataFile_ThenItIsInRequestBody.verified.txt b/src/NSwag.Generation.AspNetCore.Tests/Parameters/Snapshots/FormDataTests.WhenOperationHasFormDataFile_ThenItIsInRequestBody.verified.txt index e35bd969e4..db8e8a9c33 100644 --- a/src/NSwag.Generation.AspNetCore.Tests/Parameters/Snapshots/FormDataTests.WhenOperationHasFormDataFile_ThenItIsInRequestBody.verified.txt +++ b/src/NSwag.Generation.AspNetCore.Tests/Parameters/Snapshots/FormDataTests.WhenOperationHasFormDataFile_ThenItIsInRequestBody.verified.txt @@ -95,11 +95,11 @@ { "name": "caseId", "in": "path", - "required": true, "schema": { "type": "string" }, - "x-position": 1 + "x-position": 1, + "required": true } ], "requestBody": { diff --git a/src/NSwag.Generation.AspNetCore/AspNetCoreOpenApiDocumentGenerator.cs b/src/NSwag.Generation.AspNetCore/AspNetCoreOpenApiDocumentGenerator.cs index 3b0d215486..402ee6da8e 100644 --- a/src/NSwag.Generation.AspNetCore/AspNetCoreOpenApiDocumentGenerator.cs +++ b/src/NSwag.Generation.AspNetCore/AspNetCoreOpenApiDocumentGenerator.cs @@ -22,7 +22,6 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; using Namotion.Reflection; -using Newtonsoft.Json; using NJsonSchema; using NSwag.Generation.Processors; using NSwag.Generation.Processors.Contexts; @@ -52,48 +51,29 @@ public Task GenerateAsync(object serviceProvider) return GenerateAsync(apiDescriptionGroupCollectionProvider.ApiDescriptionGroups); } - /// Loads the from the given service provider. + /// Loads Newtonsoft.Json serializer settings from the service provider. /// The service provider. - /// The settings. - public static JsonSerializerSettings GetJsonSerializerSettings(IServiceProvider serviceProvider) + /// The settings as an object, or null if Newtonsoft.Json is not configured. + /// Use NSwag.Generation.NewtonsoftJson package for typed Newtonsoft support. + [Obsolete("Use NSwag.Generation.NewtonsoftJson.NewtonsoftJsonSettingsResolver.GetJsonSerializerSettings() instead.")] + public static object GetJsonSerializerSettings(IServiceProvider serviceProvider) { - static dynamic GetJsonOptionsWithReflection(IServiceProvider sp) + try { - try - { - // Try to load ASP.NET Core 3 options - var optionsAssembly = Assembly.Load(new AssemblyName("Microsoft.AspNetCore.Mvc.NewtonsoftJson")); - var optionsType = typeof(IOptions<>).MakeGenericType(optionsAssembly.GetType("Microsoft.AspNetCore.Mvc.MvcNewtonsoftJsonOptions", true)); - return sp?.GetService(optionsType); - } - catch + var optionsAssembly = Assembly.Load(new AssemblyName("Microsoft.AspNetCore.Mvc.NewtonsoftJson")); + var iOptionsType = Type.GetType("Microsoft.Extensions.Options.IOptions`1, Microsoft.Extensions.Options"); + if (iOptionsType == null) { - // Newtonsoft.JSON not available, use GetSystemTextJsonSettings() return null; } - } -#if NETCOREAPP3_1_OR_GREATER - dynamic options = GetJsonOptionsWithReflection(serviceProvider); -#else - object options = null; - try - { - options = new Func(() => serviceProvider?.GetRequiredService(typeof(IOptions)))(); - } - catch - { - options = GetJsonOptionsWithReflection(serviceProvider); - } -#endif - - try - { - return (JsonSerializerSettings)((dynamic)options.GetType().GetProperty("Value")?.GetValue(options))?.SerializerSettings; + var optionsType = iOptionsType.MakeGenericType( + optionsAssembly.GetType("Microsoft.AspNetCore.Mvc.MvcNewtonsoftJsonOptions", true)); + var options = serviceProvider?.GetService(optionsType); + return ((dynamic)options?.GetType().GetProperty("Value")?.GetValue(options))?.SerializerSettings; } catch { - // Newtonsoft.JSON not available, use GetSystemTextJsonSettings() return null; } } @@ -241,7 +221,7 @@ private List GenerateApiGroups( openApiOperationMetadata.GetType().GetMethod("SerializeAsV3") .Invoke(openApiOperationMetadata, [openApiJsonWriter]); - operation = JsonConvert.DeserializeObject(stringBuilder.ToString()); + operation = System.Text.Json.JsonSerializer.Deserialize(stringBuilder.ToString()); operation.Parameters.Clear(); // clear because parameters are added by the generator } #endif diff --git a/src/NSwag.Generation.NewtonsoftJson/NSwag.Generation.NewtonsoftJson.csproj b/src/NSwag.Generation.NewtonsoftJson/NSwag.Generation.NewtonsoftJson.csproj new file mode 100644 index 0000000000..61fc75824a --- /dev/null +++ b/src/NSwag.Generation.NewtonsoftJson/NSwag.Generation.NewtonsoftJson.csproj @@ -0,0 +1,20 @@ + + + netstandard2.0;net462;net8.0 + true + + + + + + + + + + + + + + + + diff --git a/src/NSwag.Generation.NewtonsoftJson/NewtonsoftJsonOpenApiGeneratorSettingsExtensions.cs b/src/NSwag.Generation.NewtonsoftJson/NewtonsoftJsonOpenApiGeneratorSettingsExtensions.cs new file mode 100644 index 0000000000..9157d1d5ef --- /dev/null +++ b/src/NSwag.Generation.NewtonsoftJson/NewtonsoftJsonOpenApiGeneratorSettingsExtensions.cs @@ -0,0 +1,55 @@ +//----------------------------------------------------------------------- +// +// Copyright (c) Rico Suter. All rights reserved. +// +// https://github.com/RicoSuter/NSwag/blob/master/LICENSE.md +// Rico Suter, mail@rsuter.com +//----------------------------------------------------------------------- + +using Newtonsoft.Json; +using NJsonSchema; +using NJsonSchema.NewtonsoftJson.Generation; + +namespace NSwag.Generation.NewtonsoftJson +{ + /// Extension methods to configure NSwag generators to use Newtonsoft.Json + /// for schema generation (for applications that use AddNewtonsoftJson()). + public static class NewtonsoftJsonOpenApiGeneratorSettingsExtensions + { + /// Configures the generator to use Newtonsoft.Json-based schema generation. + /// Call this when your ASP.NET Core application uses AddNewtonsoftJson() on MVC. + /// The generator settings. + /// Optional action to further configure the Newtonsoft schema settings. + /// The settings for chaining. + public static OpenApiDocumentGeneratorSettings UseNewtonsoftJson( + this OpenApiDocumentGeneratorSettings settings, + Action configure = null) + { + var schemaSettings = new NewtonsoftJsonSchemaGeneratorSettings + { + SchemaType = settings.SchemaSettings?.SchemaType ?? SchemaType.OpenApi3 + }; + + configure?.Invoke(schemaSettings); + settings.SchemaSettings = schemaSettings; + return settings; + } + + /// Configures the generator to use Newtonsoft.Json-based schema generation + /// with the given serializer settings (typically from MvcNewtonsoftJsonOptions). + /// The generator settings. + /// The Newtonsoft.Json serializer settings. + /// The settings for chaining. + public static OpenApiDocumentGeneratorSettings UseNewtonsoftJson( + this OpenApiDocumentGeneratorSettings settings, + JsonSerializerSettings serializerSettings) + { + settings.SchemaSettings = new NewtonsoftJsonSchemaGeneratorSettings + { + SchemaType = settings.SchemaSettings?.SchemaType ?? SchemaType.OpenApi3, + SerializerSettings = serializerSettings + }; + return settings; + } + } +} diff --git a/src/NSwag.Generation.NewtonsoftJson/NewtonsoftJsonSettingsResolver.cs b/src/NSwag.Generation.NewtonsoftJson/NewtonsoftJsonSettingsResolver.cs new file mode 100644 index 0000000000..73b740be13 --- /dev/null +++ b/src/NSwag.Generation.NewtonsoftJson/NewtonsoftJsonSettingsResolver.cs @@ -0,0 +1,44 @@ +//----------------------------------------------------------------------- +// +// Copyright (c) Rico Suter. All rights reserved. +// +// https://github.com/RicoSuter/NSwag/blob/master/LICENSE.md +// Rico Suter, mail@rsuter.com +//----------------------------------------------------------------------- + +using System.Reflection; +using Newtonsoft.Json; + +namespace NSwag.Generation.NewtonsoftJson +{ + /// Resolves Newtonsoft.Json serializer settings from ASP.NET Core's DI container. + public static class NewtonsoftJsonSettingsResolver + { + /// Loads the Newtonsoft.Json from the given + /// service provider by detecting MvcNewtonsoftJsonOptions via reflection. + /// The service provider. + /// The settings, or null if Newtonsoft.Json is not configured. + public static JsonSerializerSettings GetJsonSerializerSettings(IServiceProvider serviceProvider) + { + try + { + var optionsAssembly = Assembly.Load(new AssemblyName("Microsoft.AspNetCore.Mvc.NewtonsoftJson")); + var iOptionsType = Type.GetType("Microsoft.Extensions.Options.IOptions`1, Microsoft.Extensions.Options"); + if (iOptionsType == null) + { + return null; + } + + var optionsType = iOptionsType.MakeGenericType( + optionsAssembly.GetType("Microsoft.AspNetCore.Mvc.MvcNewtonsoftJsonOptions", true)); + var options = serviceProvider?.GetService(optionsType); + + return (JsonSerializerSettings)((dynamic)options?.GetType().GetProperty("Value")?.GetValue(options))?.SerializerSettings; + } + catch + { + return null; + } + } + } +} diff --git a/src/NSwag.Generation.Tests/OpenApiDocumentGeneratorTests.cs b/src/NSwag.Generation.Tests/OpenApiDocumentGeneratorTests.cs index 87e42a1c8b..e94f8bda82 100644 --- a/src/NSwag.Generation.Tests/OpenApiDocumentGeneratorTests.cs +++ b/src/NSwag.Generation.Tests/OpenApiDocumentGeneratorTests.cs @@ -1,7 +1,6 @@ using Namotion.Reflection; using NJsonSchema; using NJsonSchema.Generation; -using NJsonSchema.NewtonsoftJson.Generation; using Xunit; namespace NSwag.Generation.Tests @@ -19,7 +18,7 @@ private OpenApiParameter GetParameter(SchemaType schemaType) { var generatorSettings = new OpenApiDocumentGeneratorSettings { - SchemaSettings = new NewtonsoftJsonSchemaGeneratorSettings + SchemaSettings = new SystemTextJsonSchemaGeneratorSettings { SchemaType = schemaType, } diff --git a/src/NSwag.Generation.Tests/Processors/OperationSummaryAndDescriptionProcessorTests.cs b/src/NSwag.Generation.Tests/Processors/OperationSummaryAndDescriptionProcessorTests.cs index 3f4831a2cd..1a8c70abef 100644 --- a/src/NSwag.Generation.Tests/Processors/OperationSummaryAndDescriptionProcessorTests.cs +++ b/src/NSwag.Generation.Tests/Processors/OperationSummaryAndDescriptionProcessorTests.cs @@ -1,6 +1,6 @@ using System.ComponentModel; using System.Reflection; -using NJsonSchema.NewtonsoftJson.Generation; +using NJsonSchema.Generation; using NSwag.Annotations; using NSwag.Generation.Processors; using NSwag.Generation.Processors.Contexts; @@ -102,7 +102,7 @@ private OperationProcessorContext GetContext(Type controllerType, MethodInfo met var operationDescription = new OpenApiOperationDescription { Operation = new OpenApiOperation() }; var settings = new OpenApiDocumentGeneratorSettings { - SchemaSettings = new NewtonsoftJsonSchemaGeneratorSettings() + SchemaSettings = new SystemTextJsonSchemaGeneratorSettings() }; return new OperationProcessorContext(document, operationDescription, controllerType, methodInfo, null, null, settings, null); diff --git a/src/NSwag.Generation.Tests/Processors/OperationTagsProcessorTests.cs b/src/NSwag.Generation.Tests/Processors/OperationTagsProcessorTests.cs index d3768d493a..36275828ac 100644 --- a/src/NSwag.Generation.Tests/Processors/OperationTagsProcessorTests.cs +++ b/src/NSwag.Generation.Tests/Processors/OperationTagsProcessorTests.cs @@ -1,5 +1,5 @@ using System.Reflection; -using NJsonSchema.NewtonsoftJson.Generation; +using NJsonSchema.Generation; using NSwag.Annotations; using NSwag.Generation.Processors; using NSwag.Generation.Processors.Contexts; @@ -132,7 +132,7 @@ private OperationProcessorContext GetContext(Type controllerType, MethodInfo met var operationDescription = new OpenApiOperationDescription { Operation = new OpenApiOperation() }; var settings = new OpenApiDocumentGeneratorSettings { - SchemaSettings = new NewtonsoftJsonSchemaGeneratorSettings(), + SchemaSettings = new SystemTextJsonSchemaGeneratorSettings(), UseControllerSummaryAsTagDescription = true }; return new OperationProcessorContext(document, operationDescription, controllerType, methodInfo, null, null, settings, null); diff --git a/src/NSwag.Generation/OpenApiDocumentGenerator.cs b/src/NSwag.Generation/OpenApiDocumentGenerator.cs index e1c9a5055b..d50e78dc54 100644 --- a/src/NSwag.Generation/OpenApiDocumentGenerator.cs +++ b/src/NSwag.Generation/OpenApiDocumentGenerator.cs @@ -7,7 +7,6 @@ //----------------------------------------------------------------------- using Namotion.Reflection; -using Newtonsoft.Json; using NJsonSchema; using NJsonSchema.Generation; using NJsonSchema.Infrastructure; @@ -126,7 +125,7 @@ private OpenApiParameter CreatePrimitiveOpenApiParameter(ContextualType contextu var referencedSchema = SchemaGenerator.Generate(contextualParameter, _schemaResolver); - var hasSchemaAnnotations = JsonConvert.SerializeObject(operationParameter.Schema) != "{}"; + var hasSchemaAnnotations = System.Text.Json.JsonSerializer.Serialize(operationParameter.Schema) != "{}"; if (hasSchemaAnnotations || typeDescription.IsNullable) { operationParameter.Schema.IsNullableRaw = true; diff --git a/src/NSwag.Generation/OpenApiDocumentGeneratorSettings.cs b/src/NSwag.Generation/OpenApiDocumentGeneratorSettings.cs index 6330bd9e76..f49215770c 100644 --- a/src/NSwag.Generation/OpenApiDocumentGeneratorSettings.cs +++ b/src/NSwag.Generation/OpenApiDocumentGeneratorSettings.cs @@ -6,8 +6,8 @@ // Rico Suter, mail@rsuter.com //----------------------------------------------------------------------- +using System.Text.Json.Serialization; using Namotion.Reflection; -using Newtonsoft.Json; using NJsonSchema; using NJsonSchema.Generation; using NSwag.Generation.Processors; diff --git a/src/NSwag.Sample.NET100/openapi.json b/src/NSwag.Sample.NET100/openapi.json deleted file mode 100644 index cbc9cf4db2..0000000000 --- a/src/NSwag.Sample.NET100/openapi.json +++ /dev/null @@ -1,263 +0,0 @@ -{ - "x-generator": "NSwag", - "openapi": "3.0.0", - "info": { - "title": "My Title", - "description": "Hello world!", - "version": "1.0.0" - }, - "paths": { - "/api/Values": { - "get": { - "tags": [ - "Values" - ], - "operationId": "Values_GetAll", - "responses": { - "204": { - "description": "" - }, - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Person" - } - } - } - } - }, - "201": { - "description": "", - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Person" - } - } - } - } - } - } - }, - "post": { - "tags": [ - "Values" - ], - "operationId": "Values_Post", - "requestBody": { - "x-name": "value", - "content": { - "application/json": { - "schema": { - "type": "string" - } - } - }, - "required": true, - "x-position": 1 - }, - "responses": { - "200": { - "description": "" - } - } - } - }, - "/api/Values/{id}": { - "get": { - "tags": [ - "Values" - ], - "operationId": "Values_Get", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer", - "format": "int32" - }, - "x-position": 1 - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TestEnum" - } - } - } - } - } - }, - "put": { - "tags": [ - "Values" - ], - "operationId": "Values_Put", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer", - "format": "int32" - }, - "x-position": 1 - } - ], - "requestBody": { - "x-name": "value", - "content": { - "application/json": { - "schema": { - "type": "string" - } - } - }, - "required": true, - "x-position": 2 - }, - "responses": { - "200": { - "description": "" - } - } - }, - "delete": { - "tags": [ - "Values" - ], - "operationId": "Values_Delete", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer", - "format": "int32" - }, - "x-position": 1 - } - ], - "responses": { - "200": { - "description": "" - } - } - } - }, - "/api/Values/ToString({id})": { - "get": { - "tags": [ - "Values" - ], - "operationId": "Values_GetToString", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer", - "format": "int32" - }, - "x-position": 1 - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "type": "string" - } - } - } - } - } - } - }, - "/api/Values/{id}/foo": { - "get": { - "tags": [ - "Values" - ], - "operationId": "Values_GetFooBar", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer", - "format": "int32" - }, - "x-position": 1 - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "type": "string" - } - } - } - } - } - } - } - }, - "components": { - "schemas": { - "Person": { - "type": "object", - "additionalProperties": false, - "properties": { - "firstName": { - "type": "string" - }, - "middleName": { - "type": "string", - "nullable": true - }, - "lastName": { - "type": "string" - }, - "dayOfBirth": { - "type": "string", - "format": "date-time" - } - } - }, - "TestEnum": { - "type": "string", - "description": "", - "x-enumNames": [ - "Foo", - "Bar" - ], - "enum": [ - "Foo", - "Bar" - ] - } - } - } -} \ No newline at end of file diff --git a/src/NSwag.Sample.NET100Minimal/openapi.json b/src/NSwag.Sample.NET100Minimal/openapi.json deleted file mode 100644 index dc4334aebf..0000000000 --- a/src/NSwag.Sample.NET100Minimal/openapi.json +++ /dev/null @@ -1,161 +0,0 @@ -{ - "x-generator": "NSwag", - "openapi": "3.0.0", - "info": { - "title": "Minimal API", - "version": "v1" - }, - "paths": { - "/": { - "get": { - "tags": [ - "General" - ], - "operationId": "Get", - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "type": "string" - } - } - } - } - } - } - }, - "/sum/{a}/{b}": { - "get": { - "tags": [ - "Calculator" - ], - "operationId": "CalculateSum", - "parameters": [ - { - "name": "a", - "in": "path", - "required": true, - "schema": { - "type": "integer", - "format": "int32" - }, - "x-position": 1 - }, - { - "name": "b", - "in": "path", - "required": true, - "schema": { - "type": "integer", - "format": "int32" - }, - "x-position": 2 - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "type": "integer", - "format": "int32" - } - } - } - } - } - } - }, - "/abs({a})": { - "get": { - "tags": [ - "Calculator" - ], - "operationId": "AbsoluteValue", - "parameters": [ - { - "name": "a", - "in": "path", - "required": true, - "schema": { - "type": "integer", - "format": "int32" - }, - "x-position": 1 - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "type": "integer", - "format": "int32" - } - } - } - } - } - } - }, - "/id:{id}": { - "get": { - "tags": [ - "Calculator" - ], - "operationId": "Identity", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer", - "format": "int32" - }, - "x-position": 1 - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "type": "integer", - "format": "int32" - } - } - } - } - } - } - }, - "/examples": { - "get": { - "tags": [ - "Example" - ], - "operationId": "Example_Get", - "responses": { - "200": { - "description": "", - "content": { - "application/octet-stream": { - "schema": { - "type": "string", - "format": "binary" - } - } - } - } - } - } - } - }, - "components": {} -} \ No newline at end of file diff --git a/src/NSwag.Sample.NET80/openapi.json b/src/NSwag.Sample.NET80/openapi.json deleted file mode 100644 index 4912989c43..0000000000 --- a/src/NSwag.Sample.NET80/openapi.json +++ /dev/null @@ -1,295 +0,0 @@ -{ - "x-generator": "NSwag", - "openapi": "3.0.0", - "info": { - "title": "My Title", - "description": "Hello world!", - "version": "1.0.0" - }, - "paths": { - "/api/Values": { - "get": { - "tags": [ - "Values" - ], - "operationId": "Values_GetAll", - "responses": { - "204": { - "description": "" - }, - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Person" - } - } - } - } - }, - "201": { - "description": "", - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Person" - } - } - } - } - } - } - }, - "post": { - "tags": [ - "Values" - ], - "operationId": "Values_Post", - "requestBody": { - "x-name": "value", - "content": { - "application/json": { - "schema": { - "type": "string" - } - } - }, - "required": true, - "x-position": 1 - }, - "responses": { - "200": { - "description": "" - } - } - } - }, - "/api/Values/{id}": { - "get": { - "tags": [ - "Values" - ], - "operationId": "Values_Get", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer", - "format": "int32" - }, - "x-position": 1 - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TestEnum" - } - } - } - } - } - }, - "put": { - "tags": [ - "Values" - ], - "operationId": "Values_Put", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer", - "format": "int32" - }, - "x-position": 1 - } - ], - "requestBody": { - "x-name": "value", - "content": { - "application/json": { - "schema": { - "type": "string" - } - } - }, - "required": true, - "x-position": 2 - }, - "responses": { - "200": { - "description": "" - } - } - }, - "delete": { - "tags": [ - "Values" - ], - "operationId": "Values_Delete", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer", - "format": "int32" - }, - "x-position": 1 - } - ], - "responses": { - "200": { - "description": "" - } - } - } - }, - "/api/Values/ToString({id})": { - "get": { - "tags": [ - "Values" - ], - "operationId": "Values_GetToString", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer", - "format": "int32" - }, - "x-position": 1 - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "type": "string" - } - } - } - } - } - } - }, - "/api/Values/id:{id}": { - "get": { - "tags": [ - "Values" - ], - "operationId": "Values_GetToId", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer", - "format": "int32" - }, - "x-position": 1 - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "type": "string" - } - } - } - } - } - } - }, - "/api/Values/{id}/foo": { - "get": { - "tags": [ - "Values" - ], - "operationId": "Values_GetFooBar", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer", - "format": "int32" - }, - "x-position": 1 - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "type": "string" - } - } - } - } - } - } - } - }, - "components": { - "schemas": { - "Person": { - "type": "object", - "additionalProperties": false, - "properties": { - "firstName": { - "type": "string" - }, - "middleName": { - "type": "string", - "nullable": true - }, - "lastName": { - "type": "string" - }, - "dayOfBirth": { - "type": "string", - "format": "date-time" - } - } - }, - "TestEnum": { - "type": "string", - "description": "", - "x-enumNames": [ - "Foo", - "Bar" - ], - "enum": [ - "Foo", - "Bar" - ] - } - } - } -} \ No newline at end of file diff --git a/src/NSwag.Sample.NET80Minimal/openapi.json b/src/NSwag.Sample.NET80Minimal/openapi.json deleted file mode 100644 index dc4334aebf..0000000000 --- a/src/NSwag.Sample.NET80Minimal/openapi.json +++ /dev/null @@ -1,161 +0,0 @@ -{ - "x-generator": "NSwag", - "openapi": "3.0.0", - "info": { - "title": "Minimal API", - "version": "v1" - }, - "paths": { - "/": { - "get": { - "tags": [ - "General" - ], - "operationId": "Get", - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "type": "string" - } - } - } - } - } - } - }, - "/sum/{a}/{b}": { - "get": { - "tags": [ - "Calculator" - ], - "operationId": "CalculateSum", - "parameters": [ - { - "name": "a", - "in": "path", - "required": true, - "schema": { - "type": "integer", - "format": "int32" - }, - "x-position": 1 - }, - { - "name": "b", - "in": "path", - "required": true, - "schema": { - "type": "integer", - "format": "int32" - }, - "x-position": 2 - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "type": "integer", - "format": "int32" - } - } - } - } - } - } - }, - "/abs({a})": { - "get": { - "tags": [ - "Calculator" - ], - "operationId": "AbsoluteValue", - "parameters": [ - { - "name": "a", - "in": "path", - "required": true, - "schema": { - "type": "integer", - "format": "int32" - }, - "x-position": 1 - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "type": "integer", - "format": "int32" - } - } - } - } - } - } - }, - "/id:{id}": { - "get": { - "tags": [ - "Calculator" - ], - "operationId": "Identity", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer", - "format": "int32" - }, - "x-position": 1 - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "type": "integer", - "format": "int32" - } - } - } - } - } - } - }, - "/examples": { - "get": { - "tags": [ - "Example" - ], - "operationId": "Example_Get", - "responses": { - "200": { - "description": "", - "content": { - "application/octet-stream": { - "schema": { - "type": "string", - "format": "binary" - } - } - } - } - } - } - } - }, - "components": {} -} \ No newline at end of file diff --git a/src/NSwag.Sample.NET90/openapi.json b/src/NSwag.Sample.NET90/openapi.json deleted file mode 100644 index cbc9cf4db2..0000000000 --- a/src/NSwag.Sample.NET90/openapi.json +++ /dev/null @@ -1,263 +0,0 @@ -{ - "x-generator": "NSwag", - "openapi": "3.0.0", - "info": { - "title": "My Title", - "description": "Hello world!", - "version": "1.0.0" - }, - "paths": { - "/api/Values": { - "get": { - "tags": [ - "Values" - ], - "operationId": "Values_GetAll", - "responses": { - "204": { - "description": "" - }, - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Person" - } - } - } - } - }, - "201": { - "description": "", - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Person" - } - } - } - } - } - } - }, - "post": { - "tags": [ - "Values" - ], - "operationId": "Values_Post", - "requestBody": { - "x-name": "value", - "content": { - "application/json": { - "schema": { - "type": "string" - } - } - }, - "required": true, - "x-position": 1 - }, - "responses": { - "200": { - "description": "" - } - } - } - }, - "/api/Values/{id}": { - "get": { - "tags": [ - "Values" - ], - "operationId": "Values_Get", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer", - "format": "int32" - }, - "x-position": 1 - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TestEnum" - } - } - } - } - } - }, - "put": { - "tags": [ - "Values" - ], - "operationId": "Values_Put", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer", - "format": "int32" - }, - "x-position": 1 - } - ], - "requestBody": { - "x-name": "value", - "content": { - "application/json": { - "schema": { - "type": "string" - } - } - }, - "required": true, - "x-position": 2 - }, - "responses": { - "200": { - "description": "" - } - } - }, - "delete": { - "tags": [ - "Values" - ], - "operationId": "Values_Delete", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer", - "format": "int32" - }, - "x-position": 1 - } - ], - "responses": { - "200": { - "description": "" - } - } - } - }, - "/api/Values/ToString({id})": { - "get": { - "tags": [ - "Values" - ], - "operationId": "Values_GetToString", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer", - "format": "int32" - }, - "x-position": 1 - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "type": "string" - } - } - } - } - } - } - }, - "/api/Values/{id}/foo": { - "get": { - "tags": [ - "Values" - ], - "operationId": "Values_GetFooBar", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer", - "format": "int32" - }, - "x-position": 1 - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "type": "string" - } - } - } - } - } - } - } - }, - "components": { - "schemas": { - "Person": { - "type": "object", - "additionalProperties": false, - "properties": { - "firstName": { - "type": "string" - }, - "middleName": { - "type": "string", - "nullable": true - }, - "lastName": { - "type": "string" - }, - "dayOfBirth": { - "type": "string", - "format": "date-time" - } - } - }, - "TestEnum": { - "type": "string", - "description": "", - "x-enumNames": [ - "Foo", - "Bar" - ], - "enum": [ - "Foo", - "Bar" - ] - } - } - } -} \ No newline at end of file diff --git a/src/NSwag.Sample.NET90Minimal/openapi.json b/src/NSwag.Sample.NET90Minimal/openapi.json deleted file mode 100644 index dc4334aebf..0000000000 --- a/src/NSwag.Sample.NET90Minimal/openapi.json +++ /dev/null @@ -1,161 +0,0 @@ -{ - "x-generator": "NSwag", - "openapi": "3.0.0", - "info": { - "title": "Minimal API", - "version": "v1" - }, - "paths": { - "/": { - "get": { - "tags": [ - "General" - ], - "operationId": "Get", - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "type": "string" - } - } - } - } - } - } - }, - "/sum/{a}/{b}": { - "get": { - "tags": [ - "Calculator" - ], - "operationId": "CalculateSum", - "parameters": [ - { - "name": "a", - "in": "path", - "required": true, - "schema": { - "type": "integer", - "format": "int32" - }, - "x-position": 1 - }, - { - "name": "b", - "in": "path", - "required": true, - "schema": { - "type": "integer", - "format": "int32" - }, - "x-position": 2 - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "type": "integer", - "format": "int32" - } - } - } - } - } - } - }, - "/abs({a})": { - "get": { - "tags": [ - "Calculator" - ], - "operationId": "AbsoluteValue", - "parameters": [ - { - "name": "a", - "in": "path", - "required": true, - "schema": { - "type": "integer", - "format": "int32" - }, - "x-position": 1 - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "type": "integer", - "format": "int32" - } - } - } - } - } - } - }, - "/id:{id}": { - "get": { - "tags": [ - "Calculator" - ], - "operationId": "Identity", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer", - "format": "int32" - }, - "x-position": 1 - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "type": "integer", - "format": "int32" - } - } - } - } - } - } - }, - "/examples": { - "get": { - "tags": [ - "Example" - ], - "operationId": "Example_Get", - "responses": { - "200": { - "description": "", - "content": { - "application/octet-stream": { - "schema": { - "type": "string", - "format": "binary" - } - } - } - } - } - } - } - }, - "components": {} -} \ No newline at end of file