问题
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