How to convert json to Dictionary - c#

I have to convert the JSON string to Dictionary< string, object >. For that, I am following something here https://www.programming-books.io/essential/csharp/collect-all-fields-of-json-object-5293c4c9342c403bb40dd9232692a7bc and doing DotNotation to convert it to Dictionary however, the output is not as expected.
JSON Data I am trying:
{
"name": "Madame Uppercut",
"age": 39,
"secretIdentity": "Jane Wilson",
"powers": [
"Million tonne punch",
"Damage resistance",
"Superhuman reflexes"
],
"accounting": [
{
"firstName": "John",
"lastName": "Doe",
"age": 23
},
{
"firstName": "Mary",
"lastName": "Smith",
"age": 32
}
]
}
Using the code from above link and converting from DotNotation as below
var dictionary = new Dictionary<string, object>();
foreach (var jsonInput in dotNotation)
{
var hierarchy = jsonInput.Key.Split('.');
var bottom = dictionary;
for (int i = 0; i < hierarchy.Length; i++)
{
var key = hierarchy[i];
if (i == hierarchy.Length - 1)
{
bottom.Add(key, jsonInput.Value);
}
else
{
if (!bottom.ContainsKey(key))
bottom.Add(key, new Dictionary<string, object>());
bottom = (Dictionary<string, object>)bottom[key];
}
}
}
finally, the result is like.
Now, what I want is to group the arrays(powers and accounting). I know the JToken path is giving '[]' array path but I want it like below for power and account in the dictionary.
{[power, [{"Million tonne punch" }, {"Damage resistance" }, {"Superhuman reflexes"}]]}

Use Json.NET
var values = JsonConvert.DeserializeObject<Dictionary<string, object>>(json);

Related

Remove all occurrences of a particular key from a JSON response in C#

I have a JSON string from which I want to eliminate all the occurrences of a given key.
JSON I have:
string requstBody =
{
"payLoad": [
{
"BaseVersionId_": 9,
"VersionId_": 10,
"AssetCollateralLink": [
{
"AssetId": 137,
"BaseVersionId_": 9,
"VersionId_": 10
},
{
"AssetId": 136,
"BaseVersionId_": 0,
"VersionId_": 1
}
],
"CollateralProvider": [],
"AdvCollateralAllocation": [
{
"LinkId": 91,
"IsDeleted_": false,
"BaseVersionId_": 1,
"VersionId_": 2
}
]
}
]
}
I want to eliminate keys "BaseVersionID_" and "VersionId_" as follows:
string requstBody =
{
"payLoad": [
{
"AssetCollateralLink": [
{
"AssetId": 137
},
{
"AssetId": 136
}
],
"CollateralProvider": [],
"AdvCollateralAllocation": [
{
"LinkId": 91,
"IsDeleted_": false
}
]
}
]
}
I used JObject.Remove(); as follows
JObject sampleObj1 = new JObject();
sampleObj1 = JsonHelper.JsonParse(requestBody);
sampleObj1.Remove("BaseVersionId_");
but able to remove the keys under payLoad Hierarchy only.
How do I remove all the occurrences of the Key.
The required properties can be removed from the Json, simply with Linq:
var jsonObj = JObject.Parse(requestBody);
jsonObj.SelectToken("payLoad[0]").SelectToken("AdvCollateralAllocation")
.Select(jt => (JObject)jt)
.ToList()
.ForEach(r =>
r
.Properties()
.ToList()
.ForEach(e =>
{
if (e.Name == "BaseVersionId_" || e.Name == "VersionId_")
e.Remove();
}));
The resultant jsonObj will be without the BaseVersionId_ and VersionId_ names as well as their values.
I'd use JsonPath as such:
var toRemove = jsonObject
.SelectTokens("$.payLoad.[*].AssetCollateralLink.[*]..BaseVersionId_")
.Concat(stuff.SelectTokens("$.payLoad.[*].AssetCollateralLink.[*]..VersionId_"))
.Concat(stuff.SelectTokens("$.payLoad.[*].AdvCollateralAllocation.[*]..VersionId_"))
.Concat(stuff.SelectTokens("$.payLoad.[*].AdvCollateralAllocation.[*]..BaseVersionId_"))
.ToList();
for (int i = toRemove.Count - 1; i >= 0; i--)
{
toRemove[i].Parent?.Remove();
}

Dynamically Adding to JArray generates repeated data

When I write this code:
string[] title= {"title1","title2","title3","title4"};
int[] iid= {124,122,333,234};
dynamic bucket = new JObject();
bucket.bucket = "Parking Lot";
bucket.roadmap = new JArray();
dynamic issue = new JObject();
dynamic json2 = new JArray(bucket);
for(int i=0; i<title.Length; i++) {
issue.title = title[i];
issue.iid = iid[i];
issue.order = i;
bucket.roadmap.Add(issue);
}
Console.WriteLine(json2.ToString());
My result is:
[
{
"bucket": "Parking Lot",
"roadmap": [
{
"title": "title4",
"iid": 234,
"order": 3
},
{
"title": "title2",
"iid": 122,
"order": 1
},
{
"title": "title3",
"iid": 333,
"order": 2
},
{
"title": "title4",
"iid": 234,
"order": 3
}
]
}
]
The first and last "roadmap" JObjects got repeated. There is a way if dynamically generate this JSON with their unique values I want to pass?
I also tried to do it non-dynamically.
JArray bucket = new JObject();
bucket.Add("bucket","Parking Lot");
bucket.Add("order", new JArray());
JObject issue = new JObject();
...
But I could not find a way to select "bucket.order.Add" to add issues to that JArray.
In the end, I am just trying to loop through a JArray with a bunch of data and I want to separate it from their bucket type, extract each issue value and store it in a new JSON, with the structure shown here.
Thanks
Create a new issue object before adding to the array.
for(int i=0; i<5; i++) {
issue = new JObject();
issue.title = "Elbow\"";
issue.iid = 123;
issue.order = i;
bucket.roadmap.Add(issue);
}

Remove fields from JSON dynamically using Json.Net

