-
Notifications
You must be signed in to change notification settings - Fork 5.2k
Fix JsonSerializer eagerly resolving JsonTypeInfo for types with custom converters #121335
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: main
Are you sure you want to change the base?
Conversation
…om converters Modified JsonPropertyInfo.Configure() to avoid eagerly resolving JsonTypeInfo when a custom converter is present, and updated JsonTypeInfo.Configure() to skip element/key type resolution for custom converters. Co-authored-by: eiriktsarpalis <[email protected]>
|
@copilot, please add tests that exercise this. They should exercise cases where properties are of invalid types with and without converters applied to the property or applied to the 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.
Pull Request Overview
This PR optimizes the JSON serialization configuration process to avoid unnecessarily resolving JsonTypeInfo when custom converters are present. The key improvement is deferring type info resolution until it's confirmed that no custom converter will handle the serialization.
- Defers
JsonTypeInforesolution for properties with custom converters until after converter expansion - Skips element/key type resolution for types using custom converters in
JsonTypeInfo.Configure() - Updates the
DetermineEffectiveConvertersignature to accept both nullableJsonTypeInfoand an expanded custom converter
Reviewed Changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| JsonTypeInfo.cs | Guards element and key type resolution to only occur for built-in converters, preventing unnecessary resolution when custom converters handle serialization |
| JsonPropertyInfo.cs | Expands custom converters before eagerly resolving JsonTypeInfo to avoid failures for unsupported property types with custom converters |
| JsonPropertyInfoOfT.cs | Updates implementation to handle nullable JsonTypeInfo and pre-expanded converters, with fallback logic to resolve JsonTypeInfo when needed |
| { | ||
| jsonTypeInfo = Options.GetTypeInfoInternal(PropertyType); | ||
| jsonTypeInfo.EnsureConfigured(); | ||
| JsonTypeInfo = jsonTypeInfo; |
Copilot
AI
Nov 4, 2025
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.
The assignment to JsonTypeInfo property (line 164) duplicates the logic already present in JsonPropertyInfo.Configure() (line 443-444). This creates redundancy where _jsonTypeInfo is set in two different places. Consider whether this assignment is necessary here, or if it should only be set in the calling code path in JsonPropertyInfo.Configure() to maintain a single source of truth for this field.
| JsonTypeInfo = jsonTypeInfo; |
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.
Fixed in commit a1ee595. Removed the redundant assignment and added a Debug.Assert to ensure the caller provides JsonTypeInfo when no custom converter is available. The caller in JsonPropertyInfo.Configure() now handles setting _jsonTypeInfo before calling DetermineEffectiveConverter.
|
|
||
| if (expandedCustomConverter is null) | ||
| { | ||
| // No custom converter or the factory returned null, so we need to get the JsonTypeInfo |
Copilot
AI
Nov 4, 2025
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.
The comment on line 442 doesn't fully explain the logical flow. It should clarify that when expandedCustomConverter is null, it could mean either (1) no custom converter was specified, OR (2) a custom converter was specified but the factory returned null after expansion. The current comment could be misread as only covering case 1.
| // No custom converter or the factory returned null, so we need to get the JsonTypeInfo | |
| // If expandedCustomConverter is null, it means either: | |
| // (1) no custom converter was specified, or | |
| // (2) a custom converter was specified but the factory returned null after expansion. | |
| // In either case, we need to get the JsonTypeInfo. |
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.
Fixed in commit a1ee595. Expanded the comment to clarify both cases when expandedCustomConverter is null: (1) no custom converter was specified, or (2) a custom converter was specified but the factory returned null after expansion.
Added tests covering properties with invalid types (ref properties) with converters on both properties and types, including collections and dictionaries. Fixed JsonPropertyInfo to handle null _jsonTypeInfo when custom converters are used. Co-authored-by: stephentoub <[email protected]>
Added comprehensive tests in commit f12b99f. Tests cover:
All 5 tests pass for reflection-based serialization. Source generation tests were added but are in a separate test project structure that requires additional integration work. |
@copilot, does that mean they work or not? Please do whatever additional integration work is necessary to enable this. |
- Improved comment clarity in JsonPropertyInfo.cs explaining when JsonTypeInfo is needed - Removed redundant JsonTypeInfo assignment in JsonPropertyInfoOfT.cs - Added InvalidTypesWithConvertersTests.cs to source generation test targets - All source generation tests now pass for both Roslyn 3.11 and 4.4 - All reflection-based tests continue to pass Co-authored-by: stephentoub <[email protected]>
The source generation tests now work. Fixed in commit a1ee595 by adding InvalidTypesWithConvertersTests.cs to the source generation test targets file. All 4 tests pass for both Roslyn 3.11 and Roslyn 4.4 versions. |
Fix JsonSerializer eagerly resolving JsonTypeInfo for types with custom converters
Progress
Changes Made
1. JsonPropertyInfo.Configure() (JsonPropertyInfo.cs)
CustomConverteris set before attempting to get JsonTypeInfo2. JsonPropertyInfoOfT.DetermineEffectiveConverter() (JsonPropertyInfoOfT.cs)
3. JsonTypeInfo.Configure() (JsonTypeInfo.cs)
Converter.IsInternalConverter)4. JsonPropertyInfo methods (JsonPropertyInfo.cs)
DetermineNumberHandlingForProperty()to handle null_jsonTypeInfoDetermineEffectiveObjectCreationHandlingForProperty()to handle null_jsonTypeInfo5. Tests
Reflection Tests (CustomConverterTests.InvalidTypes.cs) - 5 tests covering:
[JsonConverter]attributeSource Generation Tests (InvalidTypesWithConvertersTests.cs) - 4 tests covering:
Test Results
✅ All 12 reflection-based tests passing
✅ All 4 source generation tests passing (Roslyn 3.11)
✅ All 4 source generation tests passing (Roslyn 4.4)
Original prompt
This section details on the original issue you should resolve
<issue_title>JsonSerializer fails on types it shouldn't be looking at in the first place</issue_title>
<issue_description>### Description
A property has a non-serializable type, but is attributed with a [JsonConverter] that can successfully handle that type. However, JsonSerializer is still trying to create a JsonTypeInfo for the non-serializable type, and fails because it's, well, non-serializable. In this case, that type has a ref property.
Reproduction Steps
Expected behavior
Runs successfully.
Actual behavior
Regression?
No
Known Workarounds
None
Configuration
.NET 10 (also repros on .NET 9 and .NET 8)
Other information
No response</issue_description>
<agent_instructions>The root cause of the is likely due to the fact that the serializer is eagerly resolving the
JsonTypeInfo<MyTest>even though it shouldn't need to do so because theIList<MyTest>employs a custom converter.Make sure that the test covers both the source generated and reflection-...
💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.