Differencebetween Preservereferenceshandling and Referenceloophandling in JSON.Net

What is the difference between PreserveReferencesHandling and ReferenceLoopHandling in Json.Net?

These settings can best be explained by example. Let's say that we want to represent a hierarchy of employees in a company. So we make a simple class like this:

class Employee
{
public string Name { get; set; }
public List<Employee> Subordinates { get; set; }
}

This is a small company with only three employees so far: Angela, Bob and Charles. Angela is the boss, while Bob and Charles are her subordinates. Let's set up the data to describe this relationship:

Employee angela = new Employee { Name = "Angela Anderson" };
Employee bob = new Employee { Name = "Bob Brown" };
Employee charles = new Employee { Name = "Charles Cooper" };

angela.Subordinates = new List<Employee> { bob, charles };

List<Employee> employees = new List<Employee> { angela, bob, charles };

If we serialize the list of employees to JSON...

string json = JsonConvert.SerializeObject(employees, Formatting.Indented);
Console.WriteLine(json);

...we get this output:

[
{
"Name": "Angela Anderson",
"Subordinates": [
{
"Name": "Bob Brown",
"Subordinates": null
},
{
"Name": "Charles Cooper",
"Subordinates": null
}
]
},
{
"Name": "Bob Brown",
"Subordinates": null
},
{
"Name": "Charles Cooper",
"Subordinates": null
}
]

So far so good. You'll notice, however, that the information for Bob and Charles is repeated in the JSON because the objects representing them are referenced both by the main list of employees and Angela's list of subordinates. Maybe that's OK for now.

Now suppose we'd also like to have a way to keep track of each Employee's supervisor in addition to his or her subordinates. So we change our Employee model to add a Supervisor property...

class Employee
{
public string Name { get; set; }
public Employee Supervisor { get; set; }
public List<Employee> Subordinates { get; set; }
}

...and add a couple more lines to our setup code to indicate that Charles and Bob report to Angela:

Employee angela = new Employee { Name = "Angela Anderson" };
Employee bob = new Employee { Name = "Bob Brown" };
Employee charles = new Employee { Name = "Charles Cooper" };

angela.Subordinates = new List<Employee> { bob, charles };
bob.Supervisor = angela; // added this line
charles.Supervisor = angela; // added this line

List<Employee> employees = new List<Employee> { angela, bob, charles };

But now we have a bit of a problem. Because the object graph has reference loops in it (e.g. angela references bob while bob references angela), we will get a JsonSerializationException when we try to serialize the employees list. One way we can get around this issue is by setting ReferenceLoopHandling to Ignore like this:

JsonSerializerSettings settings = new JsonSerializerSettings
{
ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
Formatting = Formatting.Indented
};

string json = JsonConvert.SerializeObject(employees, settings);

With this setting in place, we get the following JSON:

[
{
"Name": "Angela Anderson",
"Supervisor": null,
"Subordinates": [
{
"Name": "Bob Brown",
"Subordinates": null
},
{
"Name": "Charles Cooper",
"Subordinates": null
}
]
},
{
"Name": "Bob Brown",
"Supervisor": {
"Name": "Angela Anderson",
"Supervisor": null,
"Subordinates": [
{
"Name": "Charles Cooper",
"Subordinates": null
}
]
},
"Subordinates": null
},
{
"Name": "Charles Cooper",
"Supervisor": {
"Name": "Angela Anderson",
"Supervisor": null,
"Subordinates": [
{
"Name": "Bob Brown",
"Subordinates": null
}
]
},
"Subordinates": null
}
]

If you examine the JSON, it should be clear what this setting does: any time the serializer encounters a reference back to an object it is already in the process of serializing, it simply skips that member. (This prevents the serializer from getting into an infinite loop.) You can see that in Angela's list of subordinates in the top part of the JSON, neither Bob nor Charles show a supervisor. In the bottom part of the JSON, Bob and Charles both show Angela as their supervisor, but notice her subordinates list at that point does not include both Bob and Charles.

While it is possible to work with this JSON and maybe even reconstruct the original object hierarchy from it with some work, it is clearly not optimal. We can eliminate the repeated information in the JSON while still preserving the object references by using the PreserveReferencesHandling setting instead:

JsonSerializerSettings settings = new JsonSerializerSettings
{
PreserveReferencesHandling = PreserveReferencesHandling.Objects,
Formatting = Formatting.Indented
};

string json = JsonConvert.SerializeObject(employees, settings);

Now we get the following JSON:

