Open Alois-xx opened 7 years ago
I've come across the same problem. Is this a known issue?
It seems to only happen at the top level, since this dotnetfiddle seems to have the cyclical references working fine after the first level. By that I mean the root object's parent field is null, but the childrens' parents fields have proper references.
P.S. I've simplified the demo code a bit.
void Main()
{
var ser = new JsonSerializerSettings()
{
PreserveReferencesHandling = PreserveReferencesHandling.Objects,
ReferenceLoopHandling = ReferenceLoopHandling.Serialize
};
var parent = new DataForDataContract(1, "Hello");
parent.Parent = parent;
string json = JsonConvert.SerializeObject(parent, ser); // works great!
var deserializedObject = JsonConvert.DeserializeObject<DataForDataContract>(json);
if (deserializedObject.Parent == null)
throw new NullReferenceException("copy.Parent is null"); // execution reaches here
}
public class DataForDataContract
{
public int Id { get; set; }
public DataForDataContract Parent;
private string Message;
public DataForDataContract(int id, string message)
{
Id = id;
Message = message;
}
}
I habe managed to isolate the problem somehow. The problem only occurs if the the type with the cyclic reference (DataForDataContract in the example) has no default constructor. In my research, it did not matter whether the non-parameterless constructor contains a parameter for the cyclic reference itself. For example, the above example just works fine, if a default constructor is added to the DataForDataContract type.
static void Main()
{
var ser = new JsonSerializerSettings()
{
PreserveReferencesHandling = PreserveReferencesHandling.Objects,
ReferenceLoopHandling = ReferenceLoopHandling.Serialize
};
var parent = new DataForDataContract(1, "Hello");
parent.Parent = parent;
string json = JsonConvert.SerializeObject(parent, ser); // works great!
var deserializedObject = JsonConvert.DeserializeObject<DataForDataContract>(json, ser);
if (deserializedObject.Parent == null)
throw new NullReferenceException("copy.Parent is null"); // Is not triggered now
}
public class DataForDataContract
{
public int Id { get; set; }
public DataForDataContract Parent;
private string Message;
public DataForDataContract(int id, string message)
{
Id = id;
Message = message;
}
public DataForDataContract() { } // Default constructor available
}
The example at dotnetfiddle does not work either, if the default constructor is removed. I created a fork here.
EDIT: Forgot to pass serializer settings to JsonConvert.DeserializeObject. Problem is not gone though.
Actually it is the object more closer to the root that has to be created with a default constructor in order that all references are set correctly. See this exemple.
static void Main()
{
var ser = new JsonSerializerSettings()
{
PreserveReferencesHandling = PreserveReferencesHandling.Objects,
ReferenceLoopHandling = ReferenceLoopHandling.Serialize
};
var b = new B(1, "Hello");
var a = new A(1.234) { B = b };
b.As = new List<A> { a };
var aChild = new AChild(1.45f) { A = a };
a.Child = aChild;
string json = JsonConvert.SerializeObject(b, ser); // works great!
var deserializedObject = JsonConvert.DeserializeObject<B>(json, ser);
if (deserializedObject.As[0] == null)
throw new NullReferenceException();
if (deserializedObject.As[0].B == null)
throw new NullReferenceException();
if (deserializedObject.As[0].Child == null)
throw new NullReferenceException();
if (deserializedObject.As[0].Child.A == null)
throw new NullReferenceException();
}
public class B
{
public int Id { get; set; }
private string Message;
public List<A> As;
public B(int id, string message)
{
Id = id;
Message = message;
}
public B() // Must be present to deserialize A -> B
{
}
}
public class A
{
public double D { get; set; }
public B B { get; set; }
public AChild Child { get; set; }
public A(double d)
{
D = d;
}
public A() // Must be present to deserialize AChild -> A
{
}
}
public class AChild
{
public A A { get; set; }
public float F { get; set; }
//public AChild() // Can be removed
//{
//}
public AChild(float f)
{
F = f;
}
}
EDIT: Forgot to pass serializer settings to JsonConvert.DeserializeObject. Problem is not gone though.
Are there any workarounds for this issue aside from adding parameterless constructors?
For any future readers:
I was trying to test the reference handling of JSON.NET and either it is a feature or I have not yet found the right settings for it. I would expect that when I set PreserveReferencesHandling = PreserveReferencesHandling.Objects, ReferenceLoopHandling = ReferenceLoopHandling.Serialize,
that cyclic references are serialized and deserialized. Serialization works without failure but the the deserialized cyclic field is nulled out. Am I missing something? I am using Nuget package 10.0.2. `