Parsing json file in c# to get into all sections and properties - c#

==========Update
So in trying to phrase the issue properly, I have misled the responders in not giving me the answer I need. Apologies, let me try to clarify.
I need to be able to cycle through a json file that is not as structured as I indicated in the OP, it is much more random. The OP file did not convey that very well.
Let me try to describe what is closer to the file I'll be getting. The first two levels will be fixed, I'll call them LevelA and LevelB. But the properties in LevelB can be any pair of random data. This is a better json file example:
{
"LevelA": {
"LevelB": [
{
"EmpName": "John",
"EmpGender": "Male",
"Age": "25"
},
{
"FavoriteFood": "Beer",
"BaseballTeam": "Kansas City Royals"
},
{
"Red": "10",
"Blue": "40",
"White: "True"
}
]
}
}
Say I need to write out the following to console:
A LevelB entry has these properties:
Property: EmpName, Value: John
Property: EmpGender, Value: Male
Property: Age, Value: 25
A LevelB entry has these properties:
Property: FavoriteFood, Value: Beer
Property: BaseballTeam, Value: Kansas City Royals
A LevelB entry has these properties:
Property: Red, Value: 10
Property: Blue, Value: 40
Property: White, Value: True
It may not make sense but, I need to find a way to do that, I need that knowledge. I appreciate the answers about using a model, but I don't see a way to use a model without complicating what I think should be a simple task. Although not simple enough for me to figure out apparently. :)
==========
C#, VS 2019, a .NET framework console test app. I'm looking to do something simple but I can't find the right syntax.
I have a json file with the following structure. I want to loop through each Employee below and get at its properties (name, gender, etc...):
{
"Company": {
"Employees": [
{
"EmpName": "John",
"EmpGender": "Male",
"Age": "25"
},
{
"EmpName": "Mary",
"EmpGender": "Female"
},
{
"EmpName": "Bill",
"Age": "30"
}
]
}
}
First question is which package will do the job? I've installed Microsoft.Extensions.Configuration.Json and System.Configuration.ConfigurationManager but am having difficulties getting the syntax correct. Are these the right packages to use? I decided to use those because I thought I could load the file via ConfigurationBuilder and that the GetSection and/or GetChildren methods would help me. I can load the file but I can't figure out how to use those methods to give me what I want.
Second I don't want to build a model for this, I just want at the data. If I can get each employee into a dictionary, I can analyze that, and that would get me going. Thanks for any advice.

You can use the built-in JSON library in the .net core
using System.Text.Json;
add the following model definition
public class Rootobject
{
public Company Company { get; set; }
}
public class Company
{
public Employee[] Employees { get; set; }
}
public class Employee
{
public string EmpName { get; set; }
public string EmpGender { get; set; }
public string Age { get; set; }
}
deserialize your object like the following
string jsonData = File.ReadAllText("data.json");
Rootobject ob = JsonSerializer.Deserialize<Rootobject>(jsonData);
now you have ob in you c# represent your JSON as C# object
I don't want to build a model for this
if you use Visual Studio you can auto generate your model classes required for your JSON as described here the above models are auto generated by Visual Studio

If you don't want to create a model,and get a List<Dictionary<string,string>>,you can use custom model binding,here is a demo:
CustomBinder:
public class CustomBinder:IModelBinder
{
public Task BindModelAsync(ModelBindingContext bindingContext)
{
if (bindingContext == null)
{
throw new ArgumentNullException(nameof(bindingContext));
}
var model = new List<Dictionary<string, string>>();
using (var reader = new StreamReader(bindingContext.HttpContext.Request.Body))
{
var body = reader.ReadToEndAsync();
var mydata = JsonConvert.DeserializeObject<JObject>(body.Result);
var s = mydata["Company"]["Employees"].ToString();
var list=JsonConvert.DeserializeObject<List<JObject>>(s);
foreach (var jobj in list) {
Dictionary<string, string> d = new Dictionary<string, string>();
foreach (var x in jobj)
{
string name = x.Key;
string value = x.Value.ToString();
d.Add(x.Key, x.Value.ToString());
}
model.Add(d);
}
}
bindingContext.Result = ModelBindingResult.Success(model);
return Task.CompletedTask;
}
}
Action:
public void TestJson1([ModelBinder(BinderType = typeof(CustomBinder))] List<Dictionary<string, string>> jsondata)
{
}
result:

Why is it a requirement not to build models? In my opinoin that is the easiest way and if you need to expand due to JSON changes, you can easily adapt it in the code. I paste a sample code here what is using System.Text.Json namespace (no other packages are also required due to it is built-in).
using System;
using System.Collections.Generic;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace jsontest
{
class Program
{
static void Main(string[] args)
{
string input = "{ \"Company\": { \"Employees\": [{ \"EmpName\": \"John\", \"EmpGender\": \"Male\", \"Age\": \"25\" }, { \"EmpName\": \"Mary\", \"EmpGender\": \"Female\" }, { \"EmpName\": \"Bill\", \"Age\": \"30\" }]}}";
var processedInput = JsonSerializer.Deserialize<Company>(input);
foreach (var item in processedInput.Peoples.PeopleList)
{
Console.WriteLine($"{item.Name} - {item.Gender} - {item.Age}");
}
}
}
public class Employee
{
[JsonPropertyName("EmpName")]
public string Name { get; set; }
[JsonPropertyName("EmpGender")]
public string Gender { get; set; }
[JsonPropertyName("Age")]
public string Age { get; set; }
}
public class Employees
{
[JsonPropertyName("Employees")]
public List<Employee> PeopleList { get; set; }
}
public class Company
{
[JsonPropertyName("Company")]
public Employees Peoples { get; set; }
}
}
Update after your update:
Strange why you store data in JSON on this way. I wrote a quick code, it is using regex and some built-in function to parse the text. At the end result is similar what you would prefer. Code is a bit long, but only because I put some comment to make understanding easier.
using System;
using System.Collections.Generic;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace jsontest
{
class Program
{
static void Main(string[] args)
{
List<List<ListItem>> result = new List<List<ListItem>>();
string input = "{ \"Company\": { \"Employees\": [{ \"EmpName\": \"John\", \"EmpGender\": \"Male\", \"Age\": \"25\" }, { \"EmpName\": \"Mary\", \"EmpGender\": \"Female\" }, { \"EmpName\": \"Bill\", \"Age\": \"30\" }]}}";
// Remove new lines, so input will become one single line
input = input.Replace(Environment.NewLine, " ");
// 1. group is the group name (in this case Employees)
// 2. group is the content after group name
string pattern1 = #"[{].+[{](.+)[\[](.+)[\]]";
foreach (System.Text.RegularExpressions.Match m in System.Text.RegularExpressions.Regex.Matches(input, pattern1))
{
// groupName -> "Employees":
string groupName = m.Groups[1].Value;
// groupName -> Employees
groupName = groupName.Substring(0, groupName.LastIndexOf("\""));
groupName = groupName.Substring(groupName.IndexOf("\"") + 1);
// contentList -> { "EmpName": "John", "EmpGender": "Male", "Age": "25" }, { "EmpName": "Mary", "EmpGender": "Female" }, { "EmpName": "Bill", "Age": "30" }
string contentList = m.Groups[2].Value;
// Split the line onto more lines where "}," characters
// { "EmpName": "John", "EmpGender": "Male", "Age": "25"
// { "EmpName": "Mary", "EmpGender": "Female"
// { "EmpName": "Bill", "Age": "30" }
string[] contentItems = contentList.Split("},");
foreach (var item in contentItems)
{
// Check every group and store them in separate list
result.Add(new List<ListItem>());
string[] keys = item.Split(",");
foreach (var key in keys)
{
// Check every Key-Value pair and store their value in the current list
string pattern2 = "[\"](.+)[:].[\"](.+)[\"]";
foreach (System.Text.RegularExpressions.Match m2 in System.Text.RegularExpressions.Regex.Matches(key, pattern2))
{
result[result.Count - 1].Add(new ListItem() { Property = groupName, Key = m2.Groups[1].Value.Substring(0, m2.Groups[1].Value.Length - 1), Value = m2.Groups[2].Value });
}
}
}
}
for (int i = 0; i < result.Count; i++)
{
for (int j = 0; j < result[i].Count; j++)
{
if (j == 0)
Console.WriteLine($"A {result[i][j].Property} entry has these properties:");
Console.WriteLine($"Proprty: {result[i][j].Key}, Value: {result[i][j].Value}");
}
}
}
}
class ListItem
{
public string Property { get; set; }
public string Key { get; set; }
public string Value { get; set; }
}
}
Output of this code is:
A Employees entry has these properties:
Proprty: EmpName, Value: John
Proprty: EmpGender, Value: Male
Proprty: Age, Value: 25
A Employees entry has these properties:
Proprty: EmpName, Value: Mary
Proprty: EmpGender, Value: Female
A Employees entry has these properties:
Proprty: EmpName, Value: Bill
Proprty: Age, Value: 30

Related

How to convert object which is like to json object to c#?

I have an object returned from API. It is very like to json but it is not. I printed it in visual studio immediate window. Something like.
[0]: {{
"projectId": 111,
"userId": 11,
"name": "Bill, Gates",
"value": "11"
}}
[1]: {{
"projectId": 222,
"userId": 22,
"name": "Cill, Gates",
"value": "22"
}}
// .....many
It is not json type. So I use cast
IEnumerable<MyClass> list = obj.Cast<object>();
public class MyClass
{
public int projectId {get; set;}
public int userId {get; set;}
public string name {get; set;}
public string value {get; set;}
}
However when I loop through the list
foreach(var item in list)
{
var temp = item.value;
}
I can't get item or temp. Hover it or print it in the immediate window; it shows
NullReferenceException
I assume that I have to convert the object to real json type rather then using cast<object>.
So what is the best way?
You can try something like this:
json = Regex.Replace(json, #"\[[0-9]+\]:", ""); // remove [0]:
json = json.Replace("{{", "{");
json = json.Replace("}}", "},");
json = $"[{json}]";
var obj = JsonConvert.DeserializeObject(json);
might need more work if you have more nested objects
Okay, so I was experimenting with your string and trying to use Regular Expressions and I have found a way to parse your string into your corresponding MyClass structure. I hope this helps you out:
using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
public class Program
{
public static void Main()
{
string str = #"[0]: {{
'projectId': 111,
'userId': 11,
'name': 'Bill, Gates',
'value': '11'
}}
[1]: {{
'projectId': 222,
'userId': 22,
'name': 'Cill, Gates',
'value': '22'
}}";
try
{
showMatch(str, #"('(.*)':(.*)[',])");
}
catch(Exception ex)
{
Console.WriteLine("Exception occured: "+ex.Message);
}
}
static private void showMatch(string text, string expr)
{
//Declare what is required! Phew
List<MyClass> output = new List<MyClass>();
int projectId=0;
int userId=0;
string name=string.Empty;
string value=string.Empty;
var regEx = new Regex(#"[^0-9a-zA-Z]+");
//Start the matching process
MatchCollection mc = Regex.Matches(text, expr);
bool check;
foreach (Match m in mc)
{
check = false;
string[] parsed = m.ToString().Split(':');
var stripped = regEx.Replace(parsed[1], "");
if (parsed[0] == "'projectId'")
{
projectId = Convert.ToInt32(stripped);
}
if (parsed[0] == "'userId'")
{
userId = Convert.ToInt32(stripped);
}
if (parsed[0] == "'name'")
{
name = stripped;
}
if (parsed[0] == "'value'")
{
value = stripped;
check = true;
}
//Add to list
if(check==true)
{
output.Add(new MyClass { projectId = projectId, userId = userId, name = name, value = value });
}
}
output.Dump();
}
}
public class MyClass
{
public int projectId { get; set; }
public int userId { get; set; }
public string name { get; set; }
public string value { get; set; }
}
Output:
[
{
name : BillGates
projectId : 111
userId : 11
value : 11
},
{
name : CillGates
projectId : 222
userId : 22
value : 22
}
]
Working DEMO: https://dotnetfiddle.net/TCXVde

Serializing and deserializing objects with arbitrary fields using Json.NET in Unity

I'd like to serialize and deserialize objects with arbitrary fields. I've tried to have the object with the arbitrary fields extend Dictionary<string, object> in hopes that I would be able to set the arbitrary fields as Dictionary entries. In this case I expect to have Company and Position in the json response (listed in code comments) in addition to the manager and office fields. Unfortunately I'm able get the arbitrary fields but unable to get the non arbitrary fields.
I would also like to be able to add arbitrary objects, not just strings (ie add a salary object with base salary, bonus, etc to the job). I also have some restrictions and cannot use dynamic for this.
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
public Job Job { get; set; }
}
public class Job : Dictionary<string, object>
{
public string Company { get; set; }
public string Position { get; set; }
}
var job = new Job()
{
Company = "Super Mart",
Position = "Cashier"
};
// Set arbitrary fields
job["manager"] = "Kathy";
job["office"] = "001";
var john = new Person()
{
Name = "John Doe",
Age = 41,
Job = job
};
var employeeJson = JsonConvert.SerializeObject(john, Formatting.Indented);
Log.Debug("TestSerialization", "json: {0}", employeeJson);
// Result
// {
// "Name": "John Doe",
// "Age": 41,
// "Job": {
// "manager": "Kathy",
// "office": "001"
// }
// }
var johnDoe = JsonConvert.DeserializeObject<Person>(employeeJson);
Log.Debug("TestSerialization", "name: {0}, age: {1}", johnDoe.Name, johnDoe.Age);
// Result
// name: John Doe, age: 41
Log.Debug("TestSerialization", "company: {0}, position: {1}", johnDoe.Job.Company, johnDoe.Job.Position);
// Result
// company: , position:
Log.Debug("TestSerialization", "manager: {0}, office: {1}", johnDoe.Job["manager"], johnDoe.Job["office"]);
// Result
// manager: Kathy, office: 001
My result json from deserialization using this code is
{
"Name": "John Doe",
"Age": 41,
"Job": {
"manager": "Kathy",
"office": "001"
}
}
I would like the result json to be (what the service would expect)
{
"Name": "John Doe",
"Age": 41,
"Job": {
"Company" = "Super Mart",
"Position" = "Cashier"
"manager": "Kathy",
"office": "001"
}
}
I think the problem with your job Class, it derived from a dictionary so when you serialize it will not consider its members. only dictionary values,
Try this way, I am not sure this will help your context
public class Job
{
public string Company { get; set; }
public string Position { get; set; }
public Dictionary<string,object> job { get; set; }
public Job()
{
job = new Dictionary<string, object>();
}
}
Debug.Log(johnDoe.Job.job["manager"]+"-"+ johnDoe.Job.job["office"]);

