-
Notifications
You must be signed in to change notification settings - Fork 185
fix: (.NET) Improve json De/serialization #1138
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
0a48f7a
b44f0b1
4c6b9f7
b071cbc
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 | ||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -1,6 +1,5 @@ | ||||||||||||||||||||||||||||
{% macro sub_schema(property) %}{% if property.sub_schema %}{% if property.type == 'array' %}List<{{property.sub_schema | caseUcfirst | overrideIdentifier}}>{% else %}{{property.sub_schema | caseUcfirst | overrideIdentifier}}{% endif %}{% else %}{{property | typeName}}{% endif %}{% if not property.required %}?{% endif %}{% endmacro %} | ||||||||||||||||||||||||||||
{% macro property_name(definition, property) %}{{ property.name | caseUcfirst | removeDollarSign | escapeKeyword }}{% endmacro %} | ||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||
using System; | ||||||||||||||||||||||||||||
using System.Linq; | ||||||||||||||||||||||||||||
using System.Collections.Generic; | ||||||||||||||||||||||||||||
|
@@ -40,31 +39,29 @@ namespace {{ spec.title | caseUcfirst }}.Models | |||||||||||||||||||||||||||
public static {{ definition.name | caseUcfirst | overrideIdentifier }} From(Dictionary<string, object> map) => new {{ definition.name | caseUcfirst | overrideIdentifier }}( | ||||||||||||||||||||||||||||
{%~ for property in definition.properties %} | ||||||||||||||||||||||||||||
{{ property.name | caseCamel | escapeKeyword | removeDollarSign }}:{{' '}} | ||||||||||||||||||||||||||||
{%- if not property.required -%}map.ContainsKey("{{ property.name }}") ? {% endif %} | ||||||||||||||||||||||||||||
{%- if property.sub_schema %} | ||||||||||||||||||||||||||||
{%- if property.type == 'array' -%} | ||||||||||||||||||||||||||||
map["{{ property.name }}"] is JsonElement jsonArray{{ loop.index }} ? jsonArray{{ loop.index }}.Deserialize<List<Dictionary<string, object>>>()!.Select(it => {{ property.sub_schema | caseUcfirst | overrideIdentifier }}.From(map: it)).ToList() : ((IEnumerable<Dictionary<string, object>>)map["{{ property.name }}"]).Select(it => {{ property.sub_schema | caseUcfirst | overrideIdentifier }}.From(map: it)).ToList() | ||||||||||||||||||||||||||||
((IEnumerable<object>)map["{{ property.name }}"]).Select(it => {{ property.sub_schema | caseUcfirst | overrideIdentifier }}.From(map: (Dictionary<string, object>)it)).ToList() | ||||||||||||||||||||||||||||
{%- else -%} | ||||||||||||||||||||||||||||
{{ property.sub_schema | caseUcfirst | overrideIdentifier }}.From(map: map["{{ property.name }}"] is JsonElement jsonObj{{ loop.index }} ? jsonObj{{ loop.index }}.Deserialize<Dictionary<string, object>>()! : (Dictionary<string, object>)map["{{ property.name }}"]) | ||||||||||||||||||||||||||||
{{ property.sub_schema | caseUcfirst | overrideIdentifier }}.From(map: (Dictionary<string, object>)map["{{ property.name }}"]) | ||||||||||||||||||||||||||||
{%- endif %} | ||||||||||||||||||||||||||||
Comment on lines
+45
to
48
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. 🛠️ Refactor suggestion Safer casts for sub-schemas (avoid InvalidCast when value types deviate). Use safe casts for optionals to prevent InvalidCastException when value is null/mismatched. - ((IEnumerable<object>)map["{{ property.name }}"]).Select(it => {{ property.sub_schema | caseUcfirst | overrideIdentifier }}.From(map: (Dictionary<string, object>)it)).ToList()
+ ((map["{{ property.name }}"] as IEnumerable<object>) ?? Array.Empty<object>())
+ .Select(it => {{ property.sub_schema | caseUcfirst | overrideIdentifier }}.From(map: (Dictionary<string, object>)it))
+ .ToList() - {{ property.sub_schema | caseUcfirst | overrideIdentifier }}.From(map: (Dictionary<string, object>)map["{{ property.name }}"])
+ (map["{{ property.name }}"] as Dictionary<string, object>) is { } obj
+ ? {{ property.sub_schema | caseUcfirst | overrideIdentifier }}.From(map: obj)
+ : null Note: The second change pairs with the Line 42 null-guard; harmless for required props, safer for optionals. 📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents
|
||||||||||||||||||||||||||||
{%- else %} | ||||||||||||||||||||||||||||
{%- if property.type == 'array' -%} | ||||||||||||||||||||||||||||
map["{{ property.name }}"] is JsonElement jsonArrayProp{{ loop.index }} ? jsonArrayProp{{ loop.index }}.Deserialize<{{ property | typeName }}>()! : ({{ property | typeName }})map["{{ property.name }}"] | ||||||||||||||||||||||||||||
((IEnumerable<object>)map["{{ property.name }}"]).Select(x => {% if property.items.type == "string" %}x?.ToString(){% elseif property.items.type == "integer" %}{% if not property.required %}x == null ? (long?)null : {% endif %}Convert.ToInt64(x){% elseif property.items.type == "number" %}{% if not property.required %}x == null ? (double?)null : {% endif %}Convert.ToDouble(x){% elseif property.items.type == "boolean" %}{% if not property.required %}x == null ? (bool?)null : {% endif %}(bool)x{% else %}x{% endif %}).{% if property.items.type == "string" and property.required %}Where(x => x != null).{% endif %}ToList()! | ||||||||||||||||||||||||||||
Fellmonkey marked this conversation as resolved.
Show resolved
Hide resolved
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 null-forgiving operator
Suggested change
Copilot uses AI. Check for mistakes. Positive FeedbackNegative Feedback |
||||||||||||||||||||||||||||
{%- 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 }}"]) | ||||||||||||||||||||||||||||
{%- if not property.required -%}map["{{ property.name }}"] == null ? null : {% endif %}Convert.To{% if property.type == "integer" %}Int64{% else %}Double{% endif %}(map["{{ property.name }}"]) | ||||||||||||||||||||||||||||
{%- else %} | ||||||||||||||||||||||||||||
{%- if property.type == "boolean" -%} | ||||||||||||||||||||||||||||
({{ property | typeName }}{% if not property.required %}?{% endif %})map["{{ property.name }}"] | ||||||||||||||||||||||||||||
{%- else %} | ||||||||||||||||||||||||||||
{%- if not property.required -%} | ||||||||||||||||||||||||||||
map.TryGetValue("{{ property.name }}", out var {{ property.name | caseCamel | escapeKeyword | removeDollarSign }}) ? {{ property.name | caseCamel | escapeKeyword | removeDollarSign }}?.ToString() : null | ||||||||||||||||||||||||||||
{%- else -%} | ||||||||||||||||||||||||||||
map["{{ property.name }}"].ToString() | ||||||||||||||||||||||||||||
{%- endif %} | ||||||||||||||||||||||||||||
{%- else -%} | ||||||||||||||||||||||||||||
map["{{ property.name }}"]{% if not property.required %}?{% endif %}.ToString() | ||||||||||||||||||||||||||||
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. 🛠️ Refactor suggestion Preserve ISO strings when converter produced DateTime/Offset. If the converter inferred DateTime/Offset, ToString() becomes culture-dependent. Emit ISO 8601 for round-trip. - map["{{ property.name }}"]{% if not property.required %}?{% endif %}.ToString()
+ map["{{ property.name }}"] switch
+ {
+ DateTimeOffset dto => dto.ToString("O"),
+ DateTime dt => dt.ToUniversalTime().ToString("O"),
+ _ => map["{{ property.name }}"]{% if not property.required %}?{% endif %}.ToString()
+ } 📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents
|
||||||||||||||||||||||||||||
{%- endif %} | ||||||||||||||||||||||||||||
{%~ endif %} | ||||||||||||||||||||||||||||
{%~ endif %} | ||||||||||||||||||||||||||||
{%~ endif %} | ||||||||||||||||||||||||||||
{%- if not property.required %} : null{% endif %} | ||||||||||||||||||||||||||||
{%- if not loop.last or (loop.last and definition.additionalProperties) %}, | ||||||||||||||||||||||||||||
{%~ endif %} | ||||||||||||||||||||||||||||
{%~ endfor %} | ||||||||||||||||||||||||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -23,4 +23,4 @@ namespace {{ spec.title | caseUcfirst }} | |
ChunksUploaded = chunksUploaded; | ||
} | ||
} | ||
} | ||
} |
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.
Optional key guard misses the “present-but-null” case (can throw).
If the key exists with null, casts below will NRE/ICE. Guard for null too.
📝 Committable suggestion
🤖 Prompt for AI Agents