Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -420,6 +420,22 @@
{{/model.discriminator}}
{{^model.discriminator}}
{{#composedSchemas}}
{{#oneOf}}
{{^vendorExtensions.x-duplicated-data-type}}
{{^isPrimitiveType}}
{{^isArray}}
{{^isMap}}
if ({{#lambda.camelcase_sanitize_param}}{{model.classname}}{{/lambda.camelcase_sanitize_param}}.{{name}} != null)
{
{{baseType}}JsonConverter converter = ({{baseType}}JsonConverter) jsonSerializerOptions.Converters.First(c => c.CanConvert(typeof({{baseType}})));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: This lookup hard-codes the declared oneOf variant type, so a derived runtime value loses properties declared by its concrete model during serialization. Dispatch WriteProperties through the runtime concrete converter, or add a polymorphic converter abstraction that preserves derived properties.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At modules/openapi-generator/src/main/resources/csharp/libraries/generichost/JsonConverter.mustache, line 430:

<comment>This lookup hard-codes the declared oneOf variant type, so a derived runtime value loses properties declared by its concrete model during serialization. Dispatch `WriteProperties` through the runtime concrete converter, or add a polymorphic converter abstraction that preserves derived properties.</comment>

<file context>
@@ -420,6 +420,22 @@
+            {{^isMap}}
+            if ({{#lambda.camelcase_sanitize_param}}{{model.classname}}{{/lambda.camelcase_sanitize_param}}.{{name}} != null)
+            {
+                {{baseType}}JsonConverter converter = ({{baseType}}JsonConverter) jsonSerializerOptions.Converters.First(c => c.CanConvert(typeof({{baseType}})));
+                converter.WriteProperties(writer, {{#lambda.camelcase_sanitize_param}}{{model.classname}}{{/lambda.camelcase_sanitize_param}}.{{name}}, jsonSerializerOptions);
+            }
</file context>

converter.WriteProperties(writer, {{#lambda.camelcase_sanitize_param}}{{model.classname}}{{/lambda.camelcase_sanitize_param}}.{{name}}, jsonSerializerOptions);
}

{{/isMap}}
{{/isArray}}
{{/isPrimitiveType}}
{{/vendorExtensions.x-duplicated-data-type}}
{{/oneOf}}
{{#anyOf}}
if ({{#lambda.joinWithAmpersand}}{{^required}}{{#lambda.camelcase_sanitize_param}}{{model.classname}}{{/lambda.camelcase_sanitize_param}}.{{name}}Option.IsSet {{/required}}{{#lambda.camelcase_sanitize_param}}{{model.classname}}{{/lambda.camelcase_sanitize_param}}.{{name}}{{^required}}Option.Value{{/required}} != null{{/lambda.joinWithAmpersand}})
{{#isPrimitiveType}}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* Copyright 2026 OpenAPI-Generator Contributors (https://openapi-generator.tech)
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*/
package org.openapitools.codegen.csharpnetcore;

import org.openapitools.codegen.DefaultGenerator;
import org.openapitools.codegen.config.CodegenConfigurator;
import org.testng.annotations.Test;

import java.nio.file.Path;

import static org.openapitools.codegen.TestUtils.assertFileContains;
import static org.openapitools.codegen.TestUtils.newTempFolder;

public class CSharpGenericHostOneOfSerializationTest {
@Test
public void testObjectOneOfWithoutDiscriminatorWritesVariant() throws Exception {
Path output = newTempFolder();
CodegenConfigurator config = new CodegenConfigurator().setGeneratorName("csharp").setLibrary("generichost")
.setInputSpec("src/test/resources/3_0/csharp/oneof-object-serialization.yaml")
.setOutputDir(output.toString()).addAdditionalProperty("packageName", "OneOfRepro");
new DefaultGenerator().opts(config.toClientOptInput()).generate();
assertFileContains(output.resolve("src/OneOfRepro/Model/Entry.cs"),
"if (entry.FileEntry != null)",
"converter.WriteProperties(writer, entry.FileEntry, jsonSerializerOptions);",
"if (entry.FolderEntry != null)",
"converter.WriteProperties(writer, entry.FolderEntry, jsonSerializerOptions);");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
openapi: 3.0.3
info:
title: oneOf reproduction
version: 1.0.0
paths: {}
components:
schemas:
Entry:
oneOf:
- $ref: '#/components/schemas/FileEntry'
- $ref: '#/components/schemas/FolderEntry'
FileEntry:
type: object
required: [name, size]
properties:
name:
type: string
size:
type: integer
FolderEntry:
type: object
required: [name, id]
properties:
name:
type: string
id:
type: string
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,18 @@ public override void Write(Utf8JsonWriter writer, Fruit fruit, JsonSerializerOpt
{
writer.WriteStartObject();

if (fruit.Apple != null)
{
AppleJsonConverter converter = (AppleJsonConverter) jsonSerializerOptions.Converters.First(c => c.CanConvert(typeof(Apple)));
converter.WriteProperties(writer, fruit.Apple, jsonSerializerOptions);
}

if (fruit.Banana != null)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: These independent checks serialize both variants when callers set both public properties, although Fruit is a oneOf and Read selects Apple first. Use else if for Banana or enforce mutual exclusivity in the setters.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At samples/client/petstore/csharp/generichost/latest/UseDateTimeOffset/src/Org.OpenAPITools/Model/Fruit.cs, line 214:

<comment>These independent checks serialize both variants when callers set both public properties, although `Fruit` is a `oneOf` and `Read` selects Apple first. Use `else if` for Banana or enforce mutual exclusivity in the setters.</comment>

<file context>
@@ -205,6 +205,18 @@ public override void Write(Utf8JsonWriter writer, Fruit fruit, JsonSerializerOpt
+                converter.WriteProperties(writer, fruit.Apple, jsonSerializerOptions);
+            }
+
+            if (fruit.Banana != null)
+            {
+                BananaJsonConverter converter = (BananaJsonConverter) jsonSerializerOptions.Converters.First(c => c.CanConvert(typeof(Banana)));
</file context>
Suggested change
if (fruit.Banana != null)
else if (fruit.Banana != null)

{
BananaJsonConverter converter = (BananaJsonConverter) jsonSerializerOptions.Converters.First(c => c.CanConvert(typeof(Banana)));
converter.WriteProperties(writer, fruit.Banana, jsonSerializerOptions);
}

WriteProperties(writer, fruit, jsonSerializerOptions);
writer.WriteEndObject();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,18 @@ public override void Write(Utf8JsonWriter writer, FruitReq fruitReq, JsonSeriali
{
writer.WriteStartObject();

if (fruitReq.AppleReq != null)
{
AppleReqJsonConverter converter = (AppleReqJsonConverter) jsonSerializerOptions.Converters.First(c => c.CanConvert(typeof(AppleReq)));
converter.WriteProperties(writer, fruitReq.AppleReq, jsonSerializerOptions);
}

if (fruitReq.BananaReq != null)
{
BananaReqJsonConverter converter = (BananaReqJsonConverter) jsonSerializerOptions.Converters.First(c => c.CanConvert(typeof(BananaReq)));
converter.WriteProperties(writer, fruitReq.BananaReq, jsonSerializerOptions);
}

WriteProperties(writer, fruitReq, jsonSerializerOptions);
writer.WriteEndObject();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,12 @@ public override void Write(Utf8JsonWriter writer, MixedOneOfContent mixedOneOfCo
{
writer.WriteStartObject();

if (mixedOneOfContent.MixedSubId != null)
{
MixedSubIdJsonConverter converter = (MixedSubIdJsonConverter) jsonSerializerOptions.Converters.First(c => c.CanConvert(typeof(MixedSubId)));
converter.WriteProperties(writer, mixedOneOfContent.MixedSubId, jsonSerializerOptions);
}

WriteProperties(writer, mixedOneOfContent, jsonSerializerOptions);
writer.WriteEndObject();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,18 @@ public override void Write(Utf8JsonWriter writer, Fruit fruit, JsonSerializerOpt
{
writer.WriteStartObject();

if (fruit.Apple != null)
{
AppleJsonConverter converter = (AppleJsonConverter) jsonSerializerOptions.Converters.First(c => c.CanConvert(typeof(Apple)));
converter.WriteProperties(writer, fruit.Apple, jsonSerializerOptions);
}

if (fruit.Banana != null)
{
BananaJsonConverter converter = (BananaJsonConverter) jsonSerializerOptions.Converters.First(c => c.CanConvert(typeof(Banana)));
converter.WriteProperties(writer, fruit.Banana, jsonSerializerOptions);
}

WriteProperties(writer, fruit, jsonSerializerOptions);
writer.WriteEndObject();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,18 @@ public override void Write(Utf8JsonWriter writer, FruitReq fruitReq, JsonSeriali
{
writer.WriteStartObject();

if (fruitReq.AppleReq != null)
{
AppleReqJsonConverter converter = (AppleReqJsonConverter) jsonSerializerOptions.Converters.First(c => c.CanConvert(typeof(AppleReq)));
converter.WriteProperties(writer, fruitReq.AppleReq, jsonSerializerOptions);
}

if (fruitReq.BananaReq != null)
{
BananaReqJsonConverter converter = (BananaReqJsonConverter) jsonSerializerOptions.Converters.First(c => c.CanConvert(typeof(BananaReq)));
converter.WriteProperties(writer, fruitReq.BananaReq, jsonSerializerOptions);
}

WriteProperties(writer, fruitReq, jsonSerializerOptions);
writer.WriteEndObject();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,12 @@ public override void Write(Utf8JsonWriter writer, MixedOneOfContent mixedOneOfCo
{
writer.WriteStartObject();

if (mixedOneOfContent.MixedSubId != null)
{
MixedSubIdJsonConverter converter = (MixedSubIdJsonConverter) jsonSerializerOptions.Converters.First(c => c.CanConvert(typeof(MixedSubId)));
converter.WriteProperties(writer, mixedOneOfContent.MixedSubId, jsonSerializerOptions);
}

WriteProperties(writer, mixedOneOfContent, jsonSerializerOptions);
writer.WriteEndObject();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,18 @@ public override void Write(Utf8JsonWriter writer, Fruit fruit, JsonSerializerOpt
{
writer.WriteStartObject();

if (fruit.Apple != null)
{
AppleJsonConverter converter = (AppleJsonConverter) jsonSerializerOptions.Converters.First(c => c.CanConvert(typeof(Apple)));
converter.WriteProperties(writer, fruit.Apple, jsonSerializerOptions);
}

if (fruit.Banana != null)
{
BananaJsonConverter converter = (BananaJsonConverter) jsonSerializerOptions.Converters.First(c => c.CanConvert(typeof(Banana)));
converter.WriteProperties(writer, fruit.Banana, jsonSerializerOptions);
}

WriteProperties(writer, fruit, jsonSerializerOptions);
writer.WriteEndObject();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,18 @@ public override void Write(Utf8JsonWriter writer, FruitReq fruitReq, JsonSeriali
{
writer.WriteStartObject();

if (fruitReq.AppleReq != null)
{
AppleReqJsonConverter converter = (AppleReqJsonConverter) jsonSerializerOptions.Converters.First(c => c.CanConvert(typeof(AppleReq)));
converter.WriteProperties(writer, fruitReq.AppleReq, jsonSerializerOptions);
}

if (fruitReq.BananaReq != null)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: These independent checks serialize both variants when callers set both public properties, producing a payload that violates the oneOf contract. Use an else if (or reject ambiguous state) so only one variant is emitted.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At samples/client/petstore/csharp/generichost/net10/NullReferenceTypes/src/Org.OpenAPITools/Model/FruitReq.cs, line 189:

<comment>These independent checks serialize both variants when callers set both public properties, producing a payload that violates the `oneOf` contract. Use an `else if` (or reject ambiguous state) so only one variant is emitted.</comment>

<file context>
@@ -180,6 +180,18 @@ public override void Write(Utf8JsonWriter writer, FruitReq fruitReq, JsonSeriali
+                converter.WriteProperties(writer, fruitReq.AppleReq, jsonSerializerOptions);
+            }
+
+            if (fruitReq.BananaReq != null)
+            {
+                BananaReqJsonConverter converter = (BananaReqJsonConverter) jsonSerializerOptions.Converters.First(c => c.CanConvert(typeof(BananaReq)));
</file context>
Suggested change
if (fruitReq.BananaReq != null)
else if (fruitReq.BananaReq != null)

{
BananaReqJsonConverter converter = (BananaReqJsonConverter) jsonSerializerOptions.Converters.First(c => c.CanConvert(typeof(BananaReq)));
converter.WriteProperties(writer, fruitReq.BananaReq, jsonSerializerOptions);
}

WriteProperties(writer, fruitReq, jsonSerializerOptions);
writer.WriteEndObject();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,12 @@ public override void Write(Utf8JsonWriter writer, MixedOneOfContent mixedOneOfCo
{
writer.WriteStartObject();

if (mixedOneOfContent.MixedSubId != null)
{
MixedSubIdJsonConverter converter = (MixedSubIdJsonConverter) jsonSerializerOptions.Converters.First(c => c.CanConvert(typeof(MixedSubId)));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: This lookup can select a user-supplied converter that supports MixedSubId but is not MixedSubIdJsonConverter, causing serialization to throw before writing the variant. Select the generated converter by its concrete type instead of casting the first converter that claims support.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At samples/client/petstore/csharp/generichost/net10/NullReferenceTypes/src/Org.OpenAPITools/Model/MixedOneOfContent.cs, line 258:

<comment>This lookup can select a user-supplied converter that supports `MixedSubId` but is not `MixedSubIdJsonConverter`, causing serialization to throw before writing the variant. Select the generated converter by its concrete type instead of casting the first converter that claims support.</comment>

<file context>
@@ -253,6 +253,12 @@ public override void Write(Utf8JsonWriter writer, MixedOneOfContent mixedOneOfCo
 
+            if (mixedOneOfContent.MixedSubId != null)
+            {
+                MixedSubIdJsonConverter converter = (MixedSubIdJsonConverter) jsonSerializerOptions.Converters.First(c => c.CanConvert(typeof(MixedSubId)));
+                converter.WriteProperties(writer, mixedOneOfContent.MixedSubId, jsonSerializerOptions);
+            }
</file context>
Suggested change
MixedSubIdJsonConverter converter = (MixedSubIdJsonConverter) jsonSerializerOptions.Converters.First(c => c.CanConvert(typeof(MixedSubId)));
MixedSubIdJsonConverter converter = jsonSerializerOptions.Converters.OfType<MixedSubIdJsonConverter>().First();

converter.WriteProperties(writer, mixedOneOfContent.MixedSubId, jsonSerializerOptions);
}

WriteProperties(writer, mixedOneOfContent, jsonSerializerOptions);
writer.WriteEndObject();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
using System.Text.Json;
using Microsoft.Extensions.DependencyInjection;
using Org.OpenAPITools.Client;
using Org.OpenAPITools.Model;
using Xunit;

namespace Org.OpenAPITools.Test.Model
{
public class OneOfSerializationRegressionTests
{
[Fact]
public void WritesActiveObjectVariantAndWrapperProperties()
{
JsonSerializerOptions? options = null;
new HostConfiguration(new ServiceCollection()).ConfigureJsonOptions(value => options = value);
Assert.NotNull(options);
Fruit fruit = JsonSerializer.Deserialize<Fruit>("{\"kind\":\"gala\",\"color\":\"red\"}", options)!;
Assert.NotNull(fruit.Apple);
using (JsonDocument json = JsonDocument.Parse(JsonSerializer.Serialize(fruit, options)))
{
Assert.Equal("gala", json.RootElement.GetProperty("kind").GetString());
Assert.Equal("red", json.RootElement.GetProperty("color").GetString());
}

fruit.Apple = null;
fruit.Banana = new Banana { Count = 2 };
using (JsonDocument json = JsonDocument.Parse(JsonSerializer.Serialize(fruit, options)))
{
Assert.Equal(2, json.RootElement.GetProperty("count").GetInt32());
Assert.Equal("red", json.RootElement.GetProperty("color").GetString());
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,24 @@ public override void Write(Utf8JsonWriter writer, Fruit fruit, JsonSerializerOpt
{
writer.WriteStartObject();

if (fruit.Apple != null)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: This serializes every non-null oneOf slot rather than enforcing one active variant. Because Fruit exposes public setters, setting two variants produces ambiguous JSON and round-tripping loses the later variant; reject multiple variants or serialize only the selected variant.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At samples/client/petstore/csharp/generichost/net10/OneOf/src/Org.OpenAPITools/Model/Fruit.cs, line 240:

<comment>This serializes every non-null oneOf slot rather than enforcing one active variant. Because `Fruit` exposes public setters, setting two variants produces ambiguous JSON and round-tripping loses the later variant; reject multiple variants or serialize only the selected variant.</comment>

<file context>
@@ -237,6 +237,24 @@ public override void Write(Utf8JsonWriter writer, Fruit fruit, JsonSerializerOpt
         {
             writer.WriteStartObject();
 
+            if (fruit.Apple != null)
+            {
+                AppleJsonConverter converter = (AppleJsonConverter) jsonSerializerOptions.Converters.First(c => c.CanConvert(typeof(Apple)));
</file context>

{
AppleJsonConverter converter = (AppleJsonConverter) jsonSerializerOptions.Converters.First(c => c.CanConvert(typeof(Apple)));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: These lookups assume that the first converter claiming each variant is the generated *JsonConverter, but ConfigureJsonOptions allows callers to reorder or replace converters. Select the generated converter by concrete type, or serialize and copy the configured variant object, instead of casting an arbitrary CanConvert match; apply the same fix to Banana and Orange.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At samples/client/petstore/csharp/generichost/net10/OneOf/src/Org.OpenAPITools/Model/Fruit.cs, line 242:

<comment>These lookups assume that the first converter claiming each variant is the generated `*JsonConverter`, but `ConfigureJsonOptions` allows callers to reorder or replace converters. Select the generated converter by concrete type, or serialize and copy the configured variant object, instead of casting an arbitrary `CanConvert` match; apply the same fix to Banana and Orange.</comment>

<file context>
@@ -237,6 +237,24 @@ public override void Write(Utf8JsonWriter writer, Fruit fruit, JsonSerializerOpt
 
+            if (fruit.Apple != null)
+            {
+                AppleJsonConverter converter = (AppleJsonConverter) jsonSerializerOptions.Converters.First(c => c.CanConvert(typeof(Apple)));
+                converter.WriteProperties(writer, fruit.Apple, jsonSerializerOptions);
+            }
</file context>

converter.WriteProperties(writer, fruit.Apple, jsonSerializerOptions);
}

if (fruit.Banana != null)
{
BananaJsonConverter converter = (BananaJsonConverter) jsonSerializerOptions.Converters.First(c => c.CanConvert(typeof(Banana)));
converter.WriteProperties(writer, fruit.Banana, jsonSerializerOptions);
}

if (fruit.Orange != null)
{
OrangeJsonConverter converter = (OrangeJsonConverter) jsonSerializerOptions.Converters.First(c => c.CanConvert(typeof(Orange)));
converter.WriteProperties(writer, fruit.Orange, jsonSerializerOptions);
}

WriteProperties(writer, fruit, jsonSerializerOptions);
writer.WriteEndObject();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,18 @@ public override void Write(Utf8JsonWriter writer, Fruit fruit, JsonSerializerOpt
{
writer.WriteStartObject();

if (fruit.Apple != null)
{
AppleJsonConverter converter = (AppleJsonConverter) jsonSerializerOptions.Converters.First(c => c.CanConvert(typeof(Apple)));
converter.WriteProperties(writer, fruit.Apple, jsonSerializerOptions);
}

if (fruit.Banana != null)
{
BananaJsonConverter converter = (BananaJsonConverter) jsonSerializerOptions.Converters.First(c => c.CanConvert(typeof(Banana)));
converter.WriteProperties(writer, fruit.Banana, jsonSerializerOptions);
}

WriteProperties(writer, fruit, jsonSerializerOptions);
writer.WriteEndObject();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,18 @@ public override void Write(Utf8JsonWriter writer, FruitReq fruitReq, JsonSeriali
{
writer.WriteStartObject();

if (fruitReq.AppleReq != null)
{
AppleReqJsonConverter converter = (AppleReqJsonConverter) jsonSerializerOptions.Converters.First(c => c.CanConvert(typeof(AppleReq)));
converter.WriteProperties(writer, fruitReq.AppleReq, jsonSerializerOptions);
}

if (fruitReq.BananaReq != null)
{
BananaReqJsonConverter converter = (BananaReqJsonConverter) jsonSerializerOptions.Converters.First(c => c.CanConvert(typeof(BananaReq)));
converter.WriteProperties(writer, fruitReq.BananaReq, jsonSerializerOptions);
}

WriteProperties(writer, fruitReq, jsonSerializerOptions);
writer.WriteEndObject();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,12 @@ public override void Write(Utf8JsonWriter writer, MixedOneOfContent mixedOneOfCo
{
writer.WriteStartObject();

if (mixedOneOfContent.MixedSubId != null)
{
MixedSubIdJsonConverter converter = (MixedSubIdJsonConverter) jsonSerializerOptions.Converters.First(c => c.CanConvert(typeof(MixedSubId)));
converter.WriteProperties(writer, mixedOneOfContent.MixedSubId, jsonSerializerOptions);
}

WriteProperties(writer, mixedOneOfContent, jsonSerializerOptions);
writer.WriteEndObject();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,18 @@ public override void Write(Utf8JsonWriter writer, Fruit fruit, JsonSerializerOpt
{
writer.WriteStartObject();

if (fruit.Apple != null)
{
AppleJsonConverter converter = (AppleJsonConverter) jsonSerializerOptions.Converters.First(c => c.CanConvert(typeof(Apple)));
converter.WriteProperties(writer, fruit.Apple, jsonSerializerOptions);
}

if (fruit.Banana != null)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: These independent branches serialize both variants when callers set both public properties, producing JSON that violates Fruit's oneOf contract and may be deserialized as a different variant. Make the variant branches mutually exclusive, matching the Apple-first selection used by Read.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At samples/client/petstore/csharp/generichost/net10/SourceGeneration/src/Org.OpenAPITools/Model/Fruit.cs, line 216:

<comment>These independent branches serialize both variants when callers set both public properties, producing JSON that violates `Fruit`'s `oneOf` contract and may be deserialized as a different variant. Make the variant branches mutually exclusive, matching the Apple-first selection used by `Read`.</comment>

<file context>
@@ -207,6 +207,18 @@ public override void Write(Utf8JsonWriter writer, Fruit fruit, JsonSerializerOpt
+                converter.WriteProperties(writer, fruit.Apple, jsonSerializerOptions);
+            }
+
+            if (fruit.Banana != null)
+            {
+                BananaJsonConverter converter = (BananaJsonConverter) jsonSerializerOptions.Converters.First(c => c.CanConvert(typeof(Banana)));
</file context>
Suggested change
if (fruit.Banana != null)
else if (fruit.Banana != null)

{
BananaJsonConverter converter = (BananaJsonConverter) jsonSerializerOptions.Converters.First(c => c.CanConvert(typeof(Banana)));
converter.WriteProperties(writer, fruit.Banana, jsonSerializerOptions);
}

WriteProperties(writer, fruit, jsonSerializerOptions);
writer.WriteEndObject();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,18 @@ public override void Write(Utf8JsonWriter writer, FruitReq fruitReq, JsonSeriali
{
writer.WriteStartObject();

if (fruitReq.AppleReq != null)
{
AppleReqJsonConverter converter = (AppleReqJsonConverter) jsonSerializerOptions.Converters.First(c => c.CanConvert(typeof(AppleReq)));
converter.WriteProperties(writer, fruitReq.AppleReq, jsonSerializerOptions);
}

if (fruitReq.BananaReq != null)
{
BananaReqJsonConverter converter = (BananaReqJsonConverter) jsonSerializerOptions.Converters.First(c => c.CanConvert(typeof(BananaReq)));
converter.WriteProperties(writer, fruitReq.BananaReq, jsonSerializerOptions);
}

WriteProperties(writer, fruitReq, jsonSerializerOptions);
writer.WriteEndObject();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,12 @@ public override void Write(Utf8JsonWriter writer, MixedOneOfContent mixedOneOfCo
{
writer.WriteStartObject();

if (mixedOneOfContent.MixedSubId != null)
{
MixedSubIdJsonConverter converter = (MixedSubIdJsonConverter) jsonSerializerOptions.Converters.First(c => c.CanConvert(typeof(MixedSubId)));
converter.WriteProperties(writer, mixedOneOfContent.MixedSubId, jsonSerializerOptions);
}

WriteProperties(writer, mixedOneOfContent, jsonSerializerOptions);
writer.WriteEndObject();
}
Expand Down
Loading
Loading