Repository navigation
Should we add support to ignore cycles on serialization? #40099
Description
Background and Motivation
Even though we have covered the blocking issue of not being able to (de)serialize reference loops (#30820, #29900) by adding ReferenceHandler.Preserve to S.T.Json, there is many asks for adding an option equivalent to Json.NET's ReferenceLoopHandling.Ignore.
Motivations for doing this are:
ReferenceHandler.Preservemay be too cumbersome and increases payload size.ReferenceHandler.Preservecreates JSON incomprehensible by serializers other than S.T.Json and Json.NET.
Proposed API
namespace System.Text.Json.Serialization
{
public abstract partial class ReferenceHandler
{
public static ReferenceHandler Preserve { get; }
+ public static ReferenceHandler IgnoreCycle { get; }
}
}Usage Examples
class Node
{
public string Description { get; set; }
public Node Next { get; set; }
}
void Test()
{
var node = new Node { Description = "Node 1" };
node.Next = node;
var opts = new JsonSerializerOptions { ReferenceHandler = ReferenceHandler.IgnoreCycle };
string json = JsonSerializer.Serialize(node, opts);
Console.WriteLine(json); // Prints: {"Description":"Node 1"}.
// Note property "Next" being ignored due to cycle detection.
}Alternative Designs
This new API is being added to ReferenceHandler class since this can be considered as an alternative to deal with references that is more isolated to the circularity problem during serialization.
Comparison with Newtonsoft.Json
void Test()
{
var node = new Node { Description = "Node 1" };
node.Next = node;
var settings = new JsonSerializerSettings { ReferenceLoopHandling = ReferenceLoopHandling.Ignore };
string json = JsonConvert.SerializeObject(node, settings);
Console.WriteLine(json); // Prints: {"Description":"Node 1"}.
}Comparison with existing ReferenceHandler.Preserve setting in S.T.Json
void Test()
{
var node = new Node { Description = "Node 1" };
node.Next = node;
var opts = new JsonSerializerOptions { ReferenceHandler = ReferenceHandler.Preserve };
string json = JsonSerializer.Serialize(node, opts);
Console.WriteLine(json); // Prints: {"$id":"1","Description":"Node 1","Next":{"$ref":"1"}}.
}Risks
One downside is that users are uanble to implement their own ReferenceHandler and cannot make their own "Ignore Cycle" handler. The discrimination between preserve and ignore would occur with an internal flag.
Concerns of adding this feature (users must be aware of these problems when opting-in for it):
- Silent loss of data given that the object where the loop is detected would be ignored from the JSON (see example: Should we add support to ignore cycles on serialization? #40099 (comment)).
- Unable to round-trip data. the JSON may differ depending on the order of serialization (e.g: object properties and dictionary elements enumeration is non-deterministic).
Activity
jozkee commentedon Jul 29, 2020
From #30820 (comment):
jozkee commentedon Jul 29, 2020
From #30820 (comment)
jozkee commentedon Jul 29, 2020
From #30820 (comment)
danmoseley commentedon Jul 30, 2020
@jozkee is this required to ship 5.0 (per milestone set here)?
jozkee commentedon Jul 30, 2020
@danmosemsft I think it could disappoint many users if we don't ship this on 5.0.
danmoseley commentedon Jul 30, 2020
@jozkee maybe, but master is a 6.0 branch in less than 3 weeks: if we add features, we're implicitly deciding to leave bugs un-fixed.
That's a general statement - I don't have context on this issue or JSON in general so it would be a good discussion to have with @ericstj .
ericsampson commentedon Aug 9, 2020
@jozkee @ericstj FWIW when I heard that STJ would have loop handling in 5.0, I just assumed that would include something along the lines of Ignore. It would be disappointing to not have something along those lines in 5.0
Also FWIW I don't love the Newtonsoft options. Like another commenter mention above, it would be great to have an in-between option that didn't suffer the data loss of Ignore; one option that seems great space-wise would be to only emit reference tags where they are needed, although that might require backtracking/rewriting?
snow-jallen commentedon Sep 30, 2020
What's the verdict? Is this going to ship in .net 5?
48 remaining items
jozkee commentedon Feb 1, 2021
Sending back to
api-ready-for-reviewin order to discuss a more suitable name for the API:1. IgnoreCycle: Original proposal.
2. IgnoreCycles: From #46101 (comment) one could read
PreserveasPreserveDuplicates, which would be plural too.3. BreakCycles: Given that we are breaking cycles by emitting the JSON
nulltoken and to be clear about the behavior diverging from Newtonsoft.4. EmitNullForCycles: From #40099 (comment).
ericsampson commentedon Feb 1, 2021
What about something like
EmitNullForCyclesjozkee commentedon Feb 1, 2021
@ericsampson sounds like a reasonable name that exactly describes what the API does.
bartonjs commentedon Feb 4, 2021
Changed to plural via email: