具有ObservableCollection和INotifyPropertyChange的Json.Net类层次结构被序列化但未反序列化 [英] Json.Net Class hierarchy with ObservableCollection and INotifyPropertyChange gets serialized but not deserialized

查看:207
本文介绍了具有ObservableCollection和INotifyPropertyChange的Json.Net类层次结构被序列化但未反序列化的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我发现自己对此有些迷茫.老实说,如果只是类结构与JSON错误不匹配,我就看不到错误.但我对此表示怀疑,因为它与我用来创建JSON的类结构非常相似.

I find myself a bit lost on this one. I honestly can't see the error if it's just a class structure doesn't match JSON error. But I doubt it since it's the very same class structure I'm using to create the JSON.

如果有人能指出我正确的方向,那我将非常伟大.

If anyone can point me in the right direction, I'd be most greateful.

我创建了一个dotnetfiddle以避免用大量的代码来抓住问题.这是链接:小提琴

I've created a dotnetfiddle to avoid clutching the question with huge pieces of code. Here's the link: Fiddle

我使用控制台应用程序生成该JSON,该控制台应用程序获取有关数据库架构的信息.我使用一个公共项目,其中定义了所有实体,以将数据加载到内存中,然后从该结构生成JSON.然后,我将同一项目与另一个应用程序上的相同实体一起使用,以将另一个数据库架构与JSON日志进行比较.该应用程序无法反序列化JSON.试图为单个类提供一个最小的示例,就像在小提琴上看到的那样……也不会反序列化. 据我了解,ObservableCollections实际上应该进行序列化和反序列化而不会出现问题,并且INotifyPropertyChange不会引起问题(只要您不尝试使用空引用触发事件).所以...有人知道这是怎么回事吗?.

I generate that JSON with a console application that gets info on the DB schema. I use a common project with all the entities defined in it to load the data in memory and then generate the JSON from that structure. Then I use the same project with the same entities on another application to compare another DB schema to the JSON log. That application is unable to deserialize the JSON. Tried to provide a minimal example with a single class and as you can see on the fiddle...that doesn't deserialize either. It is my understanding that ObservableCollections should in fact serialize and deserialize without issues, and that INotifyPropertyChange should not cause issues (as long as you're not trying to fire an event with a null reference). So...anyone has any idea what's going on here?.

忘记提及.注意只有基本类型的字符串是如何反序列化的...因此它正在运行一些反序列化,而不是像ObservableCollection或用户类这样的类.也许这有助于以某种方式查明问题所在.

Forgot to mention. Notice how only the base type string gets deserialized...so it IS running some deserialization, just not of classes like ObservableCollection or user classes. Maybe that helps somehow to pinpoint the issue.

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

Added a trace writer and the JSON.Net trace is detecting the right type for the objects, so I'm guessing the issue is on converting types or initializing some of the types

推荐答案

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

The problem is in how your property getters work combined with the default ObjectCreationHandling setting in Json.Net. Allow me to explain:

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

By default, if a reference property has an existing (non-null) value during deserialization, Json.Net tries to reuse the existing instance and populate it instead of creating a new instance. To find out whether the property has a value, Json.Net calls the getter. In your case, the getter returns a new instance when the backing field is null, but, critically, it does not set the backing field to the new instance:

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

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

Json.Net then populates the new instance. Because the backing field was never set to the new instance, that instance ultimately gets thrown away. Json.Net never calls your setter because it assumes that your object already has a reference to the new instance, since it got that instance from the getter. Then, when you next call that getter after deserialization, you get a new, empty instance back instead of what you were expecting.

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

There are two ways to fix the problem:

  1. 每次创建新实例时,更改您的吸气剂以设置背景字段,例如:

  1. Change your getters to set the backing field whenever a new instance is created, for example:

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

OR

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

  1. Change the ObjectCreationHandling setting to Replace to force Json.Net to always create new instances when deserializing. Json.Net will then call the setter and not the getter, which I think is what you want.

var settings = new JsonSerializerSettings
{
    ObjectCreationHandling = ObjectCreationHandling.Replace
};

var data = JsonConvert.DeserializeObject<StoredProcedureData>(json, settings);

在您的情况下,我实际上建议您同时应用 个修复程序.如果您不修复吸气器(选项1),则可能在代码的其他地方遇到类似的问题.例如,您可能会有类似的内容:

In your case, I would actually recommend that you apply both fixes. If you don't fix your getters (option 1), you could run into a similar issue elsewhere in your code. For example, you might have something like this:

var data = new StoredProcedureData();
data.Header.SPName = "foo";
if (data.Header.SPName == "foo")
{
    Console.WriteLine("foo");
}
else
{
    Console.WriteLine("oops");
}

猜猜将打印哪个值?

如果您恰好在某个地方初始化了一个具有默认值集合的集合,那么选项2将防止可能出现的意外结果.例如,如果您有这样的事情:

And option 2 will protect against possibly unexpected results if you happen to have initialized a collection somewhere to have a default set of values. For example, if you had something like this:

public StoredProcedureData()
{
    _funcRef = new ObservableCollection<string>();
    _funcRef.Add("Initialize");
}

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

then when you deserialize, you will get the default values plus the values from the JSON, which is probably not what you want. Setting ObjectCreationHandling to Replace will ensure that you will end up with just the values which were deserialized from the JSON.

这篇关于具有ObservableCollection和INotifyPropertyChange的Json.Net类层次结构被序列化但未反序列化的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