I see many questions and answers about what I'm trying to do but after reading the answers I'm not able to get the key and value out of this json.
Here is the json being returned:
{
"#odata.context": "https://con813-crm612cf723bbf35af6devaos.cloudax.dynamics.com/data/$metadata#Customers(CustomerAccount,Name)",
"value": [
{
"#odata.etag": "W/\"JzAsMjI1NjU0MjE1NTg7MCwwOzAsNTYzNzE0NTMyODswLDU2MzcxNDQ1NzY7MCwyMjU2NTQyNTY5MzswLDIyNTY1NDI3MjM2OzAsMDswLDIyNTY1NDI3MjM2OzAsMjI1NjU0MjcyMzY7MCwwJw==\"",
"CustomerAccount": "DE-001",
"Name": "Contoso Europe"
},
{
"#odata.etag": "W/\"JzAsMjI1NjU0MjE1NTk7MCwwOzAsMzU2MzcxNDkxMTI7MCw1NjM3MTQ0NTc3OzAsMjI1NjU0MjU2OTQ7MCwyMjU2NTQyNzIzODswLDA7MCwyMjU2NTQyNzIzODswLDIyNTY1NDI3MjM4OzAsMCc=\"",
"CustomerAccount": "US-001",
"Name": "Contoso Retail San Diego"
},
{
"#odata.etag": "W/\"JzAsMjI1NjU0MjE1NjA7MCwwOzAsMzU2MzcxNDkxMTM7MCw1NjM3MTQ0NTc4OzAsMjI1NjU0MjU2OTU7MCwyMjU2NTQyNzI0MDswLDA7MCwyMjU2NTQyNzI0MDswLDIyNTY1NDI3MjQwOzAsMCc=\"",
"CustomerAccount": "US-002",
"Name": "Contoso Retail Los Angeles"
}
]
}
I need to get the names of the keys, which is "CustomerAccount" and "Name" in this example, and then their values. I can't figure out to just return those values.
JObject parsedJson = JObject.Parse(_json);
StringBuilder builder = new StringBuilder();
foreach (JProperty property in parsedJson.Properties())
{
builder.Append((string.Format("Name: [{0}], Value: [{1}].", property.Name, property.Value)));
}
Hoping to add more clarity; In this example I would like to write out the key/values after "#odata.etag" which the keys are "CustomerAccount" and "Name" and their values are after the colon. The keys/values are dynamic so I need to loop through writing out whatever the key names and values are after each "#odata.etag" value.
Use JsonConvert.DeserializeObject and pass the type of object you want to parse. there was a missing closing curly brace at the end. I hope I understood what you want to do correctly, if not plz leave a comment
class Value
{
public string CustomerAccount { get; set; }
public string Name { get; set; }
}
class Customer
{
public List<Value> Value { get; set; }
}
class Program
{
static void Main(string[] args)
{
var obj = JsonConvert.DeserializeObject<Customer>(#" {
'#odata.context': 'https://con813-crm612cf723bbf35af6devaos.cloudax.dynamics.com/data/$metadata#Customers(CustomerAccount,Name)',
'value': [
{
'#odata.etag': 'W/\'JzAsMjI1NjU0MjE1NTg7MCwwOzAsNTYzNzE0NTMyODswLDU2MzcxNDQ1NzY7MCwyMjU2NTQyNTY5MzswLDIyNTY1NDI3MjM2OzAsMDswLDIyNTY1NDI3MjM2OzAsMjI1NjU0MjcyMzY7MCwwJw==\'',
'CustomerAccount': 'DE-001',
'Name': 'Contoso Europe'
},
{
'#odata.etag': 'W/\'JzAsMjI1NjU0MjE1NTk7MCwwOzAsMzU2MzcxNDkxMTI7MCw1NjM3MTQ0NTc3OzAsMjI1NjU0MjU2OTQ7MCwyMjU2NTQyNzIzODswLDA7MCwyMjU2NTQyNzIzODswLDIyNTY1NDI3MjM4OzAsMCc=\'',
'CustomerAccount': 'US-001',
'Name': 'Contoso Retail San Diego'
},
{
'#odata.etag': 'W/\'JzAsMjI1NjU0MjE1NjA7MCwwOzAsMzU2MzcxNDkxMTM7MCw1NjM3MTQ0NTc4OzAsMjI1NjU0MjU2OTU7MCwyMjU2NTQyNzI0MDswLDA7MCwyMjU2NTQyNzI0MDswLDIyNTY1NDI3MjQwOzAsMCc=\'',
'CustomerAccount': 'US-002',
'Name': 'Contoso Retail Los Angeles'
}
]
}");
foreach (var value in obj.Value)
{
Console.WriteLine($"Name: 'Name' Value: {value.Name}");
Console.WriteLine($"Name: 'CustomerAccount' Value: {value.CustomerAccount}");
}
}
}
If you don't know the keys (properties are dynamic) of the object, you could use the following code snippet but it needs a change in the class declaration.
class Customer
{
//this is list of value objects (value is a dictionary)
public List<Dictionary<String,String>> Value { get; set; }
}
And here's how to deserialize and loop through the array of values
var obj = JsonConvert.DeserializeObject<Customer>(myString);
foreach (var value in obj.Value)
{
foreach (var key in value)
{
if (key.Key == "#odata.etag")
continue;
Console.WriteLine("Name: [{0}], Value: [{1}]",key.Key, key.Value);
}
}
Related
I want to be able to print the type, name and the sub entries, but I'm at a a loss as to how. Can I access specific positions of an array instead of naming the property I want?
The main property has entry properties, like mainProperty.entries, and I can pull the strings out using a foreach. But when the "entries" has properties that don't have a name, I have no idea how to access them.
I know that the "Entry text here" are JValues, and that the others are JObjects.
The entry without objects can be accessed through mainProperty.entries.
Since the other type doesn't have a name, I don't know how to access the type, name and "sub" entries. (mainProperty.JObject.type)
{
"mainProperty": [
{
"entries": [
"Entry text here",
{
"type": "entries",
"name": "Entry of entry",
"entries": [
"Entry text here"
]
},
{
"type": "entries",
"name": "Entry of entry",
"entries": [
"Entry text here"
]
}
]
}
]
}
{
"mainProperty": [
{
"entries": [
"Entry text here",
"Second line of text"
]
}
]
}
foreach (var entry in mainProperty.entries)
{
debugOutput(entry.GetType().ToString());
//debugOutput("\t" + (string)entry);
}
The output should be:
First example:
Entry text here
**Entry of entry** Entry text here
**Entry of entry** Entry text here
Second example:
Entry text here
Second line of text
In your JSON, entries is an array of mixed types, where the values can either be strings or objects. If they are objects, each child object has an entries property whose value is also an array. In your examples it seems that this inner array will always contain strings, but it looks like it could actually be a fully recursive structure, where sub-entries can contain sub-sub-entries and so on. If that is so, you would need a recursive method to dump out the entries. Something like this could work:
public static void DumpEntries(JObject obj, string indent = "")
{
JArray entries = (JArray)obj["entries"];
if (entries != null)
{
foreach (JToken entry in entries)
{
if (entry.Type == JTokenType.String)
{
debugOutput(indent + entry.ToString());
}
else if (entry.Type == JTokenType.Object && (string)entry["type"] == "entries")
{
debugOutput(indent + "**" + (string)entry["name"] + "**");
DumpEntries((JObject)entry, indent + " ");
}
}
}
}
Then use it with your JSON like this:
var root = JObject.Parse(json);
foreach (JObject obj in (JArray)root["mainProperty"])
{
DumpEntries(obj);
}
Fiddle: https://dotnetfiddle.net/MjRNGn
In answer to your original question,
Can I access specific positions of an array instead of naming the property I want?
Yes, you absolutely can do this.
For example, if you wanted to get the first sub-entry of the third entry, you could do this:
var root = JObject.Parse(json);
var sub = root["mainProperty"][0]["entries"][2]["entries"][0];
debugOutput((string)sub);
Fiddle: https://dotnetfiddle.net/sMyMxH
If you modify your JSON data like this:
var response = {
"mainProperty": [{
"entries": [{
"type": "entries",
"name": "Entry of entry",
"entries": [
"Entry text here"
]
},
{
"type": "entries",
"name": "Entry of entry",
"entries": [
"Entry text here"
]
}
]
}]
}
You must create Response object but first, you need add NewtonSoft.Json to your project from NuGet. After add this library to your project you need to create Response object like this:
public class Entry
{
public string type { get; set; }
public string name { get; set; }
public List<string> entries { get; set; }
}
public class MainProperty
{
public List<Entry> entries { get; set; }
}
public class RootObject
{
public List<MainProperty> mainProperty { get; set; }
}
Than you can access your data:
var _res = JsonConvert.DeserializeObject<RootObject>(response.ToString());
foreach(var item in _res.mainProperty.entries.entries)
{
debugOutput(item);
}
Here are two json samples.
I want to combine this json into a file.
If a key exists in a value that is combined without thinking, it is difficult to replace only the value that is high in value.
First Sample.
{
"employees": [
{
"firstName": "Tom",
"HighValue": "3"
},
{
"firstName": "Maria",
"HighValue": "4"
},
{
"firstName": "Robert",
"HighValue": "45"
}
]
}
Second Sample.
{
"employees": [
{
"firstName": "Tom",
"HighValue": "6"
},
{
"firstName": "Maria",
"HighValue": "4"
},
{
"firstName": "Robert",
"HighValue": "45"
},
{
"firstName": "John",
"HighValue": "1"
}
]
}
I want Result:
{
"employees": [
{
"firstName": "Tom",
"HighValue": "6"
},
{
"firstName": "Maria",
"HighValue": "4"
},
{
"firstName": "Robert",
"HighValue": "45"
},
{
"firstName": "John",
"HighValue": "1"
}
]
}
The goal is to combine two samples, Json, into one result json. What's the best way?
One simple approach would be by making use of a json framework like Json.NET which handles serialization/deserialization for you.
First create a data model into which your json data can be deserialized to. I used the online tool json2csharp for this. This will give you the following model:
public class Employee
{
public string firstName { get; set; }
public int HighValue { get; set; }
}
public class RootObject
{
public List<Employee> employees { get; set; }
}
Now you can simply deserialize your json strings into objects like this:
string json1 = "";
string json2 = "";
var obj1 = JsonConvert.DeserializeObject<RootObject>(json1);
var obj2 = JsonConvert.DeserializeObject<RootObject>(json2);
After this step you just have to iterate over your employees, check if they exist in both lists and add/update them accordingly:
foreach(var emp in obj2.employees)
{
Employee existing = null;
try
{
existing = obj1.employees.SingleOrDefault(e => e.firstName == emp.firstName);
}
catch(Exception ex)
{
// The same employee exists multiple times in the first list
}
if(existing != null)
{
// obj1 already contains an employee with the given name, check which value is bigger
if(existing.HighValue < emp.HighValue)
{
// The value of the existing employee is smaller
// -> Update the value with the value from the second object
existing.HighValue = emp.HighValue;
}
}
else
{
// obj1 does not already contain an employee with the given name, add the whole employee
obj1.employees.Add(emp);
}
}
Now obj1 contains the combined list of employees.
To serialize the combined list back to json do the following:
var json = JsonConvert.SerializeObject(obj1);
(For the record JSON isn't one of my strongest points but I thought answering this for you would be a good challenge)
The following code takes both JSON samples as JObjects using Newtonsoft.Json, then merges the two samples together. I then use LINQ to group and select the people with the highest values and output a new array.
The advantage to this solution is if you only want to use C# to merge, not use it for any other logic, you don't need to deserialize to an object.
Give it a try:
var firstSample = JObject.Parse("Your first sample goes here");
var secondSample = JObject.Parse("Your second sample goes here");
var newJsonArray = new JArray(); //Makes new array for output
firstSample.Merge(secondSample); //Merges the firstSample JObject with secondSample JObject
var filteredJson = firstSample["employees"] //Makes new JObject with filtered values
.GroupBy(x => x["firstName"]) // Groups by the first name
.Select(x => x.Where(y => y["HighValue"] == x.Max(z => z["HighValue"]))) // Select the employee in each group with the highest value
.ToList(); // Converts to a list out
foreach (var token in filteredJson)
{
newJsonArray.Add(token); // For each selected employee, add it to the new array
}
var outputJson = new JObject(new JProperty("employees", newJsonArray)); // Composes the new JSon string
I get a Json data from url as follows. But as you see, there are no key names in Json.
For example, "Flame Towers" is place name value but there is no any key name. Likewise, "2017-02-10" is date value, "The Lego Batman Movie 2D" is film name value but it declared as key and ["10:10"] is an array consists film session times.
I tried many class structurs for deserialize it to C# classes using JsonConvert.DeserializeObject<ClassName>(jsonString);
But every time it returns a null object. Also tried parse manually with JObject class and it seemed to me very confused.
So, can anyone help for true class structure parsing with JsonConvert class?
{
{
"Flame Towers": {
"2017-02-10": {
"The Lego Batman Movie 2D": [
"10:10"
],
"Qatil 2D": [
"10:30"
],
"Fifty Shades Darker 2D": [
"10:30",
"11:40",
"12:50",
"14:00",
"15:10",
"16:20",
"17:30",
"18:40",
"19:50",
"21:00",
"22:10",
"23:20",
"00:30",
"01:40"
],
"John Wick: Chapter Two 2D": [
"11:00",
"12:10",
"13:20",
"14:30",
"15:40",
"16:50",
"18:00",
"20:20",
"21:30",
"22:40",
"23:50",
"01:00",
"02:10"
],
"The Lego Batman Movie 3D": [
"11:00",
"12:10",
"13:00",
"14:10",
"15:00",
"17:00",
"19:00"
],
"Ballerina 3D": [
"16:10"
],
"Rings 2D": [
"17:55"
],
"Ağanatiq 2D": [
"19:55"
],
"Resident Evil: The Final Chapter 3D": [
"21:40",
"21:00",
"23:50",
"01:10"
],
"The Great Wall 3D": [
"23:10"
]
}
},
"Metro Park": {
"2017-02-10": {
"John Wick: Chapter Two 2D": [
"10:30",
"12:50",
"15:10",
"17:30",
"19:50",
"22:10",
"00:30"
],
"Ağanatiq 2D": [
"10:00",
"11:50",
"13:40",
"15:30",
"17:20",
"19:10",
"21:00",
"23:00",
"00:50"
],
"The Lego Batman Movie 2D": [
"10:30"
],
"Fifty Shades Darker 2D": [
"11:00",
"13:20",
"15:40",
"18:00",
"20:20",
"02:00"
],
"Hoqqa 2D": [
"11:10",
"12:50",
"14:30",
"16:10",
"17:50",
"19:30",
"21:10",
"22:50",
"00:30",
"02:10"
],
"Naxox 2D": [
"11:20",
"13:10",
"15:00",
"16:50",
"18:40",
"20:30",
"22:20",
"00:10"
],
"The Lego Batman Movie 3D": [
"12:30",
"14:30",
"16:30",
"18:30"
],
"Ballerina 3D": [
"20:30"
],
"Resident Evil: The Final Chapter 3D": [
"22:40",
"00:50"
],
"The Great Wall 3D": [
"22:20",
"02:30"
],
"Притяжение 3D": [
"00:20"
]
}
}
}
}
There is a simple, hacky and speedy way of doing this. Just cut the first and the last { } symbols from the string before serializing.
if (jsonString.StartsWith("{{") && jsonString.EndsWith("}}"))
jsonString = jsonString.Substring(2, jsonString.Length - 4);
JsonConvert.DeserializeObject<ClassName>(jsonString);
It looks like data from a collection of movie theatres and their active shows where the top item "Flame Towers" are the name of the cinema, the "2017-02-10" is the date and under is each show/movie and then their "display" time.
Knowing this, you can create a data structure that matches this.
... Something like this perhaps?
public class Movie : IEnumerable<TimeSpan>
{
public Movie(string name, IReadOnlyList<TimeSpan> runTimes)
{
this.Name = name;
this.RunTimes = runTimes;
}
public string Name { get; }
public IReadOnlyList<TimeSpan> RunTimes { get; }
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public IEnumerator<TimeSpan> GetEnumerator()
{
return RunTimes.GetEnumerator();
}
public override string ToString()
{
return "[Movie] " + Name;
}
public static Movie Parse(JProperty data)
{
var name = data.Name;
var runTimes = new List<TimeSpan>();
foreach (var child in data.Values())
{
runTimes.Add(TimeSpan.Parse(child.Value<string>()));
}
return new Movie(name, runTimes);
}
}
public class MovieCollectionDate : IEnumerable<Movie>
{
public MovieCollectionDate(DateTime date, IReadOnlyList<Movie> movies)
{
this.Date = date;
this.Movies = movies;
}
public DateTime Date { get; }
public IReadOnlyList<Movie> Movies { get; }
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public IEnumerator<Movie> GetEnumerator()
{
return this.Movies.GetEnumerator();
}
public override string ToString()
{
return "[Date] " + Date + " - " + Movies.Count + " show(s)";
}
public static MovieCollectionDate Parse(JProperty data)
{
var date = DateTime.Parse(data.Name);
var movies = new List<Movie>();
foreach (var upperChild in data.Children<JObject>())
{
foreach (var child in upperChild.Children())
{
movies.Add(Movie.Parse(child as JProperty));
}
}
return new MovieCollectionDate(date, movies);
}
}
public class MovieTheatre : IEnumerable<MovieCollectionDate>
{
public MovieTheatre(string name, IReadOnlyList<MovieCollectionDate> dateAndMovies)
{
this.Name = name;
this.DateAndMovies = dateAndMovies;
}
public string Name { get; }
public IReadOnlyList<MovieCollectionDate> DateAndMovies { get; }
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public IEnumerator<MovieCollectionDate> GetEnumerator()
{
return this.DateAndMovies.GetEnumerator();
}
public override string ToString()
{
return "[Theatre] " + Name + " - " + DateAndMovies.Count + " open day(s)";
}
public static MovieTheatre Parse(JProperty data)
{
var name = data.Name;
var movieCollectionDates = new List<MovieCollectionDate>();
foreach (var upperChild in data.Children<JObject>())
{
foreach (var child in upperChild.Children())
{
movieCollectionDates.Add(MovieCollectionDate.Parse(child as JProperty));
}
}
return new MovieTheatre(name, movieCollectionDates);
}
}
public class MovieTheatreCollection : IEnumerable<MovieTheatre>
{
public MovieTheatreCollection(IReadOnlyList<MovieTheatre> movieTheatres)
{
this.MovieTheatres = movieTheatres;
}
public IReadOnlyList<MovieTheatre> MovieTheatres { get; }
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public IEnumerator<MovieTheatre> GetEnumerator()
{
return this.MovieTheatres.GetEnumerator();
}
public override string ToString()
{
return "MovieTheatreCollection: Containing " + MovieTheatres.Count + " movie theatre(s)";
}
public static MovieTheatreCollection Parse(JObject data)
{
var theatres = new List<MovieTheatre>();
foreach (var child in data.Children().Cast<JProperty>())
{
theatres.Add(MovieTheatre.Parse(child));
}
return new MovieTheatreCollection(theatres);
}
}
this is obviously not the most elegant way of solving the problem. But seeing as this "key-less" json wouldnt just Deserialize properly without some sort of hack. Creating a data structure that matches your needs (more manual work unfortunately) will at least work ;)
You can use the code above with the following code:
JObject obj = JObject.Parse(... the json string you had above ...)
MovieTheatreCollection movieTheatres = MovieTheatreCollection.Parse(obj);
foreach (var movieTheatre in movieTheatres)
{
Console.WriteLine(movieTheatre);
foreach (var openDay in movieTheatre)
{
Console.WriteLine(" " + openDay);
foreach (var movie in openDay)
{
Console.WriteLine(" " + movie);
foreach (var runtime in movie) Console.WriteLine(" - " + runtime);
}
}
}
Finally, just like 'Just Shadow' mentioned in the answer above, the json is malformed and contains extra curly brackets that needs to be removed or the object wont parse properly.
An ugly, but reasonably compact way to parse this would be:
static void Main(string[] args)
{
var jo = JObject.Parse(File.ReadAllText("data.json").Trim('{').Trim('}'));
foreach (var place in jo)
{
Console.WriteLine($"Place: {place.Key}");
foreach (var dateOrMovie in place.Value.Children<JProperty>())
{
Console.WriteLine($"\tDate: {dateOrMovie.Name}");
var movies = dateOrMovie.Children<JObject>().First().Children<JProperty>();
foreach (var movie in movies)
{
Console.WriteLine($"\t\t{movie.Name}");
foreach (JValue time in movie.Children<JArray>().First())
{
Console.WriteLine($"\t\t\t{time.Value}");
}
}
}
}
}
I am trying to GET JSON From a webservice , which i was able to figure out .
BTW This was my JSON Data -
{
"X-YZ-111/AB.CD": {
"P1": "F",
"P2": "43.46"
},
"X-YZ-112/AB.CD": {
"P1": "F",
"P2": "8.02"
},
"X-YZ-113/AB.CD": {
"P1": "F",
"P2": "9066.58"
},
"X-YZ-114/AB.CD": {
"P1": "F",
"P2": "6.00"
},
"X-YZ-115/AB.CD": {
"P1": "F",
"P2": "6.00"
},
"X-YZ-116/AB.CD": {
"P1": "F",
"P2": "10.00"
}}
Using Windows.Data.Json;
private async void getJSON_click(object sender,RoutedEventArgs e)
{
var client=new HttpClient();
client.MaxResponseBufferSize=1024*1024;
var response= await Client.GetAsync(new Uri(The URL here));
var result = await response.Content.ReadAsStringAsync();
var component=JsonObject.Parse(result);
}
Now i am able trying to parse it into a collection,where each item has "X-YZ-111/AB.CD" as name property and P1 and P2 as other 2 properties ,which i will try to bind to a Tile in UI using CollectionViewSource later.
Now This is the C# classes Json2CShardpDotCom is generating
public class Name
{
public string Type { get; set; }
public string Val { get; set; }
}
public class Root
{
public Name Name { get; set; }
}
Any suggestions on how to loop through these values using foreach and what classes i should make in C#?
Edit 1: BTW i know there has to be an dictionary where i first do a foreach on outer loop to retrieve the Name and foreach on inner loop to retrieve the P1 and P2.
But i am confused regarding what classes i should use in C# to consume JSON and create collection item out of my JSON
i am confused regarding what classes i should use in C# to consume JSON and create collection item out of my JSON
You don't need any class. Just a little bit Linq
I would use Json.Net for this
var jObj = JObject.Parse(result);
var dict = jObj.Children()
.Cast<JProperty>()
.ToDictionary(p => p.Name,
p => new Tuple<string, string>((string)p.Value["P1"], (string)p.Value["P2"]));
That is all. Sample usage would be:
foreach (var kv in dict)
{
Console.WriteLine("{0}: {1} {2}", kv.Key, kv.Value.Item1, kv.Value.Item2);
}
EDIT
Same code using JsonObject
var jObj = JsonObject.Parse(json);
var dict = jObj.Cast<KeyValuePair<string, JsonValue>>()
.ToDictionary(j=>j.Key,
j=>new Tuple<string,string>(j.Value["P1"],j.Value["P2"]));
I use the DataContractJsonSerializer to parse a json string into a object hierarchie.
The json string looks like this:
{
"groups": [
{
"attributes": [
{
"sortOrder": "1",
"value": "A"
},
{
"sortOrder": "2",
"value": "B"
}
]
},
{
"attributes": {
"sortOrder": "1",
"value": "C"
}
}
]
}
As you can see the sub value of "attributes" can be an array or a single item.
I found the code part where the problem occures:
[DataContract]
public class ItemGroup
{
[DataMember(Name="attributes")]
public List<DetailItem> Items { get; set; }
}
This works for the first one but fails on the second one.
Has anyone an answer for this?
Thx
If you control how the JSON is created then make sure that attributes is an array even if it only contains one element. Then the second element will look like this and parse fine.
{
"attributes": [{
"sortOrder": "1",
"value": "C"
}]
}
As Daniel said, if you can control the creation of Json, it is better to continue that way.
But if you can't, then you can use Json.Net library & the JsonObject class from
this link to write some code like:
JObject o = (JObject)JsonConvert.DeserializeObject(input);
dynamic json = new JsonObject(o);
foreach (var x in json.groups)
{
var attrs = x.attributes;
if (attrs is JArray)
{
foreach (var y in attrs)
{
Console.WriteLine(y.value);
}
}
else
{
Console.WriteLine(attrs.value);
}
}
I tried to get this working with DataContractJsonSerializer, JavaScriptSerializer, and JSON.Net and none would deserialize directly to an object successfully in all cases. I used a similar approach as L.B, using JSON.Net, although without the use of dynamics and the extra JsonObject class. Adapting my code to your scenario would look something like the following:
private List<ItemGroup> ParseItemGroupList(string input)
{
JObject json = JObject.Parse(input);
List<ItemGroup> groups = new List<ItemGroup>();
JArray gArray = json["groups"] as JArray;
foreach (var gToken in gArray)
{
ItemGroup newGroup = new ItemGroup();
JToken attrToken = gToken["attributes"] as JToken;
if (attrToken is JArray)
{
newGroup.Items = attrToken.Children().Select(MapDetailItem()).ToList();
}
else
{
newGroup.Items = new List<DetailItem>() { MapDetailItem().Invoke(attrToken) };
}
groups.Add(newGroup);
}
return groups;
}
private static Func<JToken, DetailItem> MapDetailItem()
{
return json => new DetailItem
{
SortOrder = (string)json["sortOrder"],
Value = (string)json["value"]
};
}
Hopefully, someone will add a setting for JSON.Net to allow it to force deserialization to a collection with a single item rather than throwing an exception. It's a shame that you have to do all of the parsing manually when there is only one small portion of the JSON that doesn't parse correctly automatically.