[
{
"$id": "1",
"Name": "Angela Anderson",
"Supervisor": null,
"Subordinates": [
{
"$id": "2",
"Name": "Bob Brown",
"Supervisor": {
"$ref": "1"
},
"Subordinates": null
},
{
"$id": "3",
"Name": "Charles Cooper",
"Supervisor": {
"$ref": "1"
},
"Subordinates": null
}
]
},
{
"$ref": "2"
},
{
"$ref": "3"
}
]

Notice that now each object has been assigned a sequential $id value in the JSON. The first time that an object appears, it is serialized in full, while subsequent references are replaced with a special $ref property that refers back to the original object with the corresponding $id. With this setting in place, the JSON is much more concise and can be deserialized back into the original object hierarchy with no additional work required, assuming you are using a library that understands the $id and $ref notation produced by Json.Net / Web API.

So why would you choose one setting or the other? It depends on your needs of course. If the JSON will be consumed by a client that does not understand the $id/$ref format, and it can tolerate having incomplete data in places, you would choose to use ReferenceLoopHandling.Ignore. If you're looking for more compact JSON and you will be using Json.Net or Web API (or another compatible library) to deserialize the data, then you would choose to use PreserveReferencesHandling.Objects. If your data is a directed acyclic graph with no duplicate references then you don't need either setting.

Json.net serializer is handling `PreserveReferencesHandling` and `ReferenceLoopHandling` wrong

