Skip to content

Commit ba3ada4

Browse files
Copilotgewarren
andcommitted
Add documentation for ReferenceResolver limitations with custom converters
Co-authored-by: gewarren <[email protected]>
1 parent 20e6fbe commit ba3ada4

File tree

1 file changed

+36
-0
lines changed

1 file changed

+36
-0
lines changed

docs/standard/serialization/system-text-json/converters-how-to.md

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -355,6 +355,42 @@ When the sample code calls the serializer, it uses a <xref:System.Text.Json.Json
355355

356356
The preceding example only does serialization, but a similar approach can be adopted for deserialization.
357357

358+
### ReferenceResolver limitations with custom converters
359+
360+
When using <xref:System.Text.Json.Serialization.ReferenceHandler.Preserve%2A>, 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 <xref:System.Text.Json.Serialization.ReferenceResolver> instance.
361+
362+
Consider this scenario:
363+
364+
```csharp
365+
public class Parent
366+
{
367+
public Child Child1 { get; set; }
368+
public Child Child2 { get; set; }
369+
}
370+
371+
[JsonConverter(typeof(ChildConverter))]
372+
public class Child
373+
{
374+
public string Name { get; set; }
375+
}
376+
```
377+
378+
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.
379+
380+
#### Current workarounds and limitations
381+
382+
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.
383+
384+
If you need both custom conversion logic and reference preservation, consider these approaches:
385+
386+
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.
387+
388+
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.
389+
390+
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.
391+
392+
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).
393+
358394
## Other custom converter samples
359395

360396
The [Migrate from Newtonsoft.Json to System.Text.Json](migrate-from-newtonsoft.md) article contains additional samples of custom converters.

0 commit comments

Comments
 (0)