Skip to content

fix(csharp): use enum rendering functions for array-of-enum properties in JSON converter#23375

Open
Neokil wants to merge 2 commits intoOpenAPITools:masterfrom
Neokil:fix/csharp-enum-array-rendering
Open

fix(csharp): use enum rendering functions for array-of-enum properties in JSON converter#23375
Neokil wants to merge 2 commits intoOpenAPITools:masterfrom
Neokil:fix/csharp-enum-array-rendering

Conversation

@Neokil
Copy link
Copy Markdown

@Neokil Neokil commented Mar 27, 2026

Description

Array-typed properties whose items are enums were serialized and deserialized
using the generic JsonSerializer.Serialize/Deserialize path in the generated
JsonConverter. This ignored the *ValueConverter.ToJsonValue /
*ValueConverter.FromStringOrDefault functions that are used correctly for
scalar enum properties, and caused enum values to be written using the default
.NET JSON behaviour — which outputs the PascalCase enum member name — instead
of the casing defined in the OpenAPI spec.

Example: An OpenAPI spec defining ShippingMethod with value express would
be serialized as "Express" instead of "express" when that enum appeared as
an array item.

Root cause

JsonConverter.mustache had no {{#items.isEnum}} branches in either the read
or write path. Scalar enum properties ({{#isEnum}}) were handled correctly, but
array-of-enum properties fell through to the generic JsonSerializer path.

Changes

  • modules/openapi-generator/src/main/resources/csharp/libraries/generichost/JsonConverter.mustache
    • Read path: added {{#items.isEnum}} branch inside the fallback
      deserialize block — iterates the JSON array and calls
      *ValueConverter.FromStringOrDefault (external enum) or
      *FromStringOrDefault (inner enum) per item.
    • Write path: added {{#items.isEnum}} branch in all four
      required/optional × nullable/non-nullable write cases — emits
      WriteStartArray, calls *ValueConverter.ToJsonValue /
      *ToJsonValue per item (using WriteStringValue for string enums and
      WriteNumberValue for numeric enums), then WriteEndArray.
  • modules/openapi-generator/src/test/resources/3_0/csharp/enum-array.yaml
    — new OpenAPI spec with a model containing both a scalar enum property
    (preferredMethod) and an array-of-enum property (allowedMethods) to
    exercise both the working and previously broken cases.

How to reproduce

Generate a C# client from an OpenAPI spec that contains a model with an
array property whose items are a string enum. Serialize an instance of that
model and observe that the enum values in the array are written in PascalCase
("Express") rather than the value defined in the spec ("express").

Generator

  • Language: C# (csharp)
  • Library: generichost
  • Template file: libraries/generichost/JsonConverter.mustache

PR checklist

  • Read the contribution guidelines.
  • Pull Request title clearly describes the work in the pull request and Pull Request description provides details about how to validate the work. Missing information here may result in delayed response from the community.
  • Run the following to build the project and update samples:
    ./mvnw clean package || exit
    ./bin/generate-samples.sh ./bin/configs/*.yaml || exit
    ./bin/utils/export_docs_generators.sh || exit
    
    (For Windows users, please run the script in WSL)
    Commit all changed files.
    This is important, as CI jobs will verify all generator outputs of your HEAD commit as it would merge with master.
    These must match the expectations made by your contribution.
    You may regenerate an individual generator by passing the relevant config(s) as an argument to the script, for example ./bin/generate-samples.sh bin/configs/java*.
    IMPORTANT: Do NOT purge/delete any folders/files (e.g. tests) when regenerating the samples as manually written tests may be removed.
  • File the PR against the correct branch: master (upcoming 7.x.0 minor release - breaking changes with fallbacks), 8.0.x (breaking changes without fallbacks)
  • If your PR solves a reported issue, reference it using GitHub's linking syntax (e.g., having "fixes #123" present in the PR description)
  • If your PR is targeting a particular programming language, @mention the technical committee members, so they are more likely to review the pull request.

Summary by cubic

Fixes C# generichost JSON converter to handle array-of-enum properties using the enum rendering functions, so clients honor OpenAPI casing (e.g., "express" not "Express"). Also guards JSON null values for enum arrays to keep the reader in sync.

  • Bug Fixes
    • Add items.isEnum handling in the read path to iterate arrays and use *ValueConverter.FromStringOrDefault (or inner enum equivalent); skip processing when the token is Null to avoid Utf8JsonReader desync.
    • Add items.isEnum handling in the write path for all required/optional and nullable cases, emitting arrays and using *ValueConverter.ToJsonValue per item (string or numeric).
    • Add test spec 3_0/csharp/enum-array.yaml covering scalar enum and array-of-enum serialization/deserialization.

Written for commit d0ba616. Summary will update on new commits.

…s in JSON converter

Array-typed properties whose items are enums fell through to the generic
JsonSerializer.Serialize/Deserialize path, which ignored the defined
*ValueConverter.ToJsonValue/*FromStringOrDefault functions and serialized
enum values using the default .NET JSON behaviour (PascalCase names).

Add explicit {{#items.isEnum}} branches in both the read (Deserialize) and
write (Serialize) paths of the generated JsonConverter so that each item in
an enum array is processed via the same converter functions used for scalar
enum properties.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@Neokil
Copy link
Copy Markdown
Author

Neokil commented Mar 27, 2026

FYI: @mandrean (2017/08) @shibayan (2020/02) @Blackclaws (2021/03) @lucamazzanti (2021/05) @iBicha (2023/07)

Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

1 issue found across 2 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<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:256">
P1: Enum-array deserialization lacks token-type/null guard before manual array loop, risking reader desynchronization and invalid token reads.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

…lues

Without the null check, a JSON null value for an array-of-enum property
caused the reader to advance past the null token and attempt to read the
next property as array content, desynchronizing the Utf8JsonReader.
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

1 issue found across 1 file (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<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:255">
P2: Enum-array null handling now skips setting the Option property when the JSON token is Null, so explicit nulls are treated as “not set.” This can cause required-property checks to throw and omit explicit nulls on re-serialization.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

{{^isDate}}
{{^isDateTime}}
{{#items.isEnum}}
if (utf8JsonReader.TokenType != JsonTokenType.Null)
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot Mar 27, 2026

Choose a reason for hiding this comment

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

P2: Enum-array null handling now skips setting the Option property when the JSON token is Null, so explicit nulls are treated as “not set.” This can cause required-property checks to throw and omit explicit nulls on re-serialization.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At modules/openapi-generator/src/main/resources/csharp/libraries/generichost/JsonConverter.mustache, line 255:

<comment>Enum-array null handling now skips setting the Option property when the JSON token is Null, so explicit nulls are treated as “not set.” This can cause required-property checks to throw and omit explicit nulls on re-serialization.</comment>

<file context>
@@ -252,16 +252,19 @@
                             {{#items.isEnum}}
-                            var {{#lambda.camelcase_sanitize_param}}{{name}}{{/lambda.camelcase_sanitize_param}}Items = new List<{{#items.isInnerEnum}}{{classname}}.{{/items.isInnerEnum}}{{{items.datatypeWithEnum}}}>();
-                            while (utf8JsonReader.Read())
+                            if (utf8JsonReader.TokenType != JsonTokenType.Null)
                             {
-                                if (utf8JsonReader.TokenType == JsonTokenType.EndArray)
</file context>
Fix with Cubic

@devhl-labs
Copy link
Copy Markdown
Contributor

devhl-labs commented Mar 28, 2026

Good find, please keep working this. cubic-dev-ai seems to be raising a good point. Also consider exploring the isContainer property and moving your code out of the fallback. Also, a new yaml sample for this isn't really necessary. You can append to the existing Petstore.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants