【问题标题】:Deserializing JSON string fails when JSON dictionary is empty当 JSON 字典为空时,反序列化 JSON 字符串失败
【发布时间】:2018-11-24 13:01:27
【问题描述】:

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

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

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

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

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

在这种情况下,json 的去理想化工作正常。 食物空了时会出现问题:

{
      "food": [],
}

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

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>();
}

编辑 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);

【问题讨论】:

  • 表示您的 food 键包含 sometime 对象和 sometime 数组,对吗?
  • 好像。当食物不空时,它总是一本字典。只有当它为空时,它才会被视为一个数组。
  • 给我看看你的反序列化代码?
  • 我已经添加了反序列化代码
  • @er-shoaib - 他说当食物为空时它会像空数组一样返回,但当它不为空时它的字典。

标签: c# json.net


【解决方案1】:

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

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

当您的第 3 方 json 有一个名为 food 的键并将其值作为对象时,您正在尝试反序列化为 Dictionary&lt;string, string&gt; 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() { }
}

替代方案:

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

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。

然后您可以从 canteens 数组访问您的每个 canteen 并检索每个 canteen 的 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 "[]"
    }
}

【讨论】:

  • 非常感谢!我刚刚尝试了您的解决方案,它对我来说效果很好。我以前没有见过 [JsonExtensionData] 功能。我唯一问自己的是为什么第三者会这样做。如果没有数据,他们为什么不能只返回一个空白字典而不是一个数组?
  • 很高兴听到,我也有同样的问题。不管怎样,欢迎你的:)
  • 我还添加了替代[JsonExtensionData]。请查看答案中的替代部分:)
  • 感谢您对这个问题的第二个解决方案。我想我会使用它,因为它似乎对我的方法更有用。不管怎样,第一个版本也不错:)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-08-15
  • 1970-01-01
  • 2014-05-24
相关资源
最近更新 更多