当 JSON 字典为空时,反序列化 JSON 字符串失败

CLR*_*W97 9 c# json.net

目前,当我尝试使用Newtonsoft.Json反序列化从第 3 方收到的 json 字符串(--> 我无法自己更改接收到的 json 字符串)时,我面临以下问题:

json 包含一个字典(以及我未在此处列出的其他一些条目):

"food": {
        "Menu 1": "abc",
        "Menu 2": "def"
}
Run Code Online (Sandbox Code Playgroud)

我创建了一个包含该属性的类(加上我在此处未列出的属性):

Dictionary<string, string> Food {get; set;}
Run Code Online (Sandbox Code Playgroud)

在这种情况下,json 的反序列化效果很好。当食物空了时就会出现问题:

{
      "food": [],
}
Run Code Online (Sandbox Code Playgroud)

在这种情况下,食物似乎不是字典而是数组。 这就是反序列化失败并出现以下错误的原因:

Newtonsoft.Json.JsonSerializationException:“无法将当前 JSON 数组(例如 [1,2,3])反序列化为类型 'System.Collections.Generic.Dictionary`2[System.String,System.String]',因为该类型需要JSON 对象(例如 {"name":"value"})以正确反序列化。要修复此错误,请将 JSON 更改为 JSON 对象(例如 {"name":"value"})或将反序列化类型更改为数组或实现集合接口(例如 ICollection、IList)的类型(例如可以从 JSON 数组反序列化的 List)。还可以将 JsonArrayAttribute 添加到该类型以强制其从 JSON 数组反序列化。路径“食物”。”

请问有人可以帮我解决这个问题吗?

编辑去 盐化代码:

public T DeserializeAPIResults<T>(string json)
{
        JObject obj = JsonConvert.DeserializeObject<JObject>(json);
        return obj.GetValue("canteen").ToObject<T>();
}
Run Code Online (Sandbox Code Playgroud)

编辑2 完整的json值:

        {
        "canteen": [
            {
                "name": "Canteen1",
                "src": "a link",
                "food": {
                    "Menu 1": "abc",
                    "Menu 2": "def",
                    "Menu 3": "ghi",
                    "Menu 4": "jkl",
                    "Menu 5": "mno"
                }
            },
            {
                "name": "Canteen2",
                "src": "a link",
                "food": {
                    "Menu 1": "abc",
                    "Menu 2": "def",
                    "Menu 3": "ghi"
                }
            },
            {
                "name": "Canteen3",
                "src": "a link",
                "food": {
                    "Line 1": "abc",
                    "Line 2": "def",
                    "Line 3": "ghi"
                }
            }
        ]
    }
Run Code Online (Sandbox Code Playgroud)

没有值的完整 json:

{
    "canteen": [
        {
            "name": "Canteen1",
            "src": "a link",
            "food": [],
        },
        {
            "name": "Canteen2",
            "src": "a link",
            "food": [],
        },
        {
            "name": "Canteen3",
            "src": "a link",
            "food": [],
       }
    ]
}
Run Code Online (Sandbox Code Playgroud)

编辑3 班级:

public sealed class Canteen
{
    [JsonProperty("name")]
    public string Name { get; set; }
    [JsonProperty("src")]
    public string Src { get; set; }
    [JsonProperty("food")]
    public Dictionary<string, string> Food { get; set; }
    public Canteen() { }
}
Run Code Online (Sandbox Code Playgroud)

以及方法调用:

Canteen[] canteens = DeserializeAPIResults<Canteen[]>(json);
Run Code Online (Sandbox Code Playgroud)

er-*_*sho 5

如果您的特定键的值不固定并且数据必须是可配置的,那么 Newtonsoft.json 有一个可以在这里使用的功能,那就是[JsonExtensionData]。阅读更多

\n\n
\n

现在,当对象被序列化时,会写入扩展数据。读取和写入扩展数据可以自动往返所有 JSON,而无需将每个属性添加到您\xe2\x80\x99 反序列化到的 .NET 类型。仅声明您\xe2\x80\x99感兴趣的属性,然后让扩展数据完成其余的工作。

\n
\n\n

当您的第 3 方 json 有一个名称为键food且其值作为对象时,您将尝试反序列化Dictionary<string, string> Food {get; set;},并且您的反序列化方法会正确反序列化您的 json。

\n\n

但是,当foodkey 有数组时,您的方法无法反序列化,因为您试图将数组反序列[]化为string.

\n\n

如果你使用

\n\n
[JsonExtensionData]\npublic Dictionary<string, JToken> Food { get; set; }\n
Run Code Online (Sandbox Code Playgroud)\n\n

代替

\n\n
Dictionary<string, string> Food {get; set;}\n
Run Code Online (Sandbox Code Playgroud)\n\n

然后你的反序列化就起作用了。

\n\n

所以最后你的班级将是

\n\n
public sealed class Canteen\n{\n    [JsonProperty("name")]\n    public string Name { get; set; }\n    [JsonProperty("src")]\n    public string Src { get; set; }\n\n    [JsonExtensionData]\n    public Dictionary<string, JToken> Food { get; set; }\n    public Canteen() { }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n
\n\n

选择:

\n\n

如果您在类中声明Food属性数据类型,例如JTokenCanteen

\n\n
public sealed class Canteen\n{\n    [JsonProperty("name")]\n    public string Name { get; set; }\n    [JsonProperty("src")]\n    public string Src { get; set; }\n\n    [JsonProperty("food")]\n    public JToken Food { get; set; }\n\n    public Canteen() { }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

food然后,无论您的键是对象还是数组,您都可以成功反序列化 json 。

\n\n

然后您可以从食堂数组访问每个食堂并检索每个食堂的和name键/值对。srcfood

\n\n

优点JToken是你可以检查它的类型,无论是对象还是数组

\n\n
Canteen[] canteens = DeserializeAPIResults<Canteen[]>(json);\n\nforeach (var canteen in canteens)\n{\n    string name = canteen.Name;\n    string src = canteen.Src;\n    JToken food = canteen.Food;\n\n    if (food.Type == JTokenType.Object)\n    {\n        Dictionary<string, string> foods = food.ToObject<Dictionary<string, string>>();\n    }\n    else if (food.Type == JTokenType.Array)\n    {\n        //Do something if "foods" is empty array "[]"\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n