From 20e6fbe83fe67ea0763a9288b2bf679339993bd7 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 29 Jul 2025 00:17:41 +0000 Subject: [PATCH 1/4] Initial plan From ba3ada4faba39dcdb880773176d574a770e20e90 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 29 Jul 2025 00:23:18 +0000 Subject: [PATCH 2/4] Add documentation for ReferenceResolver limitations with custom converters Co-authored-by: gewarren <24882762+gewarren@users.noreply.github.com> --- .../system-text-json/converters-how-to.md | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/docs/standard/serialization/system-text-json/converters-how-to.md b/docs/standard/serialization/system-text-json/converters-how-to.md index 693788f5e79c4..280e336c92e23 100644 --- a/docs/standard/serialization/system-text-json/converters-how-to.md +++ b/docs/standard/serialization/system-text-json/converters-how-to.md @@ -355,6 +355,42 @@ When the sample code calls the serializer, it uses a , there's an important limitation to be aware of when working with custom converters: **reference handling state is not preserved when the serializer calls into custom converters**. This means that if you have a custom converter for a type that's part of an object graph being serialized or deserialized with reference preservation enabled, the converter won't have access to the current instance. + +Consider this scenario: + +```csharp +public class Parent +{ + public Child Child1 { get; set; } + public Child Child2 { get; set; } +} + +[JsonConverter(typeof(ChildConverter))] +public class Child +{ + public string Name { get; set; } +} +``` + +If `Child1` and `Child2` reference the same object instance, and you serialize `Parent` with `ReferenceHandler.Preserve`, the custom `ChildConverter` won't be able to participate in the reference handling. The converter will be called for each `Child` instance separately, without knowledge of whether the object has already been serialized elsewhere in the graph. + +#### Current workarounds and limitations + +Currently, there's no direct way to access the active `ReferenceResolver` from within a custom converter. Calling `ReferenceHandler.CreateResolver()` throws an exception because the resolver has already been created earlier in the serialization process. + +If you need both custom conversion logic and reference preservation, consider these approaches: + +1. **Implement reference handling in the converter itself**: Manually track object references within your converter using a static or thread-local dictionary, but be aware this has limitations and complexity. + +2. **Use a different serialization approach**: Consider whether you can restructure your data or use alternative serialization strategies that don't require both custom converters and reference preservation simultaneously. + +3. **Avoid custom converters for reference types in the object graph**: If possible, use custom converters only for leaf nodes or value types that don't participate in reference cycles. + +This limitation is tracked in the .NET runtime repository as [issue #51715](https://github.com/dotnet/runtime/issues/51715), with plans for improvement tracked in [issue #42163](https://github.com/dotnet/runtime/issues/42163). + ## Other custom converter samples The [Migrate from Newtonsoft.Json to System.Text.Json](migrate-from-newtonsoft.md) article contains additional samples of custom converters. From 0bcd08c633128141c13b89ab0696533c74e72a47 Mon Sep 17 00:00:00 2001 From: Genevieve Warren <24882762+gewarren@users.noreply.github.com> Date: Mon, 28 Jul 2025 17:31:28 -0700 Subject: [PATCH 3/4] Shorten text --- .../system-text-json/converters-how-to.md | 34 +------------------ 1 file changed, 1 insertion(+), 33 deletions(-) diff --git a/docs/standard/serialization/system-text-json/converters-how-to.md b/docs/standard/serialization/system-text-json/converters-how-to.md index 280e336c92e23..e592dba85233a 100644 --- a/docs/standard/serialization/system-text-json/converters-how-to.md +++ b/docs/standard/serialization/system-text-json/converters-how-to.md @@ -357,39 +357,7 @@ The preceding example only does serialization, but a similar approach can be ado ### ReferenceResolver limitations with custom converters -When using , there's an important limitation to be aware of when working with custom converters: **reference handling state is not preserved when the serializer calls into custom converters**. This means that if you have a custom converter for a type that's part of an object graph being serialized or deserialized with reference preservation enabled, the converter won't have access to the current instance. - -Consider this scenario: - -```csharp -public class Parent -{ - public Child Child1 { get; set; } - public Child Child2 { get; set; } -} - -[JsonConverter(typeof(ChildConverter))] -public class Child -{ - public string Name { get; set; } -} -``` - -If `Child1` and `Child2` reference the same object instance, and you serialize `Parent` with `ReferenceHandler.Preserve`, the custom `ChildConverter` won't be able to participate in the reference handling. The converter will be called for each `Child` instance separately, without knowledge of whether the object has already been serialized elsewhere in the graph. - -#### Current workarounds and limitations - -Currently, there's no direct way to access the active `ReferenceResolver` from within a custom converter. Calling `ReferenceHandler.CreateResolver()` throws an exception because the resolver has already been created earlier in the serialization process. - -If you need both custom conversion logic and reference preservation, consider these approaches: - -1. **Implement reference handling in the converter itself**: Manually track object references within your converter using a static or thread-local dictionary, but be aware this has limitations and complexity. - -2. **Use a different serialization approach**: Consider whether you can restructure your data or use alternative serialization strategies that don't require both custom converters and reference preservation simultaneously. - -3. **Avoid custom converters for reference types in the object graph**: If possible, use custom converters only for leaf nodes or value types that don't participate in reference cycles. - -This limitation is tracked in the .NET runtime repository as [issue #51715](https://github.com/dotnet/runtime/issues/51715), with plans for improvement tracked in [issue #42163](https://github.com/dotnet/runtime/issues/42163). +When you use , be aware that reference handling state isn't preserved when the serializer calls into a custom converter. This means that if you have a custom converter for a type that's part of an object graph being serialized or deserialized with reference preservation enabled, the converter won't have access to the current instance. ## Other custom converter samples From b870951badc64c833a9edca05e540c696e31e0e1 Mon Sep 17 00:00:00 2001 From: Genevieve Warren <24882762+gewarren@users.noreply.github.com> Date: Tue, 29 Jul 2025 09:59:11 -0700 Subject: [PATCH 4/4] Update docs/standard/serialization/system-text-json/converters-how-to.md Co-authored-by: Eirik Tsarpalis --- .../serialization/system-text-json/converters-how-to.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/standard/serialization/system-text-json/converters-how-to.md b/docs/standard/serialization/system-text-json/converters-how-to.md index e592dba85233a..59bf9b818a5d3 100644 --- a/docs/standard/serialization/system-text-json/converters-how-to.md +++ b/docs/standard/serialization/system-text-json/converters-how-to.md @@ -357,7 +357,7 @@ The preceding example only does serialization, but a similar approach can be ado ### ReferenceResolver limitations with custom converters -When you use , be aware that reference handling state isn't preserved when the serializer calls into a custom converter. This means that if you have a custom converter for a type that's part of an object graph being serialized or deserialized with reference preservation enabled, the converter won't have access to the current instance. +When you use , be aware that reference handling state isn't preserved when the serializer calls into a custom converter. This means that if you have a custom converter for a type that's part of an object graph being serialized or deserialized with reference preservation enabled, the converter and any nested serialization calls won't have access to the current instance. ## Other custom converter samples