在使用Newtonsoft.Json
反序列化对象时,将一些父实例传递给构造函数时遇到了一个小问题。
我们假设我有以下课程
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)
}
}
现在我像这样对a
对象进行serailze而不是反序列化:
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);
问题是,在反序列化对象时,null
将传递给B
的构造函数。有没有人之前解决过这个问题?
非常感谢!
答案 0 :(得分:10)
在您的问题和评论中,您说过B
课程没有A
的任何公共财产。因此,当您序列化B
时,不会将A
写入JSON,因为Json.Net默认只序列化公共信息。因此,在反序列化时,没有足够的信息来重新创建B
,因为JSON中没有A
。因此,第一步是B
对Json.Net可见的A
的引用。如果您不想将其公开,那很好,但您至少需要使用[JsonProperty]
属性标记该成员,以允许Json.Net“看到”它。
public class B
{
[JsonProperty]
private A a;
public B(A a)
{
this.a = a; // be sure to set the A member in your constructor
}
}
现在,如果您执行上述操作,则会遇到第二个问题:您的类结构有一个引用循环(A
有一个B
列表,每个引用回A
),在这种情况下,序列化器默认会抛出异常。解决方案是将序列化程序的PreserveReferencesHandling
设置为Objects
(默认值为None
)。这不仅允许序列化程序在序列化期间处理引用循环,而且还将在反序列化期间保留原始引用,以便所有B
将引用相同的A
实例。 (这是通过写入JSON的特殊$id
和$ref
属性完成的。)
JsonSerializerSettings settings = new JsonSerializerSettings
{
PreserveReferencesHandling = PreserveReferencesHandling.Objects,
};
var json = JsonConvert.SerializeObject(a, settings);
var newA = JsonConvert.DeserializeObject<A>(json, settings);
答案 1 :(得分:0)
我想做的是,我必须在构造函数中传递对象是先使用默认构造函数创建对象,然后调用populate对象设置所有不跳过的属性,因为我用[JsonIgore]装饰了属性< / p>
var settings = new JsonSerializerSettings()
{
Error = HandleJsonDeserializationError,
PreserveReferencesHandling = PreserveReferencesHandling.Objects
}
var myObject = new ComplexObject(param1,param2);
JsonConvert.PopulateObject(json, myObject, settings);
如果您处理JsonSettings属性中的序列化错误,则可以继续填充对象并处理所有问题。签名如下:
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);
}