问题
I've got some json that looks like this:
[
{
"MenuItem1": [
{ "SubItem1": [ ] },
{ "SubItem2": [ ] },
{ "SubItem3": [
{ "SubSubItem": [ ] }
]
}
]
},
{ "MenuItem2": [ ] }
]
This can be represented with the following C# data structure:
class MenuItem
{
Dictionary<string, MenuItem[]> Items;
}
I tried deserializing this as
MenuItem[] roots = JsonConvert.DeserializeObject<MenuItem[]>(json);
but it doesn't work because it doesn't know that the Items
member is where this dictionary data is supposed to go on the recursive call. How can I make this work?
回答1:
Your basic problem is that your JSON does not represent a dictionary of lists of MenuItem
classes. Instead, it represents a list of dictionaries of MenuItem
classes - the reverse of the structure in your data model.
You have several ways you could represent and deserialize this:
Define your
MenuItem
as a subclass of a list of dictionaries ofMenuItem
types:public class MenuItem : List<Dictionary<string, MenuItem>> { }
Json.NET will be able to deserialize this as-is out of the box:
var root = JsonConvert.DeserializeObject<MenuItem>(json);
Define your
MenuItem
as containing a list of dictionaries ofMenuItem
types:[JsonConverter(typeof(MenuItemConverter))] class MenuItem { public Dictionary<string, MenuItem> [] Items; }
You will need a custom converter to bubble the
Items
up one level in the JSON:public class MenuItemConverter : JsonConverter { public override bool CanConvert(Type objectType) { return objectType == typeof(MenuItem); } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { var items = serializer.Deserialize<Dictionary<string, MenuItem>[]>(reader); return new MenuItem { Items = items }; } public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { var item = (MenuItem)value; serializer.Serialize(writer, item.Items); } }
And to deserialize, once again:
var root = JsonConvert.DeserializeObject<MenuItem>(json);
If you really want your data model to be a dictionary of lists rather than a list of dictionaries, you will need to restructure your data model as you read and write it:
[JsonConverter(typeof(MenuItemConverter))] class MenuItem { public MenuItem() { this.Items = new Dictionary<string, List<MenuItem>>(); } public Dictionary<string, List<MenuItem>> Items; } public static class DictionaryExtensions { public static void Add<TKey, TValueList, TValue>(this IDictionary<TKey, TValueList> listDictionary, TKey key, TValue value) where TValueList : IList<TValue>, new() { if (listDictionary == null) throw new ArgumentNullException(); TValueList values; if (!listDictionary.TryGetValue(key, out values)) listDictionary[key] = values = new TValueList(); values.Add(value); } public static Dictionary<TKey, TValue> ToDictionary<TKey, TValue>(TKey key, TValue value) { var dict = new Dictionary<TKey, TValue>(); dict[key] = value; return dict; } } public class MenuItemConverter : JsonConverter { public override bool CanConvert(Type objectType) { return objectType == typeof(MenuItem); } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { var items = serializer.Deserialize<Dictionary<string, MenuItem>[]>(reader); var menuItem = new MenuItem(); foreach (var pair in items.SelectMany(d => d)) menuItem.Items.Add(pair.Key, pair.Value); return menuItem; } public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { var menuItem = (MenuItem)value; if (menuItem.Items == null) writer.WriteNull(); else { var list = menuItem.Items.SelectMany(p => p.Value.Select(m => DictionaryExtensions.ToDictionary(p.Key, m))); serializer.Serialize(writer, list); } } }
And then
var root = JsonConvert.DeserializeObject<MenuItem>(json);
Prototype fiddle showing all three.
来源:https://stackoverflow.com/questions/34960853/deserializing-json-with-recursive-types