Repository navigation
fix(csharp): serialize object oneOf variants - #25549
AndreyVMarkelov wants to merge 1 commit into
Conversation
There was a problem hiding this comment.
23 issues found across 63 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. 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. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="samples/client/petstore/csharp/generichost/net8/SourceGeneration/src/Org.OpenAPITools/Model/FruitReq.cs">
<violation number="1" location="samples/client/petstore/csharp/generichost/net8/SourceGeneration/src/Org.OpenAPITools/Model/FruitReq.cs:186">
P2: This lookup can select a caller-supplied `JsonConverter<AppleReq>` and then fail the cast to `AppleReqJsonConverter`, breaking serialization when custom converters are configured. Select the generated converter by concrete type instead, and apply the same fix to the BananaReq lookup.</violation>
</file>
<file name="samples/client/petstore/csharp/generichost/net8/NullReferenceTypes/src/Org.OpenAPITools/Model/Fruit.cs">
<violation number="1" location="samples/client/petstore/csharp/generichost/net8/NullReferenceTypes/src/Org.OpenAPITools/Model/Fruit.cs:215">
P2: This independent branch can serialize both variants when callers set `Fruit.Apple` and `Fruit.Banana`, producing an invalid `oneOf` payload. Match the deserializer's Apple-first selection with `else if` (or reject multiple populated variants).</violation>
</file>
<file name="samples/client/petstore/csharp/generichost/net9/Petstore/src/Org.OpenAPITools/Model/FruitReq.cs">
<violation number="1" location="samples/client/petstore/csharp/generichost/net9/Petstore/src/Org.OpenAPITools/Model/FruitReq.cs:183">
P2: This lookup assumes the first converter claiming `AppleReq` is the generated `AppleReqJsonConverter`, but `ConfigureJsonOptions` allows callers to add or reorder converters. A custom converter can therefore make serialization fail at this cast; select the generated converter by concrete type (and apply the same fix to the `BananaReq` lookup).</violation>
</file>
<file name="samples/client/petstore/csharp/generichost/net10/NullReferenceTypes/src/Org.OpenAPITools/Model/FruitReq.cs">
<violation number="1" location="samples/client/petstore/csharp/generichost/net10/NullReferenceTypes/src/Org.OpenAPITools/Model/FruitReq.cs:189">
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.</violation>
</file>
<file name="samples/client/petstore/csharp/generichost/net9/Petstore/src/Org.OpenAPITools/Model/MixedOneOfContent.cs">
<violation number="1" location="samples/client/petstore/csharp/generichost/net9/Petstore/src/Org.OpenAPITools/Model/MixedOneOfContent.cs:256">
P2: This cast assumes the first converter matching `MixedSubId` is always the generated `MixedSubIdJsonConverter`, but `ConfigureJsonOptions` permits callers to prepend custom converters. Select the generated converter by type instead of casting an arbitrary matching converter.</violation>
</file>
<file name="samples/client/petstore/csharp/generichost/net4.8/Petstore/src/Org.OpenAPITools/Model/FruitReq.cs">
<violation number="1" location="samples/client/petstore/csharp/generichost/net4.8/Petstore/src/Org.OpenAPITools/Model/FruitReq.cs:181">
P2: This `oneOf` serializer writes both variants when `AppleReq` and `BananaReq` are non-null, producing a payload that violates the schema and that `Read` resolves to Apple while losing Banana. Reject the dual-populated state or enforce mutually exclusive variant properties before writing.</violation>
</file>
<file name="samples/client/petstore/csharp/generichost/net4.7/OneOf/src/Org.OpenAPITools/Model/Fruit.cs">
<violation number="1" location="samples/client/petstore/csharp/generichost/net4.7/OneOf/src/Org.OpenAPITools/Model/Fruit.cs:244">
P1: This new Banana serialization does not round-trip through the generated client: `count` makes `Fruit.Read` accept the object as an `Apple`, because `Apple.Read` ignores unknown properties and does not require `kind`. Validate each oneOf variant's required properties or otherwise select the matching variant before returning from `Fruit.Read`.</violation>
</file>
<file name="samples/client/petstore/csharp/generichost/standard2.0/Petstore/src/Org.OpenAPITools/Model/Fruit.cs">
<violation number="1" location="samples/client/petstore/csharp/generichost/standard2.0/Petstore/src/Org.OpenAPITools/Model/Fruit.cs:213">
P2: These independent guards can serialize both public variant properties into one object, violating the `oneOf` contract and causing data loss on round-trip. Make the Banana branch mutually exclusive with Apple, or reject instances where both variants are set.</violation>
</file>
<file name="samples/client/petstore/csharp/generichost/net10/SourceGeneration/src/Org.OpenAPITools/Model/Fruit.cs">
<violation number="1" location="samples/client/petstore/csharp/generichost/net10/SourceGeneration/src/Org.OpenAPITools/Model/Fruit.cs:216">
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`.</violation>
</file>
<file name="samples/client/petstore/csharp/generichost/net8/FormModels/src/Org.OpenAPITools/Model/Fruit.cs">
<violation number="1" location="samples/client/petstore/csharp/generichost/net8/FormModels/src/Org.OpenAPITools/Model/Fruit.cs:213">
P2: This serializes both variant objects when callers set both public properties, producing a payload that violates `Fruit`'s `oneOf` contract. Make the variant branches mutually exclusive (or reject the invalid state) so serialization emits only one active variant.</violation>
</file>
<file name="samples/client/petstore/csharp/generichost/latest/UseDateTimeOffset/src/Org.OpenAPITools/Model/Fruit.cs">
<violation number="1" location="samples/client/petstore/csharp/generichost/latest/UseDateTimeOffset/src/Org.OpenAPITools/Model/Fruit.cs:214">
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.</violation>
</file>
<file name="samples/client/petstore/csharp/generichost/net4.8/Petstore/src/Org.OpenAPITools/Model/MixedOneOfContent.cs">
<violation number="1" location="samples/client/petstore/csharp/generichost/net4.8/Petstore/src/Org.OpenAPITools/Model/MixedOneOfContent.cs:256">
P2: This lookup can select a custom converter that supports `MixedSubId` but is not `MixedSubIdJsonConverter`, so the cast throws `InvalidCastException` for valid customized serializer options. Restrict the lookup to `MixedSubIdJsonConverter` instances before casting.</violation>
</file>
<file name="samples/client/petstore/csharp/generichost/net8/NullReferenceTypes/src/Org.OpenAPITools/Model/MixedOneOfContent.cs">
<violation number="1" location="samples/client/petstore/csharp/generichost/net8/NullReferenceTypes/src/Org.OpenAPITools/Model/MixedOneOfContent.cs:258">
P2: Custom JSON options can place another converter for `MixedSubId` before the generated converter, so `First(c => c.CanConvert(...))` may return a converter that cannot be cast to `MixedSubIdJsonConverter`; serialization then throws for this variant. Select the generated converter by type (with a generated-converter fallback) instead of casting the first compatible converter.</violation>
</file>
<file name="samples/client/petstore/csharp/generichost/net9/FormModels/src/Org.OpenAPITools/Model/Fruit.cs">
<violation number="1" location="samples/client/petstore/csharp/generichost/net9/FormModels/src/Org.OpenAPITools/Model/Fruit.cs:209">
P2: This lookup can select a caller-provided `JsonConverter<Apple>` and then throw `InvalidCastException` because the result is cast to `AppleJsonConverter`. Restrict the lookup to the generated converter type (as with `c is AppleJsonConverter`) so custom converters cannot break wrapper serialization.</violation>
<violation number="2" location="samples/client/petstore/csharp/generichost/net9/FormModels/src/Org.OpenAPITools/Model/Fruit.cs:213">
P2: The independent variant checks serialize both object variants when both public properties are set, violating the `oneOf` shape and losing the Banana state on deserialization. Use mutually exclusive variant serialization or enforce one active variant before writing.</violation>
</file>
<file name="samples/client/petstore/csharp/generichost/net9/NullReferenceTypes/src/Org.OpenAPITools/Model/Fruit.cs">
<violation number="1" location="samples/client/petstore/csharp/generichost/net9/NullReferenceTypes/src/Org.OpenAPITools/Model/Fruit.cs:211">
P2: These lookups select by `CanConvert` but then cast to the generated converter. A custom converter registered before the generated converter will make `Fruit` serialization throw; select the generated converter by concrete type, or avoid requiring that cast for custom converters.</violation>
</file>
<file name="samples/client/petstore/csharp/generichost/net10/NullReferenceTypes/src/Org.OpenAPITools/Model/MixedOneOfContent.cs">
<violation number="1" location="samples/client/petstore/csharp/generichost/net10/NullReferenceTypes/src/Org.OpenAPITools/Model/MixedOneOfContent.cs:258">
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.</violation>
</file>
<file name="samples/client/petstore/csharp/generichost/net4.8/FormModels/src/Org.OpenAPITools/Model/Fruit.cs">
<violation number="1" location="samples/client/petstore/csharp/generichost/net4.8/FormModels/src/Org.OpenAPITools/Model/Fruit.cs:209">
P2: This cast assumes the first converter that supports `Apple` is the generated `AppleJsonConverter`, but `ConfigureJsonOptions` allows callers to register another compatible converter first. Select the generated converter by type (and likewise for `Banana`) so custom converters do not make `Fruit` serialization fail.</violation>
<violation number="2" location="samples/client/petstore/csharp/generichost/net4.8/FormModels/src/Org.OpenAPITools/Model/Fruit.cs:213">
P2: This branch allows serialization of both oneOf variants when `Fruit.Apple` and `Fruit.Banana` are both set, producing an invalid oneOf payload and losing Banana on deserialization. Make the variant writes mutually exclusive or reject a model with multiple active variants.</violation>
</file>
<file name="samples/client/petstore/csharp/generichost/net10/OneOf/src/Org.OpenAPITools/Model/Fruit.cs">
<violation number="1" location="samples/client/petstore/csharp/generichost/net10/OneOf/src/Org.OpenAPITools/Model/Fruit.cs:240">
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.</violation>
<violation number="2" location="samples/client/petstore/csharp/generichost/net10/OneOf/src/Org.OpenAPITools/Model/Fruit.cs:242">
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.</violation>
</file>
<file name="samples/client/petstore/csharp/generichost/net8/OneOf/src/Org.OpenAPITools/Model/Fruit.cs">
<violation number="1" location="samples/client/petstore/csharp/generichost/net8/OneOf/src/Org.OpenAPITools/Model/Fruit.cs:242">
P2: These lookups make `FruitJsonConverter` depend on every variant converter being registered in the options. A caller that registers only the wrapper converter now fails at `First`; resolve the generated converter with a fallback (and apply the same fix to Banana and Orange) instead of requiring this undocumented registration invariant.</violation>
</file>
<file name="modules/openapi-generator/src/main/resources/csharp/libraries/generichost/JsonConverter.mustache">
<violation number="1" location="modules/openapi-generator/src/main/resources/csharp/libraries/generichost/JsonConverter.mustache:430">
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.</violation>
</file>
Reply with feedback, questions, or to request a fix.
View guided diff | Re-trigger cubic
| converter.WriteProperties(writer, fruit.Apple, jsonSerializerOptions); | ||
| } | ||
|
|
||
| if (fruit.Banana != null) |
There was a problem hiding this comment.
P1: This new Banana serialization does not round-trip through the generated client: count makes Fruit.Read accept the object as an Apple, because Apple.Read ignores unknown properties and does not require kind. Validate each oneOf variant's required properties or otherwise select the matching variant before returning from Fruit.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/net4.7/OneOf/src/Org.OpenAPITools/Model/Fruit.cs, line 244:
<comment>This new Banana serialization does not round-trip through the generated client: `count` makes `Fruit.Read` accept the object as an `Apple`, because `Apple.Read` ignores unknown properties and does not require `kind`. Validate each oneOf variant's required properties or otherwise select the matching variant before returning from `Fruit.Read`.</comment>
<file context>
@@ -235,6 +235,24 @@ 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>
|
|
||
| if (fruitReq.AppleReq != null) | ||
| { | ||
| AppleReqJsonConverter converter = (AppleReqJsonConverter) jsonSerializerOptions.Converters.First(c => c.CanConvert(typeof(AppleReq))); |
There was a problem hiding this comment.
P2: This lookup can select a caller-supplied JsonConverter<AppleReq> and then fail the cast to AppleReqJsonConverter, breaking serialization when custom converters are configured. Select the generated converter by concrete type instead, and apply the same fix to the BananaReq lookup.
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/net8/SourceGeneration/src/Org.OpenAPITools/Model/FruitReq.cs, line 186:
<comment>This lookup can select a caller-supplied `JsonConverter<AppleReq>` and then fail the cast to `AppleReqJsonConverter`, breaking serialization when custom converters are configured. Select the generated converter by concrete type instead, and apply the same fix to the BananaReq lookup.</comment>
<file context>
@@ -181,6 +181,18 @@ public override void Write(Utf8JsonWriter writer, FruitReq fruitReq, JsonSeriali
+ if (fruitReq.AppleReq != null)
+ {
+ AppleReqJsonConverter converter = (AppleReqJsonConverter) jsonSerializerOptions.Converters.First(c => c.CanConvert(typeof(AppleReq)));
+ converter.WriteProperties(writer, fruitReq.AppleReq, jsonSerializerOptions);
+ }
</file context>
| AppleReqJsonConverter converter = (AppleReqJsonConverter) jsonSerializerOptions.Converters.First(c => c.CanConvert(typeof(AppleReq))); | |
| AppleReqJsonConverter converter = jsonSerializerOptions.Converters.OfType<AppleReqJsonConverter>().First(); |
| converter.WriteProperties(writer, fruit.Apple, jsonSerializerOptions); | ||
| } | ||
|
|
||
| if (fruit.Banana != null) |
There was a problem hiding this comment.
P2: This independent branch can serialize both variants when callers set Fruit.Apple and Fruit.Banana, producing an invalid oneOf payload. Match the deserializer's Apple-first selection with else if (or reject multiple populated variants).
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/net8/NullReferenceTypes/src/Org.OpenAPITools/Model/Fruit.cs, line 215:
<comment>This independent branch can serialize both variants when callers set `Fruit.Apple` and `Fruit.Banana`, producing an invalid `oneOf` payload. Match the deserializer's Apple-first selection with `else if` (or reject multiple populated variants).</comment>
<file context>
@@ -206,6 +206,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>
| if (fruit.Banana != null) | |
| else if (fruit.Banana != null) |
|
|
||
| if (fruitReq.AppleReq != null) | ||
| { | ||
| AppleReqJsonConverter converter = (AppleReqJsonConverter) jsonSerializerOptions.Converters.First(c => c.CanConvert(typeof(AppleReq))); |
There was a problem hiding this comment.
P2: This lookup assumes the first converter claiming AppleReq is the generated AppleReqJsonConverter, but ConfigureJsonOptions allows callers to add or reorder converters. A custom converter can therefore make serialization fail at this cast; select the generated converter by concrete type (and apply the same fix to the BananaReq lookup).
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/net9/Petstore/src/Org.OpenAPITools/Model/FruitReq.cs, line 183:
<comment>This lookup assumes the first converter claiming `AppleReq` is the generated `AppleReqJsonConverter`, but `ConfigureJsonOptions` allows callers to add or reorder converters. A custom converter can therefore make serialization fail at this cast; select the generated converter by concrete type (and apply the same fix to the `BananaReq` lookup).</comment>
<file context>
@@ -178,6 +178,18 @@ public override void Write(Utf8JsonWriter writer, FruitReq fruitReq, JsonSeriali
+ if (fruitReq.AppleReq != null)
+ {
+ AppleReqJsonConverter converter = (AppleReqJsonConverter) jsonSerializerOptions.Converters.First(c => c.CanConvert(typeof(AppleReq)));
+ converter.WriteProperties(writer, fruitReq.AppleReq, jsonSerializerOptions);
+ }
</file context>
| AppleReqJsonConverter converter = (AppleReqJsonConverter) jsonSerializerOptions.Converters.First(c => c.CanConvert(typeof(AppleReq))); | |
| AppleReqJsonConverter converter = (AppleReqJsonConverter) jsonSerializerOptions.Converters.First(c => c is AppleReqJsonConverter); |
| converter.WriteProperties(writer, fruitReq.AppleReq, jsonSerializerOptions); | ||
| } | ||
|
|
||
| if (fruitReq.BananaReq != null) |
There was a problem hiding this comment.
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>
| if (fruitReq.BananaReq != null) | |
| else if (fruitReq.BananaReq != null) |
|
|
||
| if (fruit.Apple != null) | ||
| { | ||
| AppleJsonConverter converter = (AppleJsonConverter) jsonSerializerOptions.Converters.First(c => c.CanConvert(typeof(Apple))); |
There was a problem hiding this comment.
P2: This cast assumes the first converter that supports Apple is the generated AppleJsonConverter, but ConfigureJsonOptions allows callers to register another compatible converter first. Select the generated converter by type (and likewise for Banana) so custom converters do not make Fruit serialization fail.
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/net4.8/FormModels/src/Org.OpenAPITools/Model/Fruit.cs, line 209:
<comment>This cast assumes the first converter that supports `Apple` is the generated `AppleJsonConverter`, but `ConfigureJsonOptions` allows callers to register another compatible converter first. Select the generated converter by type (and likewise for `Banana`) so custom converters do not make `Fruit` serialization fail.</comment>
<file context>
@@ -204,6 +204,18 @@ 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>
| AppleJsonConverter converter = (AppleJsonConverter) jsonSerializerOptions.Converters.First(c => c.CanConvert(typeof(Apple))); | |
| AppleJsonConverter converter = jsonSerializerOptions.Converters.OfType<AppleJsonConverter>().First(); |
|
|
||
| if (fruit.Apple != null) | ||
| { | ||
| AppleJsonConverter converter = (AppleJsonConverter) jsonSerializerOptions.Converters.First(c => c.CanConvert(typeof(Apple))); |
There was a problem hiding this comment.
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>
|
|
||
| if (fruit.Apple != null) | ||
| { | ||
| AppleJsonConverter converter = (AppleJsonConverter) jsonSerializerOptions.Converters.First(c => c.CanConvert(typeof(Apple))); |
There was a problem hiding this comment.
P2: These lookups make FruitJsonConverter depend on every variant converter being registered in the options. A caller that registers only the wrapper converter now fails at First; resolve the generated converter with a fallback (and apply the same fix to Banana and Orange) instead of requiring this undocumented registration invariant.
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/net8/OneOf/src/Org.OpenAPITools/Model/Fruit.cs, line 242:
<comment>These lookups make `FruitJsonConverter` depend on every variant converter being registered in the options. A caller that registers only the wrapper converter now fails at `First`; resolve the generated converter with a fallback (and apply the same fix to Banana and Orange) instead of requiring this undocumented registration invariant.</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>
| { | ||
| writer.WriteStartObject(); | ||
|
|
||
| if (fruit.Apple != null) |
There was a problem hiding this comment.
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>
| {{^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}}))); |
There was a problem hiding this comment.
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>
Description
Fixes #25547
For C#
generichostobjectoneOfmodels without a discriminator, write the active variant's properties alongside properties declared on the wrapper.Tests
oneOfsample.{}.Summary by cubic
Fixes #25547: C#
generichostobjectoneOfmodels without a discriminator now serialize the active variant's properties alongside the wrapper's own properties, instead of producing{}.JsonConverter.mustacheto emitWritePropertiescalls for non-primitiveoneOfvariants before wrapper properties.Written for commit c642c64. Summary will update on new commits.