当使用.NET Newtonsoft.json组件反序列化一些有效的json时,为什么我的POCO中的所有集合都为nul
本文关键字:json POCO 我的 为什么 nul 集合 组件 Newtonsoft NET 反序列化 有效 | 更新日期: 2023-09-27 18:12:07
问题
当尝试使用Newtonsoft的Json.NET nuget库反序列化某些Json时,当我不提供JsonSerializerSettings
时,我的集合属性为null-为什么?
详细信息
我有一些有效的json数据,并将其反序列化到我的自定义类/POCO中。现在,所有简单的属性(如string
s、int
's等(都已正确设置。我的简单子类也可以正确设置(例如User
属性或BuildingDetails
属性等(。
不过,我所有的收藏品都是null
。二传手从不被点名(我在那里放了一个破发点,他们没有被点名,但其他人确实被点名/被点名(。
例如。所有物
List<Media> Images { get { ... } set { ... } }
例如。
var foo = new Foo();
var json = JsonConvert.Serialize(foo);
var anotherFoo = JsonConvert.Deserialize<Foo>(json);
// the collection properties on anotherFoo are null
现在,这是一件疯狂的事情:当我使用JsonSerializerSettings
时,它现在可以工作了:
// NOTE: ModifiedDataContractResolver <-- skips any property that is a type
/ ModifiedData (which is a simple custom class I have).
var settings = new JsonSerializerSettings
{
ContractResolver = new ModifiedDataContractResolver(),
ObjectCreationHandling = ObjectCreationHandling.Replace,
Formatting = Formatting.Indented
};
var foo = new Foo();
var json = JsonConvert.Serialize(foo, settings);
var anotherFoo = JsonConvert.Deserialize<Foo>(json, settings);
我的收藏品现在已经准备好了!
请注意:ObjectCreationHandling = ObjectCreationHandling.Replace
。这是一个神奇的环境,使事情发挥作用。
这是在做什么?为什么没有设置意味着我的集合没有被设置/创建等等
另一条线索。如果我没有设置setter,则集合为null/未设置。如果我有这样的设置器,它也会失败:
public List<Media> Images
{
get { return new List<Media> { new Media{..}, new Media{..} }; }
set { AddImages(value); }
}
如果我不返回列表,则在getter
中集合为SET!它有效!
private List<Media> _images;
public List<Media> Images
{
get { return _images; }
set { AddImages(value); }
}
注意到它现在只是使用烘焙场吗?
集合的GETTER属性和结果之间有一些奇怪的联系/相关性,Json.net是如何使用auto
设置这些数据的?
您的问题是,您试图反序列化的属性在对象图中获取并设置了代理集合,而不是"真实"集合。这样做与Json.NET用于构造和填充返回引用类型对象、集合和字典的属性的算法的实现决策相冲突。该算法是:
-
它调用父类中的getter来获取正在反序列化的属性的当前值。
-
如果为null,并且除非使用自定义构造函数,否则它会分配属性返回类型的实例(使用类型的
JsonContract.DefaultCreator
方法(。 -
它调用父级中的setter,将分配的实例设置回父级中。
-
它继续填充类型的实例。
-
在填充实例之后,它不会再次将实例设置回原处
在开始时调用getter可以填充某个类型的实例(例如,使用JsonConvert.PopulateObject()
(,递归地填充该类型所引用的其他类型的任何预先分配的实例。(这就是JsonConverter.ReadJson()
中existingValue
参数的用途。(
然而,以这种方式填充预先分配的对象图的能力是有代价的:图中遇到的每个对象都必须是真实的对象,而不是为序列化目的创建的某个代理对象。如果getter返回的对象只是某个代理,那么代理将被填充,而不是"真正的"对象——除非代理有某种机制将对其数据的更改传递回其创建者。
(虽然这个决定可能看起来不可取,但这并不罕见;XmlSerializer
的工作方式也是一样的。有关执行此操作的序列化程序的列表,请参阅具有默认代码的集合属性的XML反序列化。(
正如您所观察到的,ObjectCreationHandling = ObjectCreationHandling.Replace
会更改此算法,以便对集合进行分配、填充和设置。这是启用代理集合反序列化的一种方法。
作为另一种解决方法,您可以选择序列化和反序列化代理数组:
[JsonIgnore]
public List<Media> Images
{
get { return new List<Media> { new Media{..}, new Media{..} }; }
set { AddImages(value); }
}
[JsonProperty("Images")] // Could be private
Media [] ImagesArray
{
get { return Images.ToArray(); }
set { AddImages(value); }
}
对于数组,Json.NET(和XmlSerializer
(必须在数组被完全读取后调用setter,因为在完全读取之前无法知道大小,因此在完全读取之后无法分配和设置数组。
(你也可以用代理ObservableCollection
来做一些技巧,但我不推荐它。(