Skip to content

Conversation

ChiragAgg5k
Copy link
Member

@ChiragAgg5k ChiragAgg5k commented Oct 13, 2025

Summary

Fixed an issue where array deserialization in Model.cs.twig failed when System.Text.Json creates Object[] instead of List.

Problem

In Model.cs.twig, the array deserialization logic didn't handle Object[] properly. When System.Text.Json deserializes a dictionary with arrays, it creates Object[], but the code tried to directly cast to List, causing InvalidCastException.

Solution

  • Added a new ToList<T> extension method in Extensions.cs.twig that properly handles:
    • JsonElement (deserializes to List)
    • Object[] (casts elements to T and converts to List)
    • List (returns as-is)
    • IEnumerable (converts to List)
    • Throws InvalidCastException for unsupported types
  • Updated Model.cs.twig to use the new extension method for all array properties
  • Simplified type handling in DotNet.php to return element types instead of List wrappers
  • Updated Model.cs.twig to use typeName directly with the ToList extension method

Changes

  • templates/dotnet/Package/Extensions/Extensions.cs.twig: Added ToList extension method
  • templates/dotnet/Package/Models/Model.cs.twig: Updated array deserialization to use ToList
  • src/SDK/Language/DotNet.php: Simplified getTypeName to return element types

Test plan

  • Test array deserialization with various scenarios (JsonElement, Object[], List)
  • Verify that nested objects with array properties deserialize correctly
  • Ensure backward compatibility with existing code

Summary by CodeRabbit

  • Refactor

    • Centralized list-conversion utility added to normalize array inputs across models, reducing duplicate code and improving maintainability.
    • Models now use the common helper to build lists for both primitive and complex items.
  • Bug Fixes

    • Improved handling of varied input shapes (JSON elements, arrays, enumerables) when converting to lists.
    • Reduced invalid-cast scenarios and increased reliability when deserializing array properties for more consistent runtime behavior.

Fixed an issue where array deserialization in Model.cs.twig failed when
System.Text.Json creates Object[] instead of List<T>. Added a new ToList<T>
extension method that properly handles JsonElement, Object[], List<T>, and
IEnumerable<T> types, ensuring robust array deserialization across all scenarios.
Updated DotNet.php to return the element type instead of List<T> wrapper,
and simplified Model.cs.twig to use typeName directly with the ToList<T>
extension method.
Copy link
Contributor

coderabbitai bot commented Oct 13, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

Adds using System.Linq and a new extension public static List<T> ConvertToList<T>(this object value) (templates/dotnet/Package/Extensions/Extensions.cs.twig) that handles JsonElement deserialization, object[], List<T>, and IEnumerable<T>, throwing InvalidCastException for unsupported types. Updates model generation (templates/dotnet/Package/Models/Model.cs.twig) to import the Extensions namespace and replace per-element JsonElement/array conversion with ConvertToList<T> for complex and primitive array properties. No changes to exported/public declarations.

Suggested reviewers

  • abnegate

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title clearly summarizes the primary change by indicating a bug fix for handling Object[] in .NET array deserialization, which directly reflects the added ConvertToList extension and its use in the model template. It is concise, specific, and relates to the main purpose of the pull request without extraneous details.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.
✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix-dotnet-array-deserialization

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 951a27a and 0b9d6d6.

