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

时间:2018-11-24 13:01:27

标签: c# json.net

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

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

"food": {
        "Menu 1": "abc",
        "Menu 2": "def"
}

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

Dictionary<string, string> Food {get; set;}

在这种情况下,对json的反序列化工作正常。 食物为空时会发生问题:

{
      "food": [],
}

在这种情况下,food似乎不是字典而是数组。 这就是为什么反序列化失败并显示以下错误的原因:

  

Newtonsoft.Json.JsonSerializationException:“无法反序列化   当前JSON数组(例如[1,2,3])转换为type   '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>();
}

编辑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"
                }
            }
        ]
    }

没有值的完整json:

{
    "canteen": [
        {
            "name": "Canteen1",
            "src": "a link",
            "food": [],
        },
        {
            "name": "Canteen2",
            "src": "a link",
            "food": [],
        },
        {
            "name": "Canteen3",
            "src": "a link",
            "food": [],
       }
    ]
}

编辑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() { }
}

方法调用:

Canteen[] canteens = DeserializeAPIResults<Canteen[]>(json);

1 个答案:

答案 0 :(得分:1)

如果特定键的值不固定且数据必须可配置,则Newtonsoft.json具有一项要在此处使用的功能,即[JsonExtensionData]Read more

  

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

当您的第三方json具有名称为food且其值为对象的密钥时,您正尝试反序列化为Dictionary<string, string> Food {get; set;},并且反序列化方法正确地反序列化了json。

但是,当food键具有数组时,您的方法将无法反序列化,因为您正尝试将数组[]反序列化为string

如果您使用

[JsonExtensionData]
public Dictionary<string, JToken> Food { get; set; }

代替

Dictionary<string, string> Food {get; set;}

然后您的反序列化将起作用。

所以最终您的课程将是

public sealed class Canteen
{
    [JsonProperty("name")]
    public string Name { get; set; }
    [JsonProperty("src")]
    public string Src { get; set; }

    [JsonExtensionData]
    public Dictionary<string, JToken> Food { get; set; }
    public Canteen() { }
}

替代:

如果您在Food类中将JToken属性数据类型声明为Canteen,例如

public sealed class Canteen
{
    [JsonProperty("name")]
    public string Name { get; set; }
    [JsonProperty("src")]
    public string Src { get; set; }

    [JsonProperty("food")]
    public JToken Food { get; set; }

    public Canteen() { }
}

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

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

JToken的优点是您可以检查其类型是对象还是数组

Canteen[] canteens = DeserializeAPIResults<Canteen[]>(json);

foreach (var canteen in canteens)
{
    string name = canteen.Name;
    string src = canteen.Src;
    JToken food = canteen.Food;

    if (food.Type == JTokenType.Object)
    {
        Dictionary<string, string> foods = food.ToObject<Dictionary<string, string>>();
    }
    else if (food.Type == JTokenType.Array)
    {
        //Do something if "foods" is empty array "[]"
    }
}