Json.Net Class 具有 ObservableCollection 和 INotifyPropertyChange 的层次结构被序列化但未被反序列化

Json.Net Class hierarchy with ObservableCollection and INotifyPropertyChange gets serialized but not deserialized

我发现自己对此有点迷茫。如果只是 class 结构与 JSON 错误不匹配,我真的看不到错误。但我对此表示怀疑,因为它与我用来创建 JSON.

的 class 结构完全相同

如果有人能指出正确的方向,我将不胜感激。

我创建了一个 dotnetfiddle 以避免用大量代码来解决问题。这是 link:Fiddle

我使用控制台应用程序生成 JSON,该应用程序获取有关数据库架构的信息。我使用一个包含其中定义的所有实体的公共项目来将数据加载到内存中,然后从该结构生成 JSON。然后,我在另一个应用程序上使用具有相同实体的相同项目,将另一个数据库模式与 JSON 日志进行比较。该应用程序无法反序列化 JSON。试图提供一个带有单个 class 的最小示例,正如您在 fiddle 上看到的那样......它也没有反序列化。 据我了解,ObservableCollections 实际上应该毫无问题地序列化和反序列化,并且 INotifyPropertyChange 不应该导致问题(只要您不尝试使用空引用触发事件)。所以...有人知道这里发生了什么吗?

编辑:忘记提及。请注意如何仅对基本类型字符串进行反序列化...所以它是 运行 一些反序列化,而不是像 ObservableCollection 或用户 classes 那样的 classes。也许这有助于以某种方式查明问题。

EDIT2:添加了跟踪编写器,JSON.Net 跟踪正在检测对象的正确类型,所以我猜问题出在转换类型或初始化某些类型上

问题在于您的 属性 getter 如何与 Json.Net 中的默认 ObjectCreationHandling 设置相结合。请允许我解释一下:

默认情况下,如果引用 属性 在反序列化期间具有现有(非空)值,Json.Net 会尝试重用现有实例并填充它,而不是创建新实例。要查明 属性 是否有值,Json.Net 调用 getter。在您的情况下, getter returns 支持字段为空时的新实例,但关键是,它不会将支持字段设置为新实例:

    get { return _header ?? new StoredProcedureDataHeader(); }

Json.Net 然后填充新实例。因为支持字段从未设置为新实例,所以该实例最终会被丢弃。 Json.Net 永远不会调用您的 setter,因为它假定您的对象已经具有对新实例的引用,因为它从 getter 中获得了该实例。然后,当你在反序列化之后调用 getter 时,你会得到一个新的空实例,而不是你所期望的。

有两种方法可以解决这个问题:

  1. 更改您的 getters 以在创建新实例时设置支持字段,例如:

    get 
    {
        if (_header == null)
        {
            _header = new StoredProcedureDataHeader();
        }
        return _header;
    }
    

  1. ObjectCreationHandling 设置更改为 Replace 以强制 Json.Net 在反序列化时始终创建新实例。 Json.Net 然后会调用 setter 而不是 getter,我认为这就是你想要的。

    var settings = new JsonSerializerSettings
    {
        ObjectCreationHandling = ObjectCreationHandling.Replace
    };
    
    var data = JsonConvert.DeserializeObject<StoredProcedureData>(json, settings);
    

对于您的情况,我实际上建议您应用两个 修复程序。如果您不修复 getters(选项 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.

反序列化的值