📒 Files selected for processing (1)
  • templates/dotnet/Package/Extensions/Extensions.cs.twig (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • templates/dotnet/Package/Extensions/Extensions.cs.twig

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (2)
templates/dotnet/Package/Extensions/Extensions.cs.twig (2)

16-26: Add null check for input value.

The method should handle null input explicitly rather than throwing NullReferenceException from pattern matching.

Apply this diff to add null handling:

 public static List<T> ToList<T>(object value)
 {
+    if (value == null)
+    {
+        throw new ArgumentNullException(nameof(value), "Cannot convert null to List<T>");
+    }
+
     return value switch
     {
         JsonElement jsonElement => jsonElement.Deserialize<List<T>>()!,
         object[] objArray => objArray.Cast<T>().ToList(),
         List<T> list => list,
         IEnumerable<T> enumerable => enumerable.ToList(),
         _ => throw new InvalidCastException($"Cannot convert {value.GetType()} to List<{typeof(T)}>")
     };
 }

21-21: Document the casting behavior for object[] to List.

The Cast<T>() operation will throw InvalidCastException if any array element cannot be cast to T. This is appropriate for catching type mismatches early, but consider documenting this behavior or adding a try-catch for clearer error messages.

-        object[] objArray => objArray.Cast<T>().ToList(),
+        object[] objArray => objArray.Cast<T>().ToList(), // Throws InvalidCastException if elements cannot be cast to T
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d1f9097 and 67972c5.

📒 Files selected for processing (3)
  • src/SDK/Language/DotNet.php (1 hunks)
  • templates/dotnet/Package/Extensions/Extensions.cs.twig (2 hunks)
  • templates/dotnet/Package/Models/Model.cs.twig (3 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/SDK/Language/DotNet.php (5)
src/SDK/Language/Python.php (1)
  • getTypeName (236-264)
src/SDK/Language/Kotlin.php (1)
  • getTypeName (107-130)
src/SDK/Language/Dart.php (1)
  • getTypeName (128-161)
src/SDK/Language/Deno.php (1)
  • getTypeName (133-155)
src/SDK/Language/Go.php (1)
  • getTypeName (139-160)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (20)
  • GitHub Check: build (8.3, WebChromium)
  • GitHub Check: build (8.3, AppleSwift56)
  • GitHub Check: build (8.3, WebNode)
  • GitHub Check: build (8.3, Python310)
  • GitHub Check: dotnet (server)
  • GitHub Check: build (8.3, PHP83)
  • GitHub Check: build (8.3, FlutterStable)
  • GitHub Check: build (8.3, Python311)
  • GitHub Check: build (8.3, Node18)
  • GitHub Check: swift (server)
  • GitHub Check: kotlin (server)
  • GitHub Check: build (8.3, DotNet80)
  • GitHub Check: build (8.3, Go118)
  • GitHub Check: build (8.3, DotNet60)
  • GitHub Check: flutter (client)
  • GitHub Check: build (8.3, CLINode18)
  • GitHub Check: build (8.3, Android14Java17)
  • GitHub Check: android (client)
  • GitHub Check: apple (client)
  • GitHub Check: build (8.3, Android5Java17)
🔇 Additional comments (4)
templates/dotnet/Package/Models/Model.cs.twig (3)

8-8: LGTM! Extension namespace import is necessary.

The import enables the use of the new ToList<T> extension method throughout the model deserialization logic.


45-45: Array deserialization logic correctly uses Extensions.ToList.

The change to use Extensions.ToList<Dictionary<string, object>> properly handles the case where System.Text.Json produces Object[] instead of List<T>, addressing the core issue described in the PR.


62-62: Verify typeName filter returns element type consistently.

This line depends on the typeName filter (which calls getTypeName) returning just the element type (e.g., string) rather than the full list type (e.g., List<string>). Ensure the getTypeName change in src/SDK/Language/DotNet.php is correct and that property declarations are not broken.

As noted in the review of src/SDK/Language/DotNet.php, verify that the sub_schema function properly wraps array types in List<> for property declarations, as the deserialization logic here depends on getTypeName returning element types.

src/SDK/Language/DotNet.php (1)

180-182: Verify generated C# model array property declarations use List<ElementType>
No .cs model files were found in the repository to confirm this change. Manually verify in the generated code that array properties are declared as List<…> rather than the raw element type.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (1)
templates/dotnet/Package/Extensions/Extensions.cs.twig (1)

23-26: Cast may throw InvalidCastException for incompatible element types.

When objArray contains elements that cannot be cast to T, Cast<T>() will throw InvalidCastException at enumeration time, which could provide a less clear error message than the explicit check at line 38.

Consider adding validation before casting to provide a clearer error message for type mismatches.

Apply this diff to add validation:

 if (value is object[] objArray)
 {
+    if (objArray.Length > 0 && objArray.Any(item => item != null && !(item is T)))
+    {
+        throw new InvalidCastException($"Cannot convert object[] with incompatible elements to List<{typeof(T)}>");
+    }
     return objArray.Cast<T>().ToList();
 }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d1f9097 and 203e7f0.

📒 Files selected for processing (3)
  • src/SDK/Language/DotNet.php (1 hunks)
  • templates/dotnet/Package/Extensions/Extensions.cs.twig (2 hunks)
  • templates/dotnet/Package/Models/Model.cs.twig (3 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/SDK/Language/DotNet.php (8)
src/SDK/Language/Kotlin.php (1)
  • getTypeName (107-130)
src/SDK/Language/Swift.php (1)
  • getTypeName (302-326)
src/SDK/Language/Dart.php (1)
  • getTypeName (128-161)
src/SDK/Language/Web.php (1)
  • getTypeName (180-243)
src/SDK/Language/CLI.php (1)
  • getTypeName (352-376)
src/SDK/Language/Go.php (1)
  • getTypeName (139-160)
src/SDK/Language/Node.php (1)
  • getTypeName (15-78)
src/SDK/Language/PHP.php (1)
  • getTypeName (253-271)
🔇 Additional comments (5)
templates/dotnet/Package/Models/Model.cs.twig (3)

8-8: LGTM!

The addition of the Extensions namespace import enables access to the new ToList<T> extension method used throughout the model deserialization logic.


62-62: typeName filter correctly returns element types for arrays
TYPE_ARRAY delegates to getTypeName($parameter['array']), and getTypeName maps to the primitive or model type.


45-45: Confirm From method parameter name
Verify that the generated From(Dictionary<string, object>) method declares its dictionary parameter as map; using .From(map: it) will fail if the parameter name differs.

src/SDK/Language/DotNet.php (2)

174-186: Review getParamExample compatibility with the type change.

The getParamExample method at lines 241-311 references $this->getTypeName($param['array']) for array types (lines 274, 276). With the new behavior where getTypeName returns element types instead of List<T>, verify that the example generation still produces valid C# code.

Based on the code at lines 274-276, it constructs new List<{elementType}>(), which should still work correctly since it now uses the element type directly.


180-182: Update the parameter macro to emit List<T> for arrays
In templates/dotnet/base/utils.twig, the line

{{ parameter | typeName }}

now yields only the element type for arrays. Wrap array parameters in List<{{ elementType }}> (or List<object> when no inner type) so generated method signatures remain correct.
critical_fixes_required

@ChiragAgg5k ChiragAgg5k force-pushed the fix-dotnet-array-deserialization branch from 203e7f0 to aac6cc3 Compare October 13, 2025 07:50
@ChiragAgg5k ChiragAgg5k requested a review from Copilot October 13, 2025 09:13
Copy link
Contributor

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR fixes array deserialization issues in .NET code generation where System.Text.Json creates Object[] instead of List, causing InvalidCastException. The solution introduces a centralized extension method to handle various array input types and simplifies model property initialization.

  • Added ConvertToList extension method to handle JsonElement, Object[], List, and IEnumerable inputs
  • Updated Model.cs.twig to use the new extension method for consistent array property handling
  • Simplified array property type handling by removing List wrapper logic

Reviewed Changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
templates/dotnet/Package/Extensions/Extensions.cs.twig Added ConvertToList extension method for robust array conversion
templates/dotnet/Package/Models/Model.cs.twig Updated array property initialization to use ConvertToList extension

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
@ChiragAgg5k ChiragAgg5k merged commit a20b20c into master Oct 13, 2025
52 checks passed
@ChiragAgg5k ChiragAgg5k deleted the fix-dotnet-array-deserialization branch October 13, 2025 09:20
@Fellmonkey
Copy link
Contributor

@ChiragAgg5k it's because of the converter

old variant (current in main)

case JsonTokenType.StartArray:
    return JsonSerializer.Deserialize<object[]>(ref reader, options)!;

in pr

case JsonValueKind.Array:
    var list = new List<object?>();
    foreach (var item in element.EnumerateArray())
    {
        list.Add(ConvertElement(item));
    }
    return list;

Although the current solution is universal, I will add a revised version to my PR.

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.

3 participants