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

时间:2017-07-26 14:19:41

标签: c# json.net observablecollection inotifypropertychanged

我发现自己在这一点上有点失落。老实说,如果只是一个类结构与JSON错误不匹配,我就看不到错误。但我怀疑它,因为它是我用来创建JSON的类结构。

如果有人能指出我正确的方向,我会非常致命。

我创建了一个dotnetfiddle,以避免使用大量代码来抓住问题。这是链接:Fiddle

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

编辑:忘了提。注意只有基类型字符串被反序列化...所以它正在运行一些反序列化,而不是像ObservableCollection或用户类这样的类。也许这有助于以某种方式确定问题。

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

1 个答案:

答案 0 :(得分:1)

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

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

    get { return _header ?? new StoredProcedureDataHeader(); }
然后,Json.Net填充新实例。因为后备字段从未设置为新实例,所以该实例最终会被丢弃。 Json.Net从不调用你的setter,因为它假设你的对象已经有一个对新实例的引用,因为它从getter获取了该实例。然后,当您在反序列化后接下来调用该getter时,您将获得一个新的空实例,而不是您期望的那样。

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

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

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

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

      var settings = new JsonSerializerSettings
      {
          ObjectCreationHandling = ObjectCreationHandling.Replace
      };
      
      var data = JsonConvert.DeserializeObject<StoredProcedureData>(json, settings);
      
    2. 在您的情况下,我实际上建议您应用两个修复程序。如果你没有修复你的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");
      }
      

      然后在反序列化时,您将获得默认值 plus 来自JSON的值,这可能不是您想要的。将ObjectCreationHandling设置为Replace将确保您最终只得到从JSON反序列化的值。