I have some JSON input, the shape of which I cannot predict, and I have to make some transformations (to call it something) so that some fields are not logged. For instance, if I have this JSON:
{
"id": 5,
"name": "Peter",
"password": "some pwd"
}
then after the transformation it should look like this:
{
"id": 5,
"name": "Peter"
}
The above sample is trivial, but the actual case is not so happy/easy. I will have some regular expressions and if any field(s) on the input JSON matches that, then it shouldn't be on the result. I will have to go recursively in case I have some nested objects. I've been seeing some stuff on LINQ to JSON but I have found nothing satisfying my needs.
Is there a way of doing this?
Note:
This is part of a logging library. I can use the JSON string if necessary or easier. The thing is that at some point in my logging pipeline I get the object (or string as required) and then I need to strip the sensitive data from it, such as passwords, but also any other client-specified data.
You can parse your JSON into a JToken, then use a recursive helper method to match property names to your regexes. Wherever there's a match, you can remove the property from its parent object. After all sensitive info has been removed, just use JToken.ToString() to get the redacted JSON.
Here is what the helper method might look like:
public static string RemoveSensitiveProperties(string json, IEnumerable<Regex> regexes)
{
JToken token = JToken.Parse(json);
RemoveSensitiveProperties(token, regexes);
return token.ToString();
}
public static void RemoveSensitiveProperties(JToken token, IEnumerable<Regex> regexes)
{
if (token.Type == JTokenType.Object)
{
foreach (JProperty prop in token.Children<JProperty>().ToList())
{
bool removed = false;
foreach (Regex regex in regexes)
{
if (regex.IsMatch(prop.Name))
{
prop.Remove();
removed = true;
break;
}
}
if (!removed)
{
RemoveSensitiveProperties(prop.Value, regexes);
}
}
}
else if (token.Type == JTokenType.Array)
{
foreach (JToken child in token.Children())
{
RemoveSensitiveProperties(child, regexes);
}
}
}
And here is a short demo of its use:
public static void Test()
{
string json = #"
{
""users"": [
{
""id"": 5,
""name"": ""Peter Gibbons"",
""company"": ""Initech"",
""login"": ""pgibbons"",
""password"": ""Sup3rS3cr3tP#ssw0rd!"",
""financialDetails"": {
""creditCards"": [
{
""vendor"": ""Viza"",
""cardNumber"": ""1000200030004000"",
""expDate"": ""2017-10-18"",
""securityCode"": 123,
""lastUse"": ""2016-10-15""
},
{
""vendor"": ""MasterCharge"",
""cardNumber"": ""1001200230034004"",
""expDate"": ""2018-05-21"",
""securityCode"": 789,
""lastUse"": ""2016-10-02""
}
],
""bankAccounts"": [
{
""accountType"": ""checking"",
""accountNumber"": ""12345678901"",
""financialInsitution"": ""1st Bank of USA"",
""routingNumber"": ""012345670""
}
]
},
""securityAnswers"":
[
""Constantinople"",
""Goldfinkle"",
""Poppykosh"",
],
""interests"": ""Computer security, numbers and passwords""
}
]
}";
Regex[] regexes = new Regex[]
{
new Regex("^.*password.*$", RegexOptions.IgnoreCase),
new Regex("^.*number$", RegexOptions.IgnoreCase),
new Regex("^expDate$", RegexOptions.IgnoreCase),
new Regex("^security.*$", RegexOptions.IgnoreCase),
};
string redactedJson = RemoveSensitiveProperties(json, regexes);
Console.WriteLine(redactedJson);
}
Here is the resulting output:
{
"users": [
{
"id": 5,
"name": "Peter Gibbons",
"company": "Initech",
"login": "pgibbons",
"financialDetails": {
"creditCards": [
{
"vendor": "Viza",
"lastUse": "2016-10-15"
},
{
"vendor": "MasterCharge",
"lastUse": "2016-10-02"
}
],
"bankAccounts": [
{
"accountType": "checking",
"financialInsitution": "1st Bank of USA"
}
]
},
"interests": "Computer security, numbers and passwords"
}
]
}
Fiddle: https://dotnetfiddle.net/KcSuDt
You can parse your JSON to a JContainer (which is either an object or array), then search the JSON hierarchy using DescendantsAndSelf() for properties with names that match some Regex, or string values that match a Regex, and remove those items with JToken.Remove().
For instance, given the following JSON:
{
"Items": [
{
"id": 5,
"name": "Peter",
"password": "some pwd"
},
{
"id": 5,
"name": "Peter",
"password": "some pwd"
}
],
"RootPasswrd2": "some pwd",
"SecretData": "This data is secret",
"StringArray": [
"I am public",
"This is also secret"
]
}
You can remove all properties whose name includes "pass.*w.*r.*d" as follows:
var root = (JContainer)JToken.Parse(jsonString);
var nameRegex = new Regex(".*pass.*w.*r.*d.*", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant);
var query = root.DescendantsAndSelf()
.OfType<JProperty>()
.Where(p => nameRegex.IsMatch(p.Name));
query.RemoveFromLowestPossibleParents();
Which results in:
{
"Items": [
{
"id": 5,
"name": "Peter"
},
{
"id": 5,
"name": "Peter"
}
],
"SecretData": "This data is secret",
"StringArray": [
"I am public",
"This is also secret"
]
}
And you can remove all string values that include the substring secret by doing:
var valueRegex = new Regex(".*secret.*", RegexOptions.IgnoreCase);
var query2 = root.DescendantsAndSelf()
.OfType<JValue>()
.Where(v => v.Type == JTokenType.String && valueRegex.IsMatch((string)v));
query2.RemoveFromLowestPossibleParents();
var finalJsonString = root.ToString();
Which when applied after the first transform results in:
{
"Items": [
{
"id": 5,
"name": "Peter"
},
{
"id": 5,
"name": "Peter"
}
],
"StringArray": [
"I am public"
]
}
For convenience, I am using the following extension methods:
public static partial class JsonExtensions
{
public static TJToken RemoveFromLowestPossibleParent<TJToken>(this TJToken node) where TJToken : JToken
{
if (node == null)
return null;
JToken toRemove;
var property = node.Parent as JProperty;
if (property != null)
{
// Also detach the node from its immediate containing property -- Remove() does not do this even though it seems like it should
toRemove = property;
property.Value = null;
}
else
{
toRemove = node;
}
if (toRemove.Parent != null)
toRemove.Remove();
return node;
}
public static IEnumerable<TJToken> RemoveFromLowestPossibleParents<TJToken>(this IEnumerable<TJToken> nodes) where TJToken : JToken
{
var list = nodes.ToList();
foreach (var node in list)
node.RemoveFromLowestPossibleParent();
return list;
}
}
Demo fiddle here.

