Newtonsoft JSON.NET 反序列化错误

Posted

技术标签:

【中文标题】Newtonsoft JSON.NET 反序列化错误【英文标题】:Newtonsoft JSON.NET Deserialization error 【发布时间】:2015-02-16 13:03:36 【问题描述】:

我在单个文件中有一堆长的 json 输出。我需要读取这些文件并将它们反序列化为最初生成 json 的实体(我可以访问原始实体)。每个文件都有通过序列化 IEnumerable<Response<MyEntity>> 类型的对象生成的 json 输出。

我在尝试反序列化时遇到异常,但我不明白为什么。这是我反序列化的尝试:

List<string> jsonOutputStrings;
// Read json from files and populate jsonOutputStrings list
List<Response<MyEntity>> apiResponses = new List<Response<MyEntity>>();

foreach (string json in jsonOutputStrings)

    apiResponses.AddRange(JsonConvert.DeserializeObject<List<Response<MyEntity>>>(json, new JsonSerializerSettings  TypeNameHandling = TypeNameHandling.All ).ToList());

我还尝试反序列化为 IEnumerable 而不是列表:

apiResponses.AddRange(JsonConvert.DeserializeObject<IEnumerable<Response<MyEntity>>>(json, new JsonSerializerSettings  TypeNameHandling = TypeNameHandling.All ).ToList());

我得到以下异常:

类型的第一次机会异常 'Newtonsoft.Json.JsonSerializationException' 发生在 Newtonsoft.Json.dll

附加信息:无法创建和填充列表类型 System.Linq.Enumerable+WhereSelectListIterator`2[Entities.Requirement,Entities.RequirementEntity]。 小路 '$values[0].ReturnedEntity.Summaries.$values[0].Requirements.$values', 第 1 行,位置 715。

整个 json 太长,无法发布(并且还包含一些机密数据),但我确实在 json 中找到了一个位置:

"Requirements":"$id":"7","$type":"System.Linq.Enumerable+WhereSelectListIterator`2[[Entities.Requirement, Entities],[Entities.RequirementEntity, API.Entities]], System.Core","$values":[...]

在我尝试反序列化的实体(与最初序列化的实体相同)中,Requirements 的类型为 IEnumerable&lt;Entities.RequirementEntity&gt;

MyEntity 的序列化如何工作对我来说没有意义,但反序列化为相同类型却没有。我该如何解决这个问题?

【问题讨论】:

您是否尝试反序列化为IEnumerable&lt;ApiResponse&lt;StudentReportApiEntity&gt;&gt;&gt; 是的,这就是我首先做的。我仍然得到同样的错误。 IEnumerable&lt;Entities.RequirementEntity&gt; 属性似乎有问题,该属性嵌套在父实体中的多个级别,但我不知道为什么...... 这可能是一个愚蠢的问题,但是您要反序列化的 List 类型的命名空间是什么? System.Collections...? 如果您从序列化结果中排除该类型,那么它对您有用吗? (检查TypeNameHandling 设置) @AndrewWhitaker - 谢谢!当我在反序列化时将 TypeNameHandling 设置为 None 时,它​​起作用了。 【参考方案1】:

您必须浏览 Response&lt;&gt;MyEntity 并查看集合是如何初始化的。这告诉我们,某个类中的一个集合是使用 linq 中的 Where 方法创建的。您可以通过执行以下代码来重现此错误:

class MyEntity

    public MyEntity()
    
        Data = new List<string>().Where(x => true);
    

    public IEnumerable<string> Data  get; set; 



class Program

    static void Main(string[] args)
    
        string data = @"[""Data"":[""a"",""b""]]";
        var j = JsonConvert.DeserializeObject<IEnumerable<MyEntity>>(data);
    

另一种可能性是在 json 中有元数据。那么你有两个解决方案:

TypeNameHandling 设置为TypeNameHandling.None(正如评论中提到的那样); 将字符串中的不工作类型替换为工作类型

当您拥有IEnumerable&lt;BaseType&gt; 并且该列表包含BaseType 的子类型时,使用TypeNameHandling.None 可能会导致错误的反序列化。

在这种情况下,您应该选择第二种选择。基本上你应该替换任何不反序列化的类型,例如用List替换它。

示例代码:

class MyEntity

    public IEnumerable<string> Data  get; set; 


class Program

    static void Main(string[] args)
    
        IList<MyEntity> entities = new MyEntity[] 
            new MyEntity  Data = new []  "1", "2" .Where(x => x != string.Empty) ,
            new MyEntity  Data = new []  "A", "B" .AsQueryable().Where(x => x != string.Empty) ,
            new MyEntity  Data = new List<string>  "A", "B"  ,
        ;

        string data = JsonConvert.SerializeObject(entities, Formatting.Indented, new JsonSerializerSettings  TypeNameHandling = TypeNameHandling.All );
        data = Regex.Replace(data, "\"\\$type\":\\s+\"System.Linq.Enumerable\\+WhereArrayIterator(.+?), System.Core\",", "\"$type\": \"System.Collections.Generic.List$1, mscorlib\",", RegexOptions.Singleline);

        var j = JsonConvert.DeserializeObject<IEnumerable<MyEntity>>(data, new JsonSerializerSettings  TypeNameHandling = TypeNameHandling.All );
    

【讨论】:

谢谢,这很有帮助。很可能这就是正在发生的事情,我将不得不寻找它以防止将来发生这种情况。我现在遇到的问题是,我们已经有大约 1000 个包含数月数据的这些 json 文件,而且我们没有办法重新生成它们。有什么方法可以操纵 json 字符串/json 文件,使它们看起来像你描述的那样不是用 linq 创建的集合,以便我可以反序列化它们? 这个问题与json本身无关,与.net类有关。在反序列化集合时,反序列化器会尝试向其中添加元素。如果集合是只读的(例如数组或 linq 结果),则抛出异常。您应该在代码中搜索集合初始值设定项。例如,如果您从我的代码中删除 'where',它将起作用。 在我的情况下,我在列表中没有导致问题的集合初始化程序,但是当最初创建和序列化 json 时,linq 用于填充 IEnumerable(不是作为类的一部分,只是使用二传手)。 我现在看到你有元数据,这会导致问题。您也可以不将TypeNameHandling 设置为None,而是将字符串中不支持的类型名称替换为支持的类型名称。我在答案中添加了示例代码。【参考方案2】:

序列化实体中是否有 Linq 结果?也许定义为 IEnumerable 并填充了 Linq Where 结果?看来这就是你的问题所在。 在序列化之前,您应该将其转换为 List 或 Array。

【讨论】:

谢谢,这很有帮助。很可能这就是正在发生的事情,我将不得不寻找它以防止将来发生这种情况。我现在遇到的问题是,我们已经有大约 1000 个包含数月数据的这些 json 文件,而且我们没有办法重新生成它们。有什么方法可以操纵 json 字符串/json 文件,使它们看起来像你描述的那样不是用 linq 创建的集合,以便我可以反序列化它们?

以上是关于Newtonsoft JSON.NET 反序列化错误的主要内容,如果未能解决你的问题,请参考以下文章

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

一:Newtonsoft.Json 支持序列化与反序列化的.net 对象类型;

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

使用Newtonsoft.Json.dll(JSON.NET)动态解析JSON.net 的json的序列化与反序列化

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

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