I have written this code assuming that:
One, Two and Three are a MainCategory -which is seralized as JProperty from a KeyValuePair<string name, List<SubCategory> values>. Long type name, eh? Add the following if you want to use the result dictionary often:
using MainCategories = System.Collections.Generic.Dictionary<string, System.Collections.Generic.List<JsonDeSerializtionTest.SubCategory>>;
categoryID and ID are properties that descibe an Id and can not co-exist on the same SubCategory but do change the type of SubCategory.
1. Create the SubCategory class:
public class SubCategory
{
[JsonIgnore]
public DataTypes DataType { get; set; }
[JsonIgnore]
public string Parent { get; set; }
[JsonProperty("ID")]
public int Id { get; set; }
[JsonProperty("categoryID")]
public int CategoryId => Id;
[JsonProperty("name")]
public string Name { get; set; }
public bool ShouldSerializeId()
{
return DataType == DataTypes.Id;
}
public bool ShouldSerializeCategoryId()
{
return DataType == DataTypes.CategoryId;
}
}
2. Create the Deserialize(string json) function:
private static MainCategories Deserialize(string json)
{
Dictionary<string, List < SubCategory >> jsonBody = new Dictionary<string, List<SubCategory>>();
JObject jObject = JObject.Parse(json);
// the outer object {one...two..}
foreach (KeyValuePair<string, JToken> jMainCategory in jObject)
{
// jMainCategory => "one": [{...}, {...}]
string key = jMainCategory.Key;
List<SubCategory> values = new List<SubCategory>();
foreach (JObject jSubCategory in jMainCategory.Value)
{
//jsubCategory => {"name" : ..., "ID": ... }
SubCategory subCategory = new SubCategory();
JToken idProperty;
if (jSubCategory.TryGetValue("ID", out idProperty))
{
subCategory.DataType = DataTypes.Id;
subCategory.Id = idProperty.Value<int>();
}
else
{
subCategory.DataType = DataTypes.CategoryId;
subCategory.Id = jSubCategory["categoryID"].Value<int>();
}
subCategory.Name = jSubCategory["name"].Value<string>();
subCategory.Parent = key;
// subCategory.AnotherProperty = jSubCategory["anotherproperty"].Value<type>();
values.Add(subCategory);
}
jsonBody.Add(key, values);
}
return jsonBody;
}
3. Use the function to get a Dictionary<string, List<SubCategory>> which you can use for sorting and filtering. Example:
public static MainCategories WhereNameStartsWith(this MainCategories jsonBody, string str)
{
MainCategories result = new MainCategories();
//if you want to keep the result json structure `as is` return a MainCategories object
foreach (var subCategory in jsonBody.SelectMany(mainCategory => mainCategory.Value).Where(subCategory => subCategory.Name.StartsWith(str)))
{
if(result.ContainsKey(subCategory.Parent))
result[subCategory.Parent].Add(subCategory);
else
result.Add(subCategory.Parent, new List<SubCategory> {subCategory});
}
// if you just want the subcategories matching the condition create a WhereListNameStartsWith method
// where `result` is a list of subcategories matching the condition
return result;
}
Upside:
No playing around with NewtonSoft JsonConverter or ContractResolvers.
The ability to use LINQ to easily sort Name AND CategoryId/Id which have the same value but make a different SubCategory.DataType:
foreach (var subCategory in jsonBody.SelectMany(mainCategory => mainCategory.Value).Where(subCategory => subCategory.DataType == DataTypes.CategoryId))
You can easily serialize the JSON back to string representation. Example:
string jsonIn ="{\"One\":[{\"ID\":1,\"name\":\"s\"}," +
"{\"categoryID\":2,\"name\":\"c\"}]," +
"\"Two\":[{\"ID\":3,\"name\":\"l\"}]," +
"\"Three\":[{\"ID\":8,\"name\":\"s&P\"}," +
"{\"ID\":52,\"name\":\"BB\"}]}";
MainCategories desrializedJson = Deserialize(jsonIn);
MainCategories filtered = desrializedJson.WhereNameStartsWith("s");
string jsonOut = JsonConvert.SerializeObject(desrializedJson, Formatting.None);
Debug.Assert(jsonOut == jsonIn); //true
Removes the need for null checking when accessing Id/CategoryId.
My favorite: using C# syntax for property names.
Downside:
Notes:
Parent is optional and is used to make things easier when using LINQ
DataTypes is an enum which is used to determine wether ID or categoryID was found if you want such information and also to serialize the proper property (identical two way convertion)
public enum DataTypes
{
Id,
CategoryId
};
Dictionary<string, List<Model>>?var jObj = JObject.Parse(jsonString);Now you have more than a dictionary. newtonsoft.com/json/help/html/…