How can I generate all possible LINQ strings of a json object for Json.net?

In json.net we can using linq to json based on this tutorial.
I want to know is there any way to generate a string query or not? Consider this json example:
{
"Name": "Test",
"Status": [
"S1",
"S2",
"S3",
"S4"
],
"People": [
{
"Name": "A",
"Family": "AA",
"Addresses": [
{
"Country": "A2",
"City": "A1",
"Phones": [
"A3",
"A4",
"A5"
]
}
]
},
{
"Name": "B",
"Family": "BB",
"Addresses": [
{
"Country": "B2",
"City": "B1",
"Phones": [
"B3",
"B4",
"B5"
]
}
]
},
{
"Name": "C",
"Family": "CC",
"Addresses": [
{
"Country": "C2",
"City": "C1",
"Phones": [
"C3",
"C4",
"C5"
]
},
{
"Country": "C7",
"City": "C6",
"Phones": [
"C8",
"C9",
"C10"
]
}
]
}
]
}
I wrote all possible LINQ strings by hand:
// JSON.NET Linq Strings (by HAND)
"Name"
"Status[0]"
"Status[1]"
"Status[2]"
"Status[3]"
"People[0].Name"
"People[0].Family"
"People[0].Addresses[0].City"
"People[0].Addresses[0].Country"
"People[0].Addresses[0].Phones[0]"
"People[0].Addresses[0].Phones[1]"
"People[0].Addresses[0].Phones[2]"
"People[1].Name"
"People[1].Family"
"People[1].Addresses[0].City"
"People[1].Addresses[0].Country"
"People[1].Addresses[0].Phones[0]"
"People[1].Addresses[0].Phones[1]"
"People[1].Addresses[0].Phones[2]"
"People[2].Name"
"People[2].Family"
"People[2].Addresses[0].City"
"People[2].Addresses[0].Country"
"People[2].Addresses[0].Phones[0]"
"People[2].Addresses[0].Phones[1]"
"People[2].Addresses[0].Phones[2]"
"People[2].Addresses[1].City"
"People[2].Addresses[1].Country"
"People[2].Addresses[1].Phones[0]"
"People[2].Addresses[1].Phones[1]"
"People[2].Addresses[1].Phones[2]"
I tried to write some code to create all possible strings automatically:
var dic = new Dictionary<string, string>();
var json = JsonConvert.SerializeObject(obj, new JsonSerializerSettings() { Formatting = Formatting.Indented });
JObject linq = JObject.Parse(json);
foreach (var x in linq) // DOES NOT WORK FOR CHILDREN ?????!!!!!
{
string name = x.Key;
JToken value = x.Value;
if (value.HasValues) // Array
{
var counter = 0;
foreach (var item in value.Values())
{
dic.Add(name + $"[{counter}]", item.ToString());
counter++;
}
}
else // String
{
var v = value.ToString();
dic.Add(name, v);
}
}
However, I don't know how I can write it for all children. Any help?
You can use SelectTokens("..*") to recursively descent the JSON token hierarchy, where ".." is the JSONPath recursive descent operator and "*" is a wildcard matching anything. Then you can use JToken.Path as your dictionary key:
var dic = linq.SelectTokens("..*")
.ToDictionary(t => t.Path, t => t.ToString());
Note this includes the root token. If you want to skip it, do:
var dic = linq.SelectTokens("..*")
.Where(t => t != linq)
.ToDictionary(t => t.Path, t => t.ToString());
You could also use JContainer.DescendantsAndSelf() or JContainer.Descendants() to do the recursive descent, filtering out all JProperty nodes for the same result:
var dic = linq.Descendants()
.Where(t => t.Type != JTokenType.Property)
.ToDictionary(t => t.Path, t => t.ToString());

