当使用.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中。现在,所有简单的属性(如strings、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设置这些数据的?

当使用.NET Newtonsoft.json组件反序列化一些有效的json时,为什么我的POCO中的所有集合都为nul

您的问题是,您试图反序列化的属性在对象图中获取并设置了代理集合,而不是"真实"集合。这样做与Json.NET用于构造和填充返回引用类型对象、集合和字典的属性的算法的实现决策相冲突。该算法是:

  1. 它调用父类中的getter来获取正在反序列化的属性的当前值。

  2. 如果为null,并且除非使用自定义构造函数,否则它会分配属性返回类型的实例(使用类型的JsonContract.DefaultCreator方法(。

  3. 它调用父级中的setter,将分配的实例设置回父级中。

  4. 它继续填充类型的实例。

  5. 在填充实例之后,它不会再次将实例设置回原处

在开始时调用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来做一些技巧,但我不推荐它。(