From 6a5378f8e33eb6789b06ebeaecb573f3430c906c Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Mon, 13 Oct 2025 13:06:17 +0530 Subject: [PATCH 1/6] fix: handle Object[] in .NET array deserialization Fixed an issue where array deserialization in Model.cs.twig failed when System.Text.Json creates Object[] instead of List. Added a new ToList extension method that properly handles JsonElement, Object[], List, and IEnumerable types, ensuring robust array deserialization across all scenarios. --- .../dotnet/Package/Extensions/Extensions.cs.twig | 13 +++++++++++++ templates/dotnet/Package/Models/Model.cs.twig | 5 +++-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/templates/dotnet/Package/Extensions/Extensions.cs.twig b/templates/dotnet/Package/Extensions/Extensions.cs.twig index d57318077..5ab58f8c7 100644 --- a/templates/dotnet/Package/Extensions/Extensions.cs.twig +++ b/templates/dotnet/Package/Extensions/Extensions.cs.twig @@ -1,6 +1,7 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Linq; using System.Text.Json; namespace {{ spec.title | caseUcfirst }}.Extensions @@ -12,6 +13,18 @@ namespace {{ spec.title | caseUcfirst }}.Extensions return JsonSerializer.Serialize(dict, Client.SerializerOptions); } + public static List ToList(object value) + { + return value switch + { + JsonElement jsonElement => jsonElement.Deserialize>()!, + object[] objArray => objArray.Cast().ToList(), + List list => list, + IEnumerable enumerable => enumerable.ToList(), + _ => throw new InvalidCastException($"Cannot convert {value.GetType()} to List<{typeof(T)}>") + }; + } + public static string ToQueryString(this Dictionary parameters) { var query = new List(); diff --git a/templates/dotnet/Package/Models/Model.cs.twig b/templates/dotnet/Package/Models/Model.cs.twig index b4f8aebeb..d901ddef0 100644 --- a/templates/dotnet/Package/Models/Model.cs.twig +++ b/templates/dotnet/Package/Models/Model.cs.twig @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Text.Json; using System.Text.Json.Serialization; using {{ spec.title | caseUcfirst }}.Enums; +using {{ spec.title | caseUcfirst }}.Extensions; namespace {{ spec.title | caseUcfirst }}.Models { @@ -41,7 +42,7 @@ namespace {{ spec.title | caseUcfirst }}.Models {{ property.name | caseCamel | escapeKeyword | removeDollarSign }}:{{' '}} {%- if property.sub_schema %} {%- if property.type == 'array' -%} - map["{{ property.name }}"] is JsonElement jsonArray{{ loop.index }} ? jsonArray{{ loop.index }}.Deserialize>>()!.Select(it => {{ property.sub_schema | caseUcfirst | overrideIdentifier }}.From(map: it)).ToList() : ((IEnumerable>)map["{{ property.name }}"]).Select(it => {{ property.sub_schema | caseUcfirst | overrideIdentifier }}.From(map: it)).ToList() + Extensions.ToList>(map["{{ property.name }}"]).Select(it => {{ property.sub_schema | caseUcfirst | overrideIdentifier }}.From(map: it)).ToList() {%- else -%} {{ property.sub_schema | caseUcfirst | overrideIdentifier }}.From(map: map["{{ property.name }}"] is JsonElement jsonObj{{ loop.index }} ? jsonObj{{ loop.index }}.Deserialize>()! : (Dictionary)map["{{ property.name }}"]) {%- endif %} @@ -58,7 +59,7 @@ namespace {{ spec.title | caseUcfirst }}.Models {%- endif %} {%- else %} {%- if property.type == 'array' -%} - map["{{ property.name }}"] is JsonElement jsonArrayProp{{ loop.index }} ? jsonArrayProp{{ loop.index }}.Deserialize<{{ property | typeName }}>()! : ({{ property | typeName }})map["{{ property.name }}"] + Extensions.ToList<{{ property | typeName | replace({'List<': '', '>': ''}) }}>(map["{{ property.name }}"]) {%- else %} {%- if property.type == "integer" or property.type == "number" %} {%- if not property.required -%}map["{{ property.name }}"] == null ? null :{% endif %}Convert.To{% if property.type == "integer" %}Int64{% else %}Double{% endif %}(map["{{ property.name }}"]) From 67972c502c673452c4f3bb3e702e1b68aaea3a5a Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Mon, 13 Oct 2025 13:09:43 +0530 Subject: [PATCH 2/6] refactor: simplify type handling for array deserialization Updated DotNet.php to return the element type instead of List wrapper, and simplified Model.cs.twig to use typeName directly with the ToList extension method. --- src/SDK/Language/DotNet.php | 4 ++-- templates/dotnet/Package/Models/Model.cs.twig | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/SDK/Language/DotNet.php b/src/SDK/Language/DotNet.php index 085a503a3..2e7e493f2 100644 --- a/src/SDK/Language/DotNet.php +++ b/src/SDK/Language/DotNet.php @@ -178,8 +178,8 @@ public function getTypeName(array $parameter, array $spec = []): string self::TYPE_BOOLEAN => 'bool', self::TYPE_FILE => 'InputFile', self::TYPE_ARRAY => (!empty(($parameter['array'] ?? [])['type']) && !\is_array($parameter['array']['type'])) - ? 'List<' . $this->getTypeName($parameter['array']) . '>' - : 'List', + ? $this->getTypeName($parameter['array']) + : 'object', self::TYPE_OBJECT => 'object', default => $parameter['type'] }; diff --git a/templates/dotnet/Package/Models/Model.cs.twig b/templates/dotnet/Package/Models/Model.cs.twig index d901ddef0..2e5803126 100644 --- a/templates/dotnet/Package/Models/Model.cs.twig +++ b/templates/dotnet/Package/Models/Model.cs.twig @@ -59,7 +59,7 @@ namespace {{ spec.title | caseUcfirst }}.Models {%- endif %} {%- else %} {%- if property.type == 'array' -%} - Extensions.ToList<{{ property | typeName | replace({'List<': '', '>': ''}) }}>(map["{{ property.name }}"]) + Extensions.ToList<{{ property | typeName }}>(map["{{ property.name }}"]) {%- else %} {%- if property.type == "integer" or property.type == "number" %} {%- if not property.required -%}map["{{ property.name }}"] == null ? null :{% endif %}Convert.To{% if property.type == "integer" %}Int64{% else %}Double{% endif %}(map["{{ property.name }}"]) From aac6cc3d6e7e26a2c10d4b4074f7b73ec99d8c1c Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Mon, 13 Oct 2025 13:19:55 +0530 Subject: [PATCH 3/6] fix: method name --- templates/dotnet/Package/Extensions/Extensions.cs.twig | 2 +- templates/dotnet/Package/Models/Model.cs.twig | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/templates/dotnet/Package/Extensions/Extensions.cs.twig b/templates/dotnet/Package/Extensions/Extensions.cs.twig index 5ab58f8c7..aed26e401 100644 --- a/templates/dotnet/Package/Extensions/Extensions.cs.twig +++ b/templates/dotnet/Package/Extensions/Extensions.cs.twig @@ -13,7 +13,7 @@ namespace {{ spec.title | caseUcfirst }}.Extensions return JsonSerializer.Serialize(dict, Client.SerializerOptions); } - public static List ToList(object value) + public static List ConvertToList(object value) { return value switch { diff --git a/templates/dotnet/Package/Models/Model.cs.twig b/templates/dotnet/Package/Models/Model.cs.twig index 2e5803126..46a0cf036 100644 --- a/templates/dotnet/Package/Models/Model.cs.twig +++ b/templates/dotnet/Package/Models/Model.cs.twig @@ -42,7 +42,7 @@ namespace {{ spec.title | caseUcfirst }}.Models {{ property.name | caseCamel | escapeKeyword | removeDollarSign }}:{{' '}} {%- if property.sub_schema %} {%- if property.type == 'array' -%} - Extensions.ToList>(map["{{ property.name }}"]).Select(it => {{ property.sub_schema | caseUcfirst | overrideIdentifier }}.From(map: it)).ToList() + Extensions.ConvertToList>(map["{{ property.name }}"]).Select(it => {{ property.sub_schema | caseUcfirst | overrideIdentifier }}.From(map: it)).ToList() {%- else -%} {{ property.sub_schema | caseUcfirst | overrideIdentifier }}.From(map: map["{{ property.name }}"] is JsonElement jsonObj{{ loop.index }} ? jsonObj{{ loop.index }}.Deserialize>()! : (Dictionary)map["{{ property.name }}"]) {%- endif %} @@ -59,7 +59,7 @@ namespace {{ spec.title | caseUcfirst }}.Models {%- endif %} {%- else %} {%- if property.type == 'array' -%} - Extensions.ToList<{{ property | typeName }}>(map["{{ property.name }}"]) + Extensions.ConvertToList<{{ property | typeName }}>(map["{{ property.name }}"]) {%- else %} {%- if property.type == "integer" or property.type == "number" %} {%- if not property.required -%}map["{{ property.name }}"] == null ? null :{% endif %}Convert.To{% if property.type == "integer" %}Int64{% else %}Double{% endif %}(map["{{ property.name }}"]) From 1ee05407e42bc217447b5202f1648e556dd20577 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Mon, 13 Oct 2025 14:31:44 +0530 Subject: [PATCH 4/6] fix: issue 3 --- src/SDK/Language/DotNet.php | 4 ++-- templates/dotnet/Package/Models/Model.cs.twig | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/SDK/Language/DotNet.php b/src/SDK/Language/DotNet.php index 2e7e493f2..085a503a3 100644 --- a/src/SDK/Language/DotNet.php +++ b/src/SDK/Language/DotNet.php @@ -178,8 +178,8 @@ public function getTypeName(array $parameter, array $spec = []): string self::TYPE_BOOLEAN => 'bool', self::TYPE_FILE => 'InputFile', self::TYPE_ARRAY => (!empty(($parameter['array'] ?? [])['type']) && !\is_array($parameter['array']['type'])) - ? $this->getTypeName($parameter['array']) - : 'object', + ? 'List<' . $this->getTypeName($parameter['array']) . '>' + : 'List', self::TYPE_OBJECT => 'object', default => $parameter['type'] }; diff --git a/templates/dotnet/Package/Models/Model.cs.twig b/templates/dotnet/Package/Models/Model.cs.twig index 46a0cf036..911f5e5cc 100644 --- a/templates/dotnet/Package/Models/Model.cs.twig +++ b/templates/dotnet/Package/Models/Model.cs.twig @@ -59,7 +59,7 @@ namespace {{ spec.title | caseUcfirst }}.Models {%- endif %} {%- else %} {%- if property.type == 'array' -%} - Extensions.ConvertToList<{{ property | typeName }}>(map["{{ property.name }}"]) + Extensions.ConvertToList<{{ property | typeName | replace({'List<': '', '>': ''}) }}>(map["{{ property.name }}"]) {%- else %} {%- if property.type == "integer" or property.type == "number" %} {%- if not property.required -%}map["{{ property.name }}"] == null ? null :{% endif %}Convert.To{% if property.type == "integer" %}Int64{% else %}Double{% endif %}(map["{{ property.name }}"]) From 951a27a86a4b96a0c3f75e3a0fd5d0d77f1e0e84 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Mon, 13 Oct 2025 14:41:19 +0530 Subject: [PATCH 5/6] fix: issue 4 --- templates/dotnet/Package/Extensions/Extensions.cs.twig | 2 +- templates/dotnet/Package/Models/Model.cs.twig | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/templates/dotnet/Package/Extensions/Extensions.cs.twig b/templates/dotnet/Package/Extensions/Extensions.cs.twig index aed26e401..c76e8ff4f 100644 --- a/templates/dotnet/Package/Extensions/Extensions.cs.twig +++ b/templates/dotnet/Package/Extensions/Extensions.cs.twig @@ -13,7 +13,7 @@ namespace {{ spec.title | caseUcfirst }}.Extensions return JsonSerializer.Serialize(dict, Client.SerializerOptions); } - public static List ConvertToList(object value) + public static List ConvertToList(this object value) { return value switch { diff --git a/templates/dotnet/Package/Models/Model.cs.twig b/templates/dotnet/Package/Models/Model.cs.twig index 911f5e5cc..e3f0bd132 100644 --- a/templates/dotnet/Package/Models/Model.cs.twig +++ b/templates/dotnet/Package/Models/Model.cs.twig @@ -42,7 +42,7 @@ namespace {{ spec.title | caseUcfirst }}.Models {{ property.name | caseCamel | escapeKeyword | removeDollarSign }}:{{' '}} {%- if property.sub_schema %} {%- if property.type == 'array' -%} - Extensions.ConvertToList>(map["{{ property.name }}"]).Select(it => {{ property.sub_schema | caseUcfirst | overrideIdentifier }}.From(map: it)).ToList() + map["{{ property.name }}"].ConvertToList>().Select(it => {{ property.sub_schema | caseUcfirst | overrideIdentifier }}.From(map: it)).ToList() {%- else -%} {{ property.sub_schema | caseUcfirst | overrideIdentifier }}.From(map: map["{{ property.name }}"] is JsonElement jsonObj{{ loop.index }} ? jsonObj{{ loop.index }}.Deserialize>()! : (Dictionary)map["{{ property.name }}"]) {%- endif %} @@ -59,7 +59,7 @@ namespace {{ spec.title | caseUcfirst }}.Models {%- endif %} {%- else %} {%- if property.type == 'array' -%} - Extensions.ConvertToList<{{ property | typeName | replace({'List<': '', '>': ''}) }}>(map["{{ property.name }}"]) + map["{{ property.name }}"].ConvertToList<{{ property | typeName | replace({'List<': '', '>': ''}) }}>() {%- else %} {%- if property.type == "integer" or property.type == "number" %} {%- if not property.required -%}map["{{ property.name }}"] == null ? null :{% endif %}Convert.To{% if property.type == "integer" %}Int64{% else %}Double{% endif %}(map["{{ property.name }}"]) From 0b9d6d6ebfe858a137863603d41f312ff8c3b109 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Mon, 13 Oct 2025 14:45:07 +0530 Subject: [PATCH 6/6] Update templates/dotnet/Package/Extensions/Extensions.cs.twig Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- templates/dotnet/Package/Extensions/Extensions.cs.twig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/dotnet/Package/Extensions/Extensions.cs.twig b/templates/dotnet/Package/Extensions/Extensions.cs.twig index c76e8ff4f..0ac19f7ce 100644 --- a/templates/dotnet/Package/Extensions/Extensions.cs.twig +++ b/templates/dotnet/Package/Extensions/Extensions.cs.twig @@ -17,7 +17,7 @@ namespace {{ spec.title | caseUcfirst }}.Extensions { return value switch { - JsonElement jsonElement => jsonElement.Deserialize>()!, + JsonElement jsonElement => jsonElement.Deserialize>() ?? throw new InvalidCastException($"Cannot deserialize {jsonElement} to List<{typeof(T)}>."), object[] objArray => objArray.Cast().ToList(), List list => list, IEnumerable enumerable => enumerable.ToList(),