Create JSON from 2 strings from Database (Latency and Formatting Issues)

I have created a JSON file from JArray in MVC application from 2 coloumns of my Database. But there are 2 issues:
1. The format of the file. It has extra brackets for each object.
[
[
{
"Code": "1",
"Name": "ASSETS"
}
],
[
{
"Code": "2",
"Name": "LIABILITIES"
}
],
[
{
"Code": "3",
"Name": "CAPITAL"
}
],
[
{
"Code": "4",
"Name": "REVENUE"
}
]
]
I want it as:
[
{
"Code": "1",
"Name": "ASSETS"
},
{
"Code": "2",
"Name": "LIABILITIES"
},
{
"Code": "3",
"Name": "CAPITAL"
},
{
"Code": "4",
"Name": "REVENUE"
}
]
I have loaded the values in JArray from Database and then add it in another JArray. I am using 2 for loops. 1 for making JArray for 5000 entries. And the second for returning the next valid primary key from Database. But the problem is that, it takes more than 15 minutes to process the loops and return the JSON file. Why is there so much latency? And how can I make it fast. Here is the code.
int idd =0;
JArray Array = new JArray();
for (int b = 0; b<5000; b++)
{
idd = dbid(idd);
IEnumerable<MST> accList = new List<MST>
{
new MST
{
S1 = db.MSTs.Find(idd).S1,
S2 = db.MSTs.Find(idd).S2
}
};
JArray Arrayone = new JArray(
accList.Select(p => new JObject
{
{ "Code", p.S1 },
{ "Name", p.S2 },
})
);
Array.Add(Arrayone);
}
string jsonfile = JsonConvert.SerializeObject(Array,Formatting.Indented);
string path = #"C:\Users\Awais\Desktop\accounts.json";
System.IO.File.WriteAllText(path, jsonfile);
return View(v);
}
public int dbid(int id)
{
decimal i = db.MSTs.Max(a => a.N100);
MST m = new MST();
for (; id <= i; id++)
{
m = db.MSTs.Find(++id);
if (m == null)
continue;
else
{
break;
}
}
return id;
}
When I tried the first loop for 100 entries, it took about 60 seconds to return the file.
By modeling your JSON with anonymous type, you could've done it like this:
var array = (from coa in db.MSTs
select new { Code = coa.S2, Name = coa.S1 }).ToArray();
string jsonfile = JsonConvert.SerializeObject(array, Formatting.Indented);
string path = #"C:\Users\Awais\Desktop\accounts.json";
System.IO.File.WriteAllText(path, jsonfile);
It took me 319 ms for 100 000 objects.
Try:
public class RootObject
{
public string Code { get; set; }
public string Name { get; set; }
}
var o = new List<RootObject>();
for (var i = 0; i < 100; ++i)
{
o.Add(new RootObject
{
Code = "foo",
Name = "bar"
});
}
var v = JsonConvert.SerializeObject(o);
Took around 274 milliseconds for my list to Serialize.
I have removed all loops and instead try to solve it in query and making a single array.
var k = (from coa in db.MSTs
select new { S2 = coa.S2, S1 = coa.S1 }).ToList().
Select(x => new MST { S2 = x.S2, S1 = x.S1 }).ToList();
JArray Arrayone = new JArray(
k.Select(p => new JObject
{
{ "Code", p.S1 },
{ "Name", p.S2 },
})
);
string jsonfile = JsonConvert.SerializeObject(Arrayone,Formatting.Indented);
string path = #"C:\Users\Awais\Desktop\accounts.json";
System.IO.File.WriteAllText(path, jsonfile);
It solves both problems. i.e. Brackets formatting issue and latency. Now with this code it works in less than 5 seconds

Categories