You are close. If you define your Toy
class as you have it in your question, you can deserialize into a List<Dictionary<string, Toy>>
. Thus, each toy is actually represented by a Dictionary
with a single entry in it. The Key
is the name of the toy and the Value
is the Toy
info.
Here is a demo:
string json = @"
[ { ""kite"" : { ""supplier"" : ""ABC"",
""currency"" : ""GBP"",
""cost"" : ""7.98""
} },
{ ""puzzle"" : { ""supplier"" : ""DEF"",
""currency"" : ""USD"",
""cost"" : ""7.98""
} },
{ ""ball"" : { ""supplier"" : ""DEF"",
""currency"" : ""USD"",
""cost"" : ""5.49""
} }
]";
List<Dictionary<string, Toy>> list =
JsonConvert.DeserializeObject<List<Dictionary<string, Toy>>>(json);
foreach (Dictionary<string, Toy> dict in list)
{
KeyValuePair<string, Toy> kvp = dict.First();
Console.WriteLine("toy: " + kvp.Key);
Console.WriteLine("supplier: " + kvp.Value.Supplier);
Console.WriteLine("cost: " + kvp.Value.Cost + " (" + kvp.Value.Currency + ")");
Console.WriteLine();
}
This outputs the following:
toy: kite
supplier: ABC
cost: 7.98 (GBP)
toy: puzzle
supplier: DEF
cost: 7.98 (USD)
toy: ball
supplier: DEF
cost: 5.49 (USD)
Admittedly, this solution is kind of "clunky", because it would be better to have the name of the toy included in the Toy
class itself rather than have an intervening Dictionary
to stumble over. There are two ways to fix this. One way is to add a Name
property on the Toy
class, deserialize into the same structure as shown above, then do a little bit of post-processing to move the names from each Dictionary
into the respective Toy
, building a new List<Toy>
in the process. The second way to do it is to create a custom JsonConverter
to handle this translation during deserialization. I'd be happy to demonstrate either of these alternate approaches if you wish. Just let me know. If you just need quick-and-dirty, then the above approach should do.
Alternative approach using a custom JsonConverter
This approach is a little "cleaner" because we can keep all the Toy
info together on a single strongly-typed object, and keep all the deserialization logic separate so it doesn't clutter up the main code.
First, we need to alter your Toy
class to give it a Name
property.
class Toy
{
public string Name { get; set; }
public string Supplier { get; set; }
public string Currency { get; set; }
public decimal Cost { get; set; }
}
Next, we create a class which inherits from JsonConverter
.
class ToyConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
// This lets JSON.Net know that this converter can handle Toy objects
return (objectType == typeof(Toy));
}
public override object ReadJson(JsonReader reader,
Type objectType, object existingValue, JsonSerializer serializer)
{
// load the toy JSON object into a JObject
JObject jo = JObject.Load(reader);
// get the first (and only) property of the object
JProperty prop = jo.Properties().First();
// deserialize the value of that property (which is another
// object containing supplier and cost info) into a Toy instance
Toy toy = prop.Value.ToObject<Toy>();
// get the name of the property and add it to the newly minted toy
toy.Name = prop.Name;
return toy;
}
public override void WriteJson(JsonWriter writer,
object value, JsonSerializer serializer)
{
// If you need to serialize Toys back into JSON, then you'll need
// to implement this method. We can skip it for now.
throw new NotImplementedException();
}
}
To use the converter, we just need to create an instance of it and pass it in the call to DeserializeObject<T>()
. Now that we have this converter, we can deserialize directly into a List<Toy>
, which is much more natural.
List<Toy> toys = JsonConvert.DeserializeObject<List<Toy>>(json, new ToyConverter());
Accessing the toy data from there is straightforward.
foreach (Toy toy in toys)
{
Console.WriteLine("toy: " + toy.Name);
Console.WriteLine("supplier: " + toy.Supplier);
Console.WriteLine("cost: " + toy.Cost + " (" + toy.Currency + ")");
Console.WriteLine();
}
You'll notice this gives exactly the same output as the previous example, but the code is much cleaner.