我发现自己在这个问题上有点迷茫。老实说,如果只是类结构与 JSON 错误不匹配,我看不到错误。但我对此表示怀疑,因为它与我用来创建 JSON 的类结构完全相同。
如果有人能指出我正确的方向,我会非常伟大。
我创建了一个dotnetfiddle,以避免用大量的代码来抓住问题。这是链接: 小提琴
我使用控制台应用程序生成该 JSON,该应用程序获取有关数据库架构的信息。我使用一个通用项目,其中包含其中定义的所有实体来加载内存中的数据,然后从该结构生成 JSON。然后,我在另一个应用程序上使用具有相同实体的同一项目,将另一个数据库架构与 JSON 日志进行比较。该应用程序无法反序列化 JSON。试图提供一个带有单个类的最小示例,正如您在小提琴上看到的那样......这也不会反序列化。 我的理解是,ObservableCollections 实际上应该毫无问题地序列化和反序列化,并且 INotifyPropertyChange 不应该引起问题(只要您不尝试触发具有空引用的事件(。所以。。。有人知道这里发生了什么吗?
编辑:忘了提。请注意如何仅反序列化基类型字符串...所以它正在运行一些反序列化,而不是像 ObservableCollection 或用户类这样的类。也许这有助于以某种方式查明问题。
EDIT2:添加了一个跟踪编写器,JSON.Net 跟踪正在检测对象的正确类型,所以我猜问题出在转换类型或初始化某些类型
问题在于您的属性获取者如何与 Json.Net 中的默认ObjectCreationHandling
设置相结合。 请允许我解释一下:
默认情况下,如果引用属性在反序列化期间具有现有(非 null(值,Json.Net 尝试重用现有实例并填充它,而不是创建新实例。 若要确定属性是否具有值,Json.Net 调用 getter。 在您的情况下,当支持字段为 null 时,getter 会返回一个新实例,但至关重要的是,它不会将支持字段设置为新实例:
get { return _header ?? new StoredProcedureDataHeader(); }
然后,Json.Net 填充新实例。 由于支持字段从未设置为新实例,因此该实例最终会被丢弃。 Json.Net 从不调用你的 setter,因为它假定你的对象已经有对新实例的引用,因为它从 getter 那里获得了该实例。 然后,当您在反序列化后下次调用该 getter 时,您会得到一个新的空实例,而不是您期望的实例。
有两种方法可以解决此问题:
更改 getter 以在创建新实例时设置支持字段,例如:
get { if (_header == null) { _header = new StoredProcedureDataHeader(); } return _header; }
或
将
ObjectCreationHandling
设置更改为Replace
以强制 Json.Net 在反序列化时始终创建新实例。 然后 Json.Net 会打电话给二传手而不是getter,我认为这是你想要的。var settings = new JsonSerializerSettings { ObjectCreationHandling = ObjectCreationHandling.Replace }; var data = JsonConvert.DeserializeObject<StoredProcedureData>(json, settings);
在您的情况下,我实际上建议您同时应用这两个修复程序。 如果你不修复你的getter(选项1(,你可能会在代码的其他地方遇到类似的问题。 例如,您可能有如下所示的内容:
var data = new StoredProcedureData();
data.Header.SPName = "foo";
if (data.Header.SPName == "foo")
{
Console.WriteLine("foo");
}
else
{
Console.WriteLine("oops");
}
猜猜将打印哪个值?
选项 2 将防止可能出现的意外结果,如果您碰巧在某处初始化了一个集合以具有一组默认值。 例如,如果您有这样的内容:
public StoredProcedureData()
{
_funcRef = new ObservableCollection<string>();
_funcRef.Add("Initialize");
}
然后,当您反序列化时,您将获得默认值以及来自 JSON 的值,这可能不是您想要的。 将ObjectCreationHandling
设置为Replace
将确保您最终只会得到从 JSON 反序列化的值。