-
-
Notifications
You must be signed in to change notification settings - Fork 7.7k
[csharp] fix: conditional serialization invents value-type defaults on all-optional models #24877
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,63 @@ | ||
| openapi: 3.0.3 | ||
| info: | ||
| title: conditional serialization value-type flags | ||
| version: 1.0.0 | ||
| paths: | ||
| /things: | ||
| post: | ||
| operationId: createThing | ||
| requestBody: | ||
| required: true | ||
| content: | ||
| application/json: | ||
| schema: | ||
| $ref: '#/components/schemas/CreateThingCommand' | ||
| responses: | ||
| '200': | ||
| description: created | ||
| patch: | ||
| operationId: updateThing | ||
| requestBody: | ||
| required: true | ||
| content: | ||
| application/json: | ||
| schema: | ||
| $ref: '#/components/schemas/UpdateThingCommand' | ||
| responses: | ||
| '200': | ||
| description: updated | ||
| components: | ||
| schemas: | ||
| UpdateThingCommand: | ||
| type: object | ||
| description: an all-optional partial-update command; its only constructor is the public one | ||
| properties: | ||
| autoRenew: | ||
| type: boolean | ||
| period: | ||
|
cubic-dev-ai[bot] marked this conversation as resolved.
|
||
| type: integer | ||
| graceDays: | ||
| type: integer | ||
| nullable: true | ||
| status: | ||
| type: string | ||
| enum: [active, suspended] | ||
| renewalMode: | ||
| type: string | ||
| enum: [manual, automatic] | ||
| nullable: true | ||
| note: | ||
| type: string | ||
| CreateThingCommand: | ||
| type: object | ||
| description: required value type keeps its non-nullable constructor parameter | ||
| required: | ||
| - name | ||
| - autoRenew | ||
| properties: | ||
| name: | ||
| type: string | ||
| autoRenew: | ||
| type: boolean | ||
| note: | ||
| type: string | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -38,11 +38,11 @@ public partial class ApiResponse : IEquatable<ApiResponse>, IValidatableObject | |
| /// <param name="code">code.</param> | ||
| /// <param name="type">type.</param> | ||
| /// <param name="message">message.</param> | ||
| public ApiResponse(int code = default, string type = default, string message = default) | ||
| public ApiResponse(int? code = default, string type = default, string message = default) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P2: Changing Prompt for AI agents
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The CLR signature does change, but I am deliberately not adding a compatibility overload. The overload would break source compatibility and bring the bug back. I tested this with the exact pair it would generate (C# 12, net8.0): public ApiResponse(int? code = default, string type = default, string message = default) // new
public ApiResponse(int code = default, string type = default, string message = default) // compat overload
A consumer who recompiles against the regenerated model (the normal workflow for generated code) picks up the new constructor with no source changes, because |
||
| { | ||
| this._Code = code; | ||
| if (this.Code != null) | ||
| if (code != null) | ||
| { | ||
| this._Code = code.Value; | ||
| this._flagCode = true; | ||
| } | ||
| this._Type = type; | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
P2: For nullable value-type properties (spec
nullable: true), the new?appended here may collide with an existing?already present indatatypeWithEnum/dataType, producing an invalidint??parameter type (compile error) instead ofint?. The C# codegen appends?to nullable value-type dataTypes (CSharpClientCodegen around line 654:if (isSupportNullable() && ModelUtils.isNullable(p) && this.getNullableTypes().contains(type))), so the type may already end in?. Unlike the enum branch, which is unguarded here, the new value-type branch should only append?whendatatypeWithEnumdoes not already end in?. The conditional-serialization sample only covers plainbool/integer(non-nullable), so this path is untested. Please verify that an optional nullable value-type property does not generate??and guard the?accordingly (e.g. also require the type not to already end with?).Prompt for AI agents
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Checked this against the generator; the
T??case does not occur. The appended?sits inside{{#vendorExtensions.x-csharp-value-type}}, andCSharpClientCodegen.patchPropertysets that flag only whengetNullableTypes()containsproperty.dataType. That set holds the bare names (int,bool,DateTime, ...), so anullable: trueproperty whose dataType is alreadyint?never gets the flag. It keeps the original template path:int? graceDaysas the parameter, with the existingif (this.GraceDays != null)check, which is meaningful for a nullable type. Enums never end in?indatatypeWithEnum(the name isStatusEnumeven when nullable), so the enum branch appends exactly one?, as it did before this PR.I verified this by generating a model with nullable int/bool, an inline enum, a nullable inline enum and a
$refenum forhttpclient,restsharpandunityWebRequest, with and withoutnullableReferenceTypes. No??appears, and the httpclient/net8.0 output builds with 0 errors and 0 warnings.In dfc897f I added a nullable integer and a nullable enum to the test fixture, and asserted that no
??is emitted and that the nullable value type keeps its original null check. That locks this in, so a future change to the value-type extension cannot silently regress it.