I have a small problem with passing some parent instance to a constructor when deserializing an object with Newtonsoft.Json
.
Let's assume i have the following classes
public class A
{
public string Str1 { get; set; }
public IList<B> Bs { get; set; }
}
public class B
{
public B(A a)
{
// a should not be null!
Console.WriteLine(a.Str)
}
}
And now i serailze and than deserialize the object a
like this:
A a = new A()
a.Bs = new List<B>()
a.Bs.Add(new B(a));
a.Bs.Add(new B(a));
a.Bs.Add(new B(a));
var json = JsonConvert.SerializeObject(a);
// Here i need to call the constructor of B when creating new instances
var newA = JsonConvert.DeserializeObject<A>(json);
The problem is, that when deserializing the object, null
will be passed to the constructor of B
. Does any one has solved this issue/problem before?
Thank you very much!
In your question and comments you've said that the class B
does not have any public property for A
. So, when you serialize B
, then no A
will be written to the JSON, because Json.Net only serializes the public information by default. Therefore, when deserializing, there will not be enough information to recreate B
, because there is no A
in the JSON. So, step one is making B
's reference to A
visible to Json.Net. If you don't want to make it public, that is fine, but you will at least need to mark the member with a [JsonProperty]
attribute to allow Json.Net to "see" it.
public class B
{
[JsonProperty]
private A a;
public B(A a)
{
this.a = a; // be sure to set the A member in your constructor
}
}
Now if you do the above you will run into a second problem: your class structure has a reference loop (A
has a list of B
s which each refer back to A
), and the serializer will throw an exception by default in this case. The solution is to set the serializer's PreserveReferencesHandling
setting to Objects
(the default is None
). This will not only allow the serializer to handle the reference loops during serialization, but will also preserve the original references during deserialization, so that all the B
s will refer to the same A
instance. (This is accomplished via special $id
and $ref
properties that are written into the JSON.)
JsonSerializerSettings settings = new JsonSerializerSettings
{
PreserveReferencesHandling = PreserveReferencesHandling.Objects,
};
var json = JsonConvert.SerializeObject(a, settings);
var newA = JsonConvert.DeserializeObject<A>(json, settings);
Working example: https://dotnetfiddle.net/N0FUID
What I like to do it I have to pass objects in a constructor is to create the object first using my default constructor and then call populate object to set all properties that are not skipped as I decorated the properties with [JsonIgore]
var settings = new JsonSerializerSettings()
{
Error = HandleJsonDeserializationError,
PreserveReferencesHandling = PreserveReferencesHandling.Objects
}
var myObject = new ComplexObject(param1,param2);
JsonConvert.PopulateObject(json, myObject, settings);
You can continue populating objects and deal with any issues if you handle serialisation errors in the JsonSettings property. The signature is as follows:
static void HandleJsonDeserializationError(object sender, Newtonsoft.Json.Serialization.ErrorEventArgs errorArgs)
{
var currentError = errorArgs.ErrorContext.Error.Message;
errorArgs.ErrorContext.Handled = true;
//loging framework logs the error, set brake point etc when debug.
Logger.Log(currentError, LogLevel.Exceptions);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With