【问题标题】:Deserialize objects from array nested inside Json response从嵌套在 Json 响应中的数组中反序列化对象
【发布时间】:2017-09-14 22:33:40
【问题描述】:

我正在尝试使用 Newtonsoft.Json 反序列化嵌套在 Json 响应中的一些对象。我想将以下 Jsons Term 对象反序列化到一个列表中。我在 Json 响应中有很多 Term 对象,因此性能和紧凑性对我来说很重要。我也只想定义Term 类,因为我暂时不关心其他数据。

我为 Term 定义了一个模型:

public class Term
{
    public string Known { get; set; }
    public string Word { get; set; }
}

我的 Json 看起来像这样:

{
    "myName":"Chris",
    "mySpecies":"Cat",
    "myTerms":
    [
        {
            "Term":
            {
                "Known":"true",
                "Word":"Meow"
            }
        },
        {
            "Term":
            {
                "Known":"false",
                "Word":"Bark"
            }
        }
    ]
}

我的 C# 反序列化代码:

var response = await httpClient.GetAsync(uri);
string responseString = response.Content.ReadAsStringAsync().GetResults();
var searchTermList = JsonConvert.DeserializeObject<List<Term>>(responseString);

我收到的问题/错误是,不确定如何从 json 响应中获取这些术语:

{Newtonsoft.Json.JsonSerializationException: Cannot deserialize the current 
JSON object (e.g. {"name":"value"}) into type 
'System.Collections.Generic.List`1[CoreProject.Models.Term]' because 
the type requires a JSON array (e.g. [1,2,3]) to deserialize correctly.
To fix this error either change the JSON to a JSON array (e.g. [1,2,3]) or 
change the deserialized type so that it is a normal .NET type (e.g. not a 
primitive type like integer, not a collection type like an array or List<T>) 
that can be deserialized from a JSON object. JsonObjectAttribute can also be 
added to the type to force it to deserialize from a JSON object.

任何建议将不胜感激:)

【问题讨论】:

标签: c# json json.net


【解决方案1】:

您收到该错误是因为您试图将 JSON 反序列化为 T(特别是 Term)的 List&lt;T&gt;,但根 JSON 容器不是数组,它是一个对象 -- 一个由{} 包围的无序键/值对集合——其中包含与您的Term 相对应的相当深入嵌入的对象集合。

鉴于此,您可以使用 http://json2csharp.com/Paste JSON as Classes 自动生成与您的 JSON 对应的完整数据模型,然后反序列化为该模型并选择感兴趣的部分。

但是,如果您不想定义完整的数据模型,则可以通过将 JSON 加载到中间 JToken hierarchy 然后使用 SelectTokens():

var root = JToken.Parse(responseString);
var searchTermList = root.SelectTokens("myTerms[*].Term")
    .Select(t => t.ToObject<Term>())
    .ToList();

注意事项:

  • 查询字符串"myTerms[*].Term" 包含JSONPath 通配符运算符[*]。该运算符匹配父元素"myTerms"下的所有数组元素。

    Json.NET 支持 Querying JSON with JSONPath 中记录的 JSONPath 语法。

  • 如果 JSON 比您的问题中显示的更复杂,您可以使用 JSONPath 递归下降运算符 ... 来查找 JSON 对象层次结构中任何级别的 Term 对象,例如:

    var searchTermList = root.SelectTokens("..Term")
        .Select(t => t.ToObject<Term>())
        .ToList();
    
  • 选择相关 JSON 对象后,您可以使用 Jtoken.ToObject&lt;Term&gt;() 将每个对象反序列化为最终的 c# 模型。

示例fiddle

【讨论】:

  • +one 不知道如何使用 jsonpath。好主意!
【解决方案2】:

试试这个

public class Term
{
    public string Known { get; set; }
    public string Word { get; set; }
}
public class Response
{
    public List<TermWrapper> MyTerms { get; set; }
}
public class TermWrapper
{
    public Term Term { get; set; }
}

...

var response = await httpClient.GetAsync(uri);
string responseString = response.Content.ReadAsStringAsync().GetResults();
var searchTermList = JsonConvert
    .DeserializeObject<Response>(responseString)
    .MyTerms
    .Select(x => x.Term);

您必须考虑 JSON 的完整结构。您有一个具有 3 个属性的对象。您感兴趣的是对象数组,但对象不是术语,它们是具有称为“术语”的属性的对象。该属性本身的类型为Term。通过创建具有相似结构的类,您可以从结构中提取所有必要的数据。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-10-23
    • 1970-01-01
    • 1970-01-01
    • 2014-03-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多