Some time back i have reported an issue for which i have got a fix in Json.net 4.5 R11.
If my circular referenced property Manager
is NULL then serialisation and deserialisation works fine.
But when circular reference property Manager
is set to NON NULL value then it gets ignored in the serialised string and hence it throws an exception in deserialisation.
Json.net issue base says the problem is in your code but i could not figure out. Can somebody help me here?
Questions:
Some more updates : This is needed in the legacy application which is currently using Binary serialization. Since changes are huge, marking all the private fields that are involved in serialization with the Json serialization tag is too much work. Since Json.net can do serialization of ISerializable object we want to do this. This works if there are no circular reference objects.
My classes
[Serializable]
class Department : ISerializable
{
public Employee Manager { get; set; }
public string Name { get; set; }
public Department() { }
public Department( SerializationInfo info, StreamingContext context )
{
Manager = ( Employee )info.GetValue( "Manager", typeof( Employee ) ); //Manager's data not found since json string itself does not have Employee property
Name = ( string )info.GetValue( "Name", typeof( string ) );
}
public void GetObjectData( SerializationInfo info, StreamingContext context )
{
info.AddValue( "Manager", Manager );
info.AddValue( "Name", Name );
}
}
[Serializable]
class Employee : ISerializable
{
public Department Department { get; set; }
public string Name { get; set; }
public Employee() { }
public Employee( SerializationInfo info, StreamingContext context )
{
Department = ( Department )info.GetValue( "Department", typeof( Department ) );
Name = ( string )info.GetValue( "Name", typeof( string ) );
}
public void GetObjectData( SerializationInfo info, StreamingContext context )
{
info.AddValue( "Department", Department );
info.AddValue( "Name", Name );
}
}
My Test code:
JsonSerializerSettings jsonSS= new JsonSerializerSettings();
jsonSS.Formatting = Formatting.Indented;
jsonSS.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; //If there is referenced object then it is not shown in the json serialisation
//jsonSS.ReferenceLoopHandling = ReferenceLoopHandling.Serialize; //Throws stackoverflow error
jsonSS.PreserveReferencesHandling = PreserveReferencesHandling.All;
Department department = new Department();
department.Name = "Dept1";
Employee emp1 = new Employee { Name = "Emp1", Department = department };
department.Manager = null;
string json1 = JsonConvert.SerializeObject( emp1, jsonSS );
//json1 =
// {
// "$id": "1",
// "Department": {
// "$id": "2",
// "Manager": null,
// "Name": "Dept1"
// },
// "Name": "Emp1"
//}
Employee empD1 = JsonConvert.DeserializeObject<Employee>( json1, jsonSS ); //Manager is set as null
department.Manager = emp1; //Non null manager is set
string json2 = JsonConvert.SerializeObject( emp1, jsonSS ); //SEE Manager property is missing
// json2 = {
// "$id": "1",
// "Department": {
// "$id": "2",
// "Name": "Dept1"
// },
// "Name": "Emp1"
//}
Employee empD2 = JsonConvert.DeserializeObject<Employee>( json2, jsonSS ); //Throws exception
Based on khellang 's answer, which locates the problem in JSon.Net not being able to handle circular references when using ISerializable
interface implementation, you could try forcing the JSon.Net serializer to ignore the ISerializable
implementation, without actually removing this implementation.
You should be able to achieve this by decorating your classes ( Department
and Employee
) with JsonObject
attribute.
I have not tested if this actually solves your issue.
Quoting the Serialization Guide (emphasis mine):
Types that implement ISerializable are serialized as JSON objects. When serializing, only the values returned from ISerializable.GetObjectData are used; members on the type are ignored. When deserializing, the constructor with a SerializationInfo and StreamingContext is called, passing the JSON object's values.
In situations where this behavior is not wanted, the JsonObjectAttribute can be placed on a .NET type that implements ISerializable to force it to be serialized as a normal JSON object.
As your question and comments date back to 2012, this solution might not have been available then. It's also possible, that current JSon.Net implementation is able to deal with circular references even when ISerializable
is used.
I'll keep it short :)
It seems that it's the ISerializable
interface that screws it up. If you remove it, everything works perfectly.
Do you have a REALLY good reason for implementing the ISerializable
interface in Employee
and Department
? If not, just remove it. If you do, GOTO 3 ;)
I don't know Newtonsoft.Json internals, but somehow it's not treating circular references properly when implementing ISerializable
. You should probably file an issue at GitHub .
But when circular reference property Manager is set to NON NULL value then it gets ignored in the serialised string and hence it throws an exception in deserialisation.
Because the circular reference is being ignored. That is the point of ReferenceLoopHandling.Ignore.
PreserveReferencesHandling does not work with ISerializable because child values have to be created before the parent value.
Does not look like you really need to implement the Iserializable interface. This class structure is way to simple. I have serialized very large classes without having to go that far.
If you are still having circular reference issue, try the following:
I believe JSON.net has a formatting option to ignore circular references
JsonSerializerSettings jsSettings = new JsonSerializerSettings(); jsSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
Ignore the offending property
restructure your class
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.