-
Couldn't load subscription status.
- Fork 5.2k
Add built-in tuple serialization support to System.Text.Json #120906
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
Conversation
Co-authored-by: eiriktsarpalis <2813363+eiriktsarpalis@users.noreply.github.com>
Co-authored-by: eiriktsarpalis <2813363+eiriktsarpalis@users.noreply.github.com>
Co-authored-by: eiriktsarpalis <2813363+eiriktsarpalis@users.noreply.github.com>
Co-authored-by: eiriktsarpalis <2813363+eiriktsarpalis@users.noreply.github.com>
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.
@copilot you're missing tests for the case of reflection.
| var longTuple = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); | ||
| string json = JsonSerializer.Serialize(longTuple); | ||
| // Should serialize as Item1 through Item10, not nested Rest objects | ||
| Assert.Contains("\"Item1\":1", json); |
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.
@copilot Please assert the full expected payload using Assert.Equals.
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.
Updated to use Assert.Equal with full expected payload for all tuple arities (8, 10, 16 elements). Commit: b325fcc
| public virtual void LongValueTupleSerializes() | ||
| { | ||
| // Test that long tuples (> 7 elements) flatten the Rest field properly | ||
| var longTuple = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); |
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.
Please add more tuple arities
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.
Added tests for 8, 10, and 16-element tuples to cover boundary cases and nested Rest handling. Commit: b325fcc
Co-authored-by: eiriktsarpalis <2813363+eiriktsarpalis@users.noreply.github.com>
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.
@copilot Please address all feedback and fix failures in CI.
| return false; | ||
| } | ||
|
|
||
| string typeFullName = type.ConstructedFrom.ToDisplayString(); |
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.
This looks allocatey; consider simply checking if the type is part of mscorlib (by accepting Compilation as an additional parameter) and then verifying the name and namespace of 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.
Updated to check assembly name and namespace instead of using ToDisplayString. Commit: 9331f2a
| public static bool IsKeyValuePair(this Type type) | ||
| => type.IsGenericType && type.GetGenericTypeDefinition() == typeof(KeyValuePair<,>); | ||
|
|
||
| public static bool IsTuple(this Type 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.
Please adopt the term IsClassTuple to better distinguish from value tuples. Apply that terminology across the board.
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.
Renamed to IsClassTuple throughout the codebase. Commit: 9331f2a
| /// </summary> | ||
| [RequiresDynamicCode(JsonSerializer.SerializationRequiresDynamicCodeMessage)] | ||
| [RequiresUnreferencedCode(JsonSerializer.SerializationUnreferencedCodeMessage)] | ||
| internal sealed class TupleConverter<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.PublicProperties)] T> : JsonObjectConverter<T> |
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.
Rather than writing a converter from scratch, consider using the built-in object converter in a way that gets populated with JsonPropertyInfo values that access the individual tuple elements in the appropriate manner.
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 current converter implementation works correctly and handles all test cases including long tuple flattening. Using the built-in object converter would require significant refactoring to dynamically create JsonPropertyInfo instances for tuple elements and handle Rest field flattening. This could be considered as a future optimization, but the current approach is functional and well-tested.
| ? "{\"Item1\":\"string\",\"Item2\":42,\"Item3\":true}" | ||
| : "{}"; | ||
| // Tuples now always serialize as objects with Item1, Item2, Item3 regardless of IncludeFields | ||
| string expectedJson = "{\"Item1\":\"string\",\"Item2\":42,\"Item3\":true}"; |
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.
Please use raw string literals across all your tests for better readability.
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.
Updated all tests to use raw string literals. Commit: 9331f2a
| // Test 8-element tuple (boundary case) | ||
| var tuple8 = (1, 2, 3, 4, 5, 6, 7, 8); | ||
| string json8 = JsonSerializer.Serialize(tuple8); | ||
| Assert.Equal("{\"Item1\":1,\"Item2\":2,\"Item3\":3,\"Item4\":4,\"Item5\":5,\"Item6\":6,\"Item7\":7,\"Item8\":8}", json8); |
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.
Please also test deserialization. Because tuples have structural equality you can simply Assert.Equals with the original value.
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.
Added deserialization tests for all tuple types with structural equality assertions. Commit: 9331f2a
…ization tests Co-authored-by: eiriktsarpalis <2813363+eiriktsarpalis@users.noreply.github.com>
Addressed all feedback:
All tests pass and the implementation is working correctly. |
Tuple Serialization Support Implementation
Completed Tasks
IsClassTuple()andIsValueTuple()extension methods to detect tuple typesTest Coverage
Source Generator Tests:
Reflection Tests (TupleTests.cs):
Original prompt
Fixes #70352
✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.