How do I make a JSON array?

I want many of them but I don't how to make so many I can only make one just like this here:
[
{
"NAME1": "Max1"
}
]
But I want to make this:
[
{
"NAME1": "Max1"
},
{
"NAME2": "Max2"
},
{
"NAME3": "Max3"
},
{
"NAME4": "Max4"
}
]
How do I do it
Here is my code:
public void nxt_Click(object sender, EventArgs e)
{
List<Voc> _data = new List<Voc>();
_data.Add(new Voc()
{
NAME = textBox1.Text
});
string jger2 = JsonConvert.SerializeObject(_data.ToArray(), Formatting.Indented);
File.WriteAllText(#"D:\Users\Oxygen\Desktop\ss.json", jger2);
}
public class Voc
{
public string NAME { get; set; }
}
has anybody any ideas?
Below are a couple of ways to do this, demonstrated in this fiddle.
The first uses anonymous types, which are objects which don't have a real class, but are constructed by assigning values to properties. However, to use these you need to know the property names at compile time; so this will only work if you know exactly how many names you'll have in your array. e.g.
var data = new object[] {
new {Name1 = textBox1.Text}
,new {Name2 = textBox2.Text}
,new {Name3 = textBox3.Text}
,new {Name4 = textBox4.Text}
};
Another approach is to use a dictionary, which can be populated with name value pairs at runtime, then you can convert these to JSON. E.g.
var textBoxes = new [] {textBox1, textBox2, textBox3, textBox4};
var dict = new Dictionary<string,string>();
for (var i = 0; i< textBoxes.Length; i++)
{
dict.Add(string.Format("Name{0}", i), textBoxes[i].Text );
}
However
I would strongly advise against these methods; or rather this approach. JSON is designed to be made up of key-value pairs. They keys should be known, whilst the values can change. That means that if you have 4 different values for a name instead of holding 4 different names, you hold those values against that name; e.g.
{"Name": ["Max1","Max2","Max3","Max4"]}
...with the number of the element being defined by the array's index.
The C# for that looks like this:
SomeClass data = GetValues();
//...
public class SomeClass
{
public IEnumerable<string> Name {get;private set;}
//or: public string[] Name {get;private set;}
//...
}
If you really need to store the different names, those should be stored as values against the name key; e.g.
[
{"Name": "Name1", "Value": "Max1"}
,{"Name": "Name2", "Value": "Max2"}
,{"Name": "Name3", "Value": "Max3"}
,{"Name": "Name4", "Value": "Max4"}
]
The C# for that looks like this:
IEnumerable<SomeClass> data = GetValues();
//or: SomeClass[] data = GetValues();
//...
public class SomeClass
{
public string Name {get;private set;}
public string Value {get;private set;}
//...
}
 
Full Code
using System;
using System.Linq;
using System.Collections.Generic;
using Newtonsoft.Json;
public class Program
{
public static void Main()
{
//setup our test data
var textBox1 = new TextBox("Max1");
var textBox2 = new TextBox("Max2");
var textBox3 = new TextBox("Max3");
var textBox4 = new TextBox("Max4");
//demo using anonymous objects (NB: property names must be known at compile time)
var data = new object[] {
new {Name1 = textBox1.Text}
,new {Name2 = textBox2.Text}
,new {Name3 = textBox3.Text}
,new {Name4 = textBox4.Text}
};
Console.WriteLine( JsonConvert.SerializeObject(data, Formatting.Indented));
//demo using a dictionary
var textBoxes = new [] {textBox1, textBox2, textBox3, textBox4};
var dict = new Dictionary<string,string>();
for (var i = 0; i< textBoxes.Length; i++)
{
dict.Add(string.Format("Name{0}", i+1), textBoxes[i].Text );
}
Console.WriteLine("[" + string.Join(",", dict.Select( e => string.Format("{{\"{0}\": \"{1}\"}}", e.Key.Replace("\"","\"\""), e.Value.Replace("\"","\"\"") ) )) + "]"); //based on https://stackoverflow.com/questions/5597349/how-do-i-convert-a-dictionary-to-a-json-string-in-c/5597628#5597628
}
}
//dummy class
public class TextBox
{
public TextBox(string text){Text = text;}
public string Text{get;private set;}
}
First of all, your Array is poorly formatted.
It should be:
{
"NAMES": [
{
"NAME": "Max1"
},
{
"NAME": "Max2"
},
{
"NAME": "Max3"
},
{
"NAME": "Max4"
}
]
}
Run that through json2Csharp.com and it will generate the following:
public class NAME
{
public string NAME { get; set; }
}
public class RootObject //Rename this
{
public List<NAME> NAMES { get; set; }
}
Which you should be able to serialize and deserialize using almost any C# JSON library.

Convert CSV (nested objects) to JSON

I need to convert a CSV data (with one line of header, and one line of data) in a JSON object.
The CSV contains nested columns, there is an example:
id,name,category/id,category/name,category/subcategory/id,category/subcategory/name,description
0,Test123,15,Cat123,10,SubCat123,Desc123
And I want a JSON to look like this:
{
"id": 0,
"name": "Test123",
"category": {
"id": 15,
"name": "Cat123",
"subcategory": {
"id": 10,
"name": "SubCat123",
}
},
"description": "Desc123"
}
I've tried CsvHelper and ChoETL libs, without success, because these libs, as far as I know, require that I have a class as model, but I don't have these classes, because the data is totally dynamic.
The site http://www.convertcsv.com/csv-to-json.htm is a good example to make this with success.
Just paste the JSON that I have created above, go to Step 3 and check the option 'Recreate nested objects and arrays', then click 'CSV to JSON' in step 5.
But I need to make this in my application, without using external frameworks.
How can I make this to work?
If you do not have it, add the newtonsoft library(dll) and then add the folowing references
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
Add the following classes
public class Rootobject
{
public int id { get; set; }
public string name { get; set; }
public Category category { get; set; }
public string description { get; set; }
}
public class Category
{
public int id { get; set; }
public string name { get; set; }
public Subcategory subcategory { get; set; }
}
public class Subcategory
{
public int id { get; set; }
public string name { get; set; }
}
then use this code
DataTable CSVData = new DataTable(); // your csv rows
HashSet<Rootobject> MyObjectsList = new HashSet<Rootobject>(); //create hashset to handle your classes
foreach(DataRow row in CSVData.Rows)
{
//change the indices in ItemArray with the right indices
MyObjectsList.Add(new Rootobject() {
id = (int)row.ItemArray[0], name = (string)row.ItemArray[0], category = new Category() {
id = (int)row.ItemArray[0], name = (string)row.ItemArray[0], subcategory = new Subcategory() {
id = (int)row.ItemArray[0], name = (string)row.ItemArray[0]
}
}
});
}
string _ResultObj = JsonConvert.SerializeObject(MyObjectsList); //here get your json string
With the latest ChoETL.JSON 1.0.1.6, you can convert them easily. This will work only if the headers are simple texts, no spaces or special characters allowed.
using (var json = new ChoJSONWriter("nested.json")
.SingleElement()
.Configure(c => c.SupportMultipleContent = true)
.Configure(c => c.DefaultArrayHandling = false)
)
{
using (var csv = new ChoCSVReader("nested.csv").WithFirstLineHeader())
json.Write(csv.Select(i => i.ConvertToNestedObject('/')));
}
Output JSON will look at below
[
{
"id":0,
"name":"Test123",
"category": {
"id": 15,
"name": "Cat123",
"subcategory": {
"id": 10,
"name": "SubCat123"
}
},
"description":"Desc123"
}
]
Sample fiddle: https://dotnetfiddle.net/vttMIB
UPDATE:
Cinchoo ETL now supports native nested object support by just setting 'NestedColumnSeparator' configuration parameter to '/'. Sample below shows how to
using (var json = new ChoJSONWriter("nested.json")
.SingleElement()
.Configure(c => c.SupportMultipleContent = true)
.Configure(c => c.DefaultArrayHandling = false)
)
{
using (var csv = new ChoCSVReader("nested.csv")
.WithFirstLineHeader()
.Configure(c => c.NestedColumnSeparator = '/')
)
json.Write(csv);
}
Sample fiddle: https://dotnetfiddle.net/xFlQso

JSON string to model / HashMap/ Dictionary in C#

I have JSON:
{
"One": [
{
"ID": 1,
"name": "s"
},
{
"categoryID": 2,
"name": "c"
}
],
"Two": [
{
"ID": 3,
"name": "l"
}
],
"Three": [
{
"ID": 8,
"name": "s&P"
},
{
"ID": 52,
"name": "BB"
}
]
}
I want to:
Take this JSON to any object(like JObject)
Filter the JSON on different conditions(like name start with s, etc
Return this JSON to client
Things I have tried:
1. Creating models:
class Model
{
public int Id;
public string name;
}
class MainModel
{
public string mainName;
public List<Model> categories;
}
And use these model to:
List<MainModel> m = json_serializer.DeserializeObject(jsonString);
I tried to Dictionary also but getting unable to cast exception.
any help would be appreciated.
The following will allow you to deserialize your JSON to a Dictionary and filter on it. This uses Newtonsoft.Json Nuget package, but that's fairly common. Code posted below. Working example found here .Net Fiddle.
using System;
using Newtonsoft.Json;
using System.Collections.Generic;
using System.Linq;
public class Program
{
public static void Main()
{
var json = "{\"One\": [{ \"ID\": 1, \"name\": \"s\"},{ \"categoryID\": 2, \"name\": \"c\"}],\"Two\": [{ \"ID\": 3, \"name\": \"l\"}],\"Three\": [{ \"ID\": 8, \"name\": \"s&P\"},{ \"ID\": 52, \"name\": \"BB\"}]}";
var deserialized = JsonConvert.DeserializeObject<Dictionary<string, List<Model>>>(json);
Console.WriteLine(deserialized["One"][0].name);
Console.WriteLine("Filter to starts with s");
var filtered = deserialized.SelectMany(item => item.Value).Where(innerItem => innerItem.name.StartsWith("s"));
foreach(var item in filtered){
Console.WriteLine(item.name);
}
}
public class Model{
public int ID {get;set;}
public string name {get;set;}
public int categoryID {get;set;}
}
}
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:
I don't know. This is a paste and forget soltuion :P
Probably a harder time when changing your JSON structure (although I might argue it would make things much easier for you)
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
};

Categories

Resources