Reputation: 41
I need to deserialize the following json:
{
"1": {
"oid": "46",
"order": "SD9999999999999",
"date": "2015-08-18 14:17:05",
"item": {
"0": {
"guid": "DEF456"
"price": "100.00"
},
"1": {
"guid": "ABC123",
"price": "99.99"
}
}
},
"2": {
"oid": "765",
"order": "SD0000000000000",
"date": "2015-08-18 14:17:05",
"item": {
"0": {
"guid": "GHI789"
"price": "10.00"
},
"1": {
"guid": "XYZ123",
"price": "9.99"
}
}
},
"store": 111,
"time": "2015-09-01 17:51:22"
}
The number of orders is unknown as well as the number of items in each order.
I have tried making a list of orders and dynamically looping through and JsonConvert.DeserializeObject() thhen adding each to the list, but this doesn't account for the items being dynamic and items end up equalling null. Any thoughts?
Upvotes: 1
Views: 8684
Reputation: 28583
This works using JObject/JToken (after fixing the missing ,
in your json).
var json = System.IO.File.ReadAllText("test.json");
var jobj = Newtonsoft.Json.Linq.JObject.Parse(json);
//exclude "store" and "time"
var orders = jobj.Children().Where(x => x.Path != "store" && x.Path != "time").ToList();
//iterate over orders
foreach (var order in orders)
{
//"name" of order
var orderName = order.Path;
//and contents
var orderItems = order.First()["item"].ToList();
foreach (var item in orderItems)
{
var itemName = item.Path;
var itemContents = item.First();
var guid = (String)itemContents["guid"];
var price = Double.Parse((String)itemContents["price"]);
}
}
And for completeness, also code deserializing to a Dictionary<String, dynamic>
var dynObj = Newtonsoft.Json.JsonConvert.DeserializeObject<Dictionary<String, dynamic>>(json);
var dynOrders = dynObj.Where(x => x.Key != "store" && x.Key != "time").ToList();
foreach (dynamic order in dynOrders)
{
var orderName = order.Key;
var orderItems = order.Value.item;
foreach (var item in orderItems)
{
var itemName = item.Name;
var guid = (String)item.Value.guid;
var price = Double.Parse((String)item.Value.price);
}
}
I will say that for both of these, I had to set breakpoints and examine the objects and see what I had to work with and what would work to get that data. "Dictionary" style objects are more awkward in C# compared to arrays. I did find another way to do this via this answer. While the most verbose, also the most consistent across object levels.
//"force" to use IDictionary interface
IDictionary<string, JToken> dict = Newtonsoft.Json.Linq.JObject.Parse(json);
var dictOrders = dict.Where(x => x.Key != "store" && x.Key != "time").ToList();
foreach (var order in dictOrders)
{
var orderName = order.Key;
var orderProps = (IDictionary<string, JToken>)order.Value;
var oid = Int32.Parse((String)orderProps["oid"]);
var orderX = (String)orderProps["order"];
var date = DateTime.Parse((String)orderProps["date"]);
var orderItems = (IDictionary<string, JToken>)orderProps["item"];
foreach (var item in orderItems)
{
var itemName = item.Key;
var itemContents = (IDictionary<string, JToken>)item.Value;
var guid = (String)itemContents["guid"];
var price = Double.Parse((String)itemContents["price"]);
}
}
By using this pattern, you should be able to take any JToken
, cast it to a IDictionary<string, JToken>
, then deal with its contents in a consistent manner using Key
for the property name and Value
for the contents (which you would also cast to IDictionary
so you can descent the object as far as you need).
Upvotes: 2