so it appears i was doing things the wrong way not according to Json.net design (thanks to @dbc's comment)

i changed the model slightly and everything worked

in the root class, i changed the definition of property SpecificBranchA to the following

[JsonProperty]
public A SpecificBranchA { get; private set; }

and the serialization works as expected.
so i think any property that contains an object with reference loop, must have a setter (even if private) and be marked with [JsonProperty]. if either is missing, the serialization gets the wrong behavior.


related issues posted by @dbc

  • Issue with serializing/deserializing object graph with self-referencing objects in combination with JSON constructor. #715
  • References are not preserved during serialization of collection if the property is read-only #1329
  • PreserveReferencesHandling.Objects deserialize does not work with non-default constructor #678

Newtonsoft JSON PreserveReferencesHandling with custom Equals usage

As stated in this answer to JSON.NET Serialization - How does DefaultReferenceResolver compare equality? by Andrew Whitaker, Json.NET exclusively uses reference equality when preserving references via PreserveReferencesHandling. The setting JsonSerializerSettings.EqualityComparer is intended for reference loop detection rather than reference preservation and resolution, as explain in this answer to Why doesn't reference loop detection use reference equality?.

Andrew's answer gives an example of a custom IReferenceResolver that resolves references using object equality for objects of a certain type and assumes all serialized objects are of that type. What you would like to do is to use object equality only for certain types (EntityA and EntityB) and fall back on Json.NET's default reference resolver for all other types.

You can accomplish this via the decorator pattern, in which you wrap an instance of Json.NET's reference resolver in your own IReferenceResolver. Then implement whatever logic is necessary for the types that need their own custom equality comparison, and pass everything else on to the underlying default resolver.

Here is one that meets your requirements:

public class SelectiveValueEqualityReferenceResolver : EquivalencingReferenceResolver
{
readonly Dictionary<Type, Dictionary<object, object>> representatives;

public SelectiveValueEqualityReferenceResolver(IReferenceResolver defaultResolver, IEnumerable<Type> valueTypes)
: base(defaultResolver)
{
if (valueTypes == null)
throw new ArgumentNullException();
representatives = valueTypes.ToDictionary(t => t, t => new Dictionary<object, object>());
}

protected override bool TryGetRepresentativeObject(object obj, out object representative)
{
var type = obj.GetType();
Dictionary<object, object> typedItems;

if (representatives.TryGetValue(type, out typedItems))
{
return typedItems.TryGetValue(obj, out representative);
}
return base.TryGetRepresentativeObject(obj, out representative);
}

protected override object GetOrAddRepresentativeObject(object obj)
{
var type = obj.GetType();
Dictionary<object, object> typedItems;

if (representatives.TryGetValue(type, out typedItems))
{
object representative;
if (!typedItems.TryGetValue(obj, out representative))
representative = (typedItems[obj] = obj);
return representative;

}
return base.GetOrAddRepresentativeObject(obj);
}
}

public abstract class EquivalencingReferenceResolver : IReferenceResolver
{
readonly IReferenceResolver defaultResolver;

public EquivalencingReferenceResolver(IReferenceResolver defaultResolver)
{
if (defaultResolver == null)
throw new ArgumentNullException();
this.defaultResolver = defaultResolver;
}

protected virtual bool TryGetRepresentativeObject(object obj, out object representative)
{
representative = obj;
return true;
}

protected virtual object GetOrAddRepresentativeObject(object obj)
{
return obj;
}

#region IReferenceResolver Members

public void AddReference(object context, string reference, object value)
{
var representative = GetOrAddRepresentativeObject(value);
defaultResolver.AddReference(context, reference, representative);
}

public string GetReference(object context, object value)
{
var representative = GetOrAddRepresentativeObject(value);
return defaultResolver.GetReference(context, representative);
}

public bool IsReferenced(object context, object value)
{
object representative;

if (!TryGetRepresentativeObject(value, out representative))
return false;
return defaultResolver.IsReferenced(context, representative);
}

public object ResolveReference(object context, string reference)
{
return defaultResolver.ResolveReference(context, reference);
}

#endregion
}

Which you would then use as follows:

var settings = new JsonSerializerSettings
{
//Commented out TypeNameHandling since the JSON in the question does not include type information
//TypeNameHandling = TypeNameHandling.All,
NullValueHandling = NullValueHandling.Ignore,
FloatParseHandling = FloatParseHandling.Decimal,
Formatting = Formatting.Indented,
PreserveReferencesHandling = PreserveReferencesHandling.Objects,
ReferenceLoopHandling = ReferenceLoopHandling.Serialize,
ReferenceResolverProvider = () => new SelectiveValueEqualityReferenceResolver(
new JsonSerializer().ReferenceResolver,
new [] { typeof(EntityA), typeof(EntityB) }),
Error = (sender, args) => Log.Error(args.ErrorContext.Error, $"Error while (de)serializing: {args.ErrorContext}; object: {args.CurrentObject}")
};

var outer = JsonConvert.DeserializeObject<OuterClass>(jsonString, settings);

var json2 = JsonConvert.SerializeObject(outer, settings);

Note that I had to make a variety of fixes to your types to make this work:

public static class EqualityHelper
{
public static bool? EqualsQuickReject<T1, T2>(T1 item1, T2 item2)
where T1 : class
where T2 : class
{
if ((object)item1 == (object)item2)
return true;
else if ((object)item1 == null || (object)item2 == null)
return false;

if (item1.GetType() != item2.GetType())
return false;

return null;
}
}

public class EntityA : IEquatable<EntityA> //Fixed added IEquatable<T>
{
public int Foo { get; set; } // FIXED made public

public override bool Equals(object obj)
{
return Equals(obj as EntityA);
}

// Fixed added required GetHashCode() that is compatible with Equals()
public override int GetHashCode()
{
return Foo.GetHashCode();
}

#region IEquatable<EntityA> Members

public bool Equals(EntityA other)
{
// FIXED - ensure Equals is reflexive, symmetric and transitive even when dealing with derived types
var initial = EqualityHelper.EqualsQuickReject(this, other);
if (initial != null)
return initial.Value;
return this.Foo == other.Foo;
}

#endregion
}

public class EntityB : IEquatable<EntityB> //Fixed added IEquatable<T>
{
public int Bar { get; set; } // FIXED made public

public EntityA Parent { get; set; } // FIXED made public

public override bool Equals(object obj)
{
return Equals(obj as EntityB);
}

// Fixed added required GetHashCode() that is compatible with Equals()
public override int GetHashCode()
{
return Bar.GetHashCode();
}

#region IEquatable<EntityB> Members

public bool Equals(EntityB other)
{
// FIXED - ensure Equals is reflexive, symmetric and transitive even when dealing with derived types
var initial = EqualityHelper.EqualsQuickReject(this, other);
if (initial != null)
return initial.Value;
return this.Bar == other.Bar;
}

#endregion
}

public class InnerWrapper
{
public string FooBar { get; set; }

public EntityB BEntity { get; set; }
}

public class OuterClass
{
public EntityA AEntity { get; set; }

public List<EntityB> InnerElements { get; set; }//FIXED -- made public and corrected type to be consistent with sample JSON
}

Notes:

  • SelectiveValueEqualityReferenceResolver works as follows. When constructed it is given a default reference resolver and a list of types for which to use object equality. Then, when one of the IReferenceResolver methods is called, it checks to see whether the incoming object is of one of the custom types. If so, it checks to see whether it has already encountered an equivalent object of the same type using object equality. If so, passes that initial object on to the default reference resolver. If not, it caches the incoming object as a defining instance of object-equivalent objects, then passes the incoming object on to the default reference resolver.

  • The above logic only works if the overridden object.Equals() is a proper equivalence relation -- i.e. reflexive, symmetric and transitive.

    In your code this was not guaranteed to be the case if EntityA or EntityB were ever subclassed. Thus I modified your Equals() methods to require that the incoming object be of the same type, rather than just a compatible type.

  • When Equals() is overriden, it is also necessary to override GetHashCode() in a compatible manner, such that equal objects have equal hash codes.

    This was not done in your code so I added the necessary logic to EntityA and EntityB.

  • Json.NET's DefaultReferenceResolver is internal so I had to use a slightly hacky method to create one, namely construct a temporary JsonSerializer and grab its ReferenceResolver.

  • SelectiveValueEqualityReferenceResolver is not thread safe so a fresh serializer instance should be used in each thread.

  • SelectiveValueEqualityReferenceResolver is designed to generate identical $id values for object-equal objects during serialization. It's not designed to merge equal objects with different $id values into reference-equal objects during deserialization. I think that could be added if required.

Prevent $id/$ref when serializing objects using Web API and JSON.NET

If using serialization attributes on your objects (such as DataContract), from the JSON.Net documentation on Serialization Attributes:

As well as using the built-in Json.NET attributes, Json.NET also looks for the [SerializableAttribute][2] (if IgnoreSerializableAttribute on DefaultContractResolver is set to false) [DataContractAttribute][3], [DataMemberAttribute][4] and [NonSerializedAttribute][5] ... when determining how JSON is to be serialized and deserialized.

It also says this:

Note

Json.NET attributes take presidence over standard .NET serialization attributes, e.g. if both JsonPropertyAttribute and DataMemberAttribute are present on a property and both customize the name, the name from JsonPropertyAttribute will be used.

It seems the solution to the problem is to add [JsonObject(IsReference = false)] to your object(s) like this:

[DataContract(IsReference = true)]
[JsonObject(IsReference = false)]
public class MyObject
{
[DataMember]
public int MyProperty { get; set; }
}

JSON nesting too deep when serializing in Web API

You have not provided the full model schema, but it seems that you don't need these settings in this case.

I suggest you to read accepted answer for this question which has a great explanation of how ReferenceLoopHandling and PreserveReferencesHandling settings are working. In general it's all about when you have the same object instance more than once in object tree that you want to serialize. Looking at your image it's not the case.

It seems that you are serializing your EF models and you are using lazy loading for connected objects. You might think of creating separate model for JSON serialization and populate only fields you need. Using this technique you will be sure what data is retrieved from DB.

Another option would be to use JsonIgnore attribute. You can mark needed properties in your EF models. For example:

public class A
{
public string NameOfA { get; set; }

[JsonIgnore]
public string IgnoredProperty { get; set; }

[JsonIgnore]
public B B { get; set; }
}

public class B
{
public string NameOfB { get; set; }
}

And if we execute this code:

var a = new A
{
NameOfA = "A",
IgnoredProperty = "Will be ignored",
B = new B
{
NameOfB = "B"
}
};
var json = JsonConvert.SerializeObject(a, Formatting.Indented);

The resulting JSON will be:

{
"NameOfA": "A"
}

JsonFormatter ReferenceLoopHandling

You should use DTO instead of Entity framework entity,
For example:

    class PersonDTO {
public Name {get; set;}
}

var person = context.People.First();
var personDTO = new PersonDTO{
Name = person.Name
}

var json = new JavaScriptSerializer().Serialize(personDTO);

How Do You Really Serialize Circular Referencing Objects With Newtonsoft.Json?

Yes, using PreserveReferencesHandling.Objects is really the best way to serialize an object graph with circular references, because it produces the most compact JSON and it actually preserves the reference structure of the object graph. That is, when you deserialize the JSON back to objects (using a library that understands the $id and $ref notation), each reference to a particular object will point to the same instance of that object, rather than having multiple instances with the same data.

In your case the problem is that your client side parser does not understand the $id and $ref notation produced by Json.Net, so the references are not being resolved. This can be fixed by using a javascript method to reconstruct the object references after deserializing the JSON. See here and here for examples.

Another possibility which might work, depending on your situation, is to set ReferenceLoopHandling to Ignore when serializing instead of setting PreserveReferencesHandling to Objects. This is not a perfect solution though. See this question for a detailed explanation of the differences between using ReferenceLoopHandling.Ignore and PreserveReferencesHandling.Objects.



Related Topics



Leave a reply



Submit