使用 .NET Newtonsoft.Json 组件反序列化某些有效 json 时,为啥我的 POCO 中的所有集合都为空

Posted

技术标签:

【中文标题】使用 .NET Newtonsoft.Json 组件反序列化某些有效 json 时,为啥我的 POCO 中的所有集合都为空【英文标题】:Why are all the collections in my POCO are null when deserializing some valid json with the .NET Newtonsoft.Json component使用 .NET Newtonsoft.Json 组件反序列化某些有效 json 时,为什么我的 POCO 中的所有集合都为空 【发布时间】:2015-12-06 03:43:54 【问题描述】:

问题

当尝试使用 Newtonsoft 的 Json.NET nuget 库反序列化某些 Json 时,如果我不提供 JsonSerializerSettings,我的集合属性为空 - 为什么?

详情

我有一些有效的 json 数据并将其反序列化到我的自定义类/POCO 中。现在,所有简单属性(如strings、int's 等)都已正确设置。我的简单子类也得到了正确设置(例如User 属性或BuildingDetails 属性等)。

不过,我所有的收藏都是null。 setter 永远不会被调用(我已经在其中设置了一个断点并且它们没有被设置,但其他的确实被调用/设置)。

例如。属性。

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。这是使事情顺利进行的神奇设置。

这是在做什么?为什么没有有这个设置意味着我的集合没有被设置/创建/等等?

另一个线索。如果我 not 有设置器,则集合为空/未设置。如果我有这样的二传手,它也会失败:

public List<Media> Images

    get  return new List<Media>  new Media.., new Media.. ; 
   set  AddImages(value); 

如果我不返回列表,则在getter 中,集合已设置!它有效!

private List<Media> _images;
public List<Media> Images

    get  return _images; 
    set  AddImages(value); 

注意它现在只是使用烘焙场了吗?

好像集合的 GETTER 属性和结果之间存在一些奇怪的联系/关联,以及 Json.net 如何使用 auto 设置设置这些数据?

【问题讨论】:

相关:XML Deserialization of collection property with code defaults(问题是关于 XML,但答案提到了 Json.NET)。 【参考方案1】:

您的问题是您尝试反序列化的属性获取并设置了一个 proxy 集合,而不是对象图中的“真实”集合。这样做违反了关于 Json.NET 用于构造和填充返回引用类型对象、集合和字典的属性的算法的实现决策。该算法是:

    调用父类中的getter获取被反序列化的属性的当前值。

    如果为 null,除非使用自定义 constructor,否则它会分配属性返回类型的实例(使用该类型的 JsonContract.DefaultCreator 方法)。

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

    它继续填充该类型的实例。

    在填充实例后,它不会再次将其设置回去。

在开始时调用 getter 可以填充一个类型的实例(例如使用JsonConvert.PopulateObject()),递归地填充该类型所引用的其他类型的任何预分配实例。 (这就是JsonConverter.ReadJson()existingValue参数的用途。)

但是,以这种方式填充预分配对象图的能力是有代价的:图中遇到的每个对象都必须是 真实 对象,而不是为序列化目的而创建的某些代理对象.如果 getter 返回的对象只是某个代理,则代理将被填充,而不是“真实”对象 - 除非代理具有某种机制将其数据的更改传递回其发起者。

(虽然这个决定可能看起来不受欢迎,但这并不罕见;XmlSerializerworks the same way。有关执行此操作的序列化程序列表,请参阅XML Deserialization of collection property with code defaults。)

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 进行技巧,但我不建议这样做。)

【讨论】:

我想我的脑袋都快疯了:/ 我需要读这个大约 10 次,我觉得...

以上是关于使用 .NET Newtonsoft.Json 组件反序列化某些有效 json 时,为啥我的 POCO 中的所有集合都为空的主要内容,如果未能解决你的问题,请参考以下文章

asp.net环境下对 Newtonsoft.json 引用的设置问题

.Net使用Newtonsoft.Json.dll(JSON.NET)对象序列化成json反序列化json示例教程

如何使用 NewtonSoft Json.Net 将 Json 字典反序列化为平面类

在Asp.Net Core 3.0中如何使用 Newtonsoft.Json 库序列化数据

在.NET使用Newtonsoft.Json转换,读取,写入json

Newtonsoft.Json(Json.Net)学习笔记