Deserializing JSON string fails when JSON dictionary is empty

一个人想着一个人 提交于 2020-05-28 04:57:26

问题


Currently I'm facing the following issue when I try to deserialize a json string that I receive from a 3rd party (--> I cannot change the received json string by myself) with Newtonsoft.Json:

The json contains a dictionary (and some other entries that I don't list here):

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

I've created a class that contains the property (plus the properties that I didn't list here):

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

In this case the desiralization of the json works fine. The problem occurs when food is empty:

{
      "food": [],
}

In this case it seems that food is not a dictionary but an array. That's the reason why the desirialization fails with the following error:

Newtonsoft.Json.JsonSerializationException: "Cannot deserialize the current JSON array (e.g. [1,2,3]) into type 'System.Collections.Generic.Dictionary`2[System.String,System.String]' because the type requires a JSON object (e.g. {"name":"value"}) to deserialize correctly. To fix this error either change the JSON to a JSON object (e.g. {"name":"value"}) or change the deserialized type to an array or a type that implements a collection interface (e.g. ICollection, IList) like List that can be deserialized from a JSON array. JsonArrayAttribute can also be added to the type to force it to deserialize from a JSON array. Path 'food'."

Is anybody out there who can help me to solve this problem, please?

EDIT The desiralization code:

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

EDIT 2 Full json with values:

        {
        "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"
                }
            }
        ]
    }

Full json without values:

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

EDIT 3 The class:

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

And the method call:

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

回答1:


If your value for particular key are not fixed and data must be configurable then Newtonsoft.json has one feature that to be use here and that is [JsonExtensionData]. Read more

Extension data is now written when an object is serialized. Reading and writing extension data makes it possible to automatically round-trip all JSON without adding every property to the .NET type you’re deserializing to. Only declare the properties you’re interested in and let extension data do the rest.

When your 3rd party json have an key with name food and its value as object then you are trying to deserialze into Dictionary<string, string> Food {get; set;} and your deserilization method correctly deserialize your json.

But when food key have array then your method fails to deserialize because you are trying to deserialize array [] into string.

If you use

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

Instead of

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

Then your deserialization works.

So finally your class will be

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

Alternative:

If you declare your Food property data type to JToken in your Canteen class like

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

Then you can successfully deserialize your json whether your food key is either object or array.

And then you can access your each of canteen from canteens array and retrieve each canteen's name, src and food key/value pair like.

The advantage of JToken is that you can check it's type whether its object or array

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 "[]"
    }
}


来源:https://stackoverflow.com/questions/53458399/deserializing-json-string-fails-when-json-dictionary-is-empty

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!