Linq in JSON.NET - c#

I have a json string as below
{
'Sheet1': [{
'EmployeeNo': '123456',
'EmployeeName': 'Midhun Mathew'
}, {
'EmployeeNo': '123457',
'EmployeeName': 'Bill Gates'
}, {
'EmployeeNo': '123456',
'Address': 'AAAA'
}, {
'EmployeeNo': '123457',
'Address': 'BBBB'
}]
}
JObject parsedJson = JObject.Parse(jsonString);
// Get the EmployeeNo's
List<JToken> tokenList = parsedJson["Sheet1"]
.GroupBy(d => d["EmployeeNo"])
.Select(s => s.Key)
.ToList();
// Get the tokens which have the Same 'EmployeeNo'
foreach (JToken j in tokenList)
{
IEnumerable<JToken> t = parsedJson["Sheet1"].Where(s => s["EmployeeNo"] == j);
}
But in the foreach I am getting only the first one which is
{
"EmployeeNo": "123456",
"EmployeeName": "Midhun Mathew"
}
I am not getting what I am doing wrong here.
My original intent is to group the JTokens having same EmployeeNo into one JObject
So in the above case I will get 2 JObjects since there are 2 different EmployeeNo's
Hope I am clear

It's not entirely clear what you're trying to do here, but you seem to be grouping by EmployeeNo, then throwing away the results to get just the keys, and then trying to do the grouping again in a more manual way.
If you remove the Select(s => s.Key) part, then you could just use the groupings directly:
var tokensByEmployeeNo = parsedJson["Sheet1"].GroupBy(x => x["EmployeeNo"]);
foreach (var grouping in tokensByEmployeeNo)
{
Console.WriteLine($"Key: {grouping.Key}");
foreach (var token in grouping)
{
Console.WriteLine(token);
}
}
If you want to create a 'new' JObject for each of these groupings, then you probably want to create a JArray per group:
var groupedArrays = parsedJson["Sheet1"]
.GroupBy(x => x["EmployeeNo"])
.Select(groupedRecords => new JArray(groupedRecords));
The first of these would look like this, for example:
[
{
"EmployeeNo": "123456",
"EmployeeName": "Midhun Mathew"
},
{
"EmployeeNo": "123456",
"Address": "AAAA"
}
]

If you just wanted to go through the list of Employees, here's how I am used doing:
var json = #"
{
'Sheet1': [{
'EmployeeNo': '123456',
'EmployeeName': 'Midhun Mathew'
}, {
'EmployeeNo': '123457',
'EmployeeName': 'Bill Gates'
}, {
'EmployeeNo': '123456',
'Address': 'AAAA'
}, {
'EmployeeNo': '123457',
'Address': 'BBBB'
}]
}";
var array = JArray.Parse(JObject.Parse(json).SelectToken("Sheet1").ToString());
foreach (var arrayElement in array)
{
var employeeNo = JObject.Parse(arrayElement.ToString()).SelectToken("EmployeeNo");
var employeeName = JObject.Parse(arrayElement.ToString()).SelectToken("EmployeeName");
Console.WriteLine("Number: {0}, Name: {1}", employeeNo, employeeName);
}
Output:

Related

How to search and get only sub document using C# mongoDB

I have data in provinces collection like this:
{
"_id": {
"$oid": "63dc7ff82e7e5e91c0f1cd87"
},
"province": "province1",
"districts": [
{
"district": "district1",
"sub_districts": [
{
"sub_district": "sub_district1",
"zip_codes": [
"zip_code1"
]
},
{
"sub_district": "sub_district2",
"zip_codes": [
"zip_code2"
]
},
],
},
],
}
This is how I get a list of sub_district for now:
- I search for province using Builders.Filter.
- Use foreach to get districts array (In province collection) and use if-statement to check if district equal searchDistrict.
- Get sub_districts array in that distric.
Source code:
public static List<string> MongoDbSelectSubDistrict(string searchProvince, string searchDistrict)
{
List<string> subDistrictList = new List<string>();
try
{
var provincesCollection = _db.GetCollection<BsonDocument>("provinces");
var builder = Builders<BsonDocument>.Filter;
var filter = builder.Empty;
if (searchProvince != "")
{
var provinceFilter = Builders<BsonDocument>.Filter.Eq("province", searchProvince);
filter &= provinceFilter;
}
/*
//***Need to be revised***
if (searchDistrict != "")
{
var districtFilter = Builders<BsonDocument>.Filter.Eq("provinces.district", searchDistrict);
filter &= districtFilter;
}
*/
var queryProvinces = provincesCollection.Find(filter).ToList();
foreach (BsonDocument queryProvince in queryProvinces)
{
BsonArray districtArray = queryProvince.GetValue("districts").AsBsonArray;
foreach (BsonDocument districtDocument in districtArray)
{
string district = districtDocument.GetValue("district").ToString();
if (district == searchDistrict) //***Need to be revised***
{
BsonArray subDistrictArray = districtDocument.GetValue("sub_districts").AsBsonArray;
foreach (BsonDocument subDistrictDocument in subDistrictArray)
{
string subDistrict = subDistrictDocument.GetValue("sub_district").ToString();
subDistrictList.Add(subDistrict);
}
}
}
}
}
catch (TimeoutException ex)
{
}
return subDistrictList;
}
Is there any efficient way to get this?
This is what I want:
[
{
"sub_district": "sub_district1",
"zip_codes": [
"zip_code1"
]
},
{
"sub_district": "sub_district2",
"zip_codes": [
"zip_code2"
]
},
]
And one more question: if I want to search for sub_district in the collection, how do I get this without looping in sub_districts array?

Is it possible to search by value in an array of json

Lets say I have this json array stored in a dynamic newtonsoft json object:
[
{
"Name":"Amal",
"Country":"India"
},
{
"Name":"Luke",
"Country":"England"
},
{
"Name":"Tom",
"Country":"Australia"
},
{
"Name":"Ram",
"Country":"India"
},
{
"Name":"Jane",
"Country":"France"
}
]
I need to be able to return the json objects in the array where a name ends with an "e"
Looking for these results:
[{ "Name":"Luke", "Country":"England" }, { "Name":"Jane", "Country":"France" }]
What you need is to use Linq extensions and Value<string>() method of the JToken since JArray is a collection of JTokens. Then build a new JArray with filtered tokens.
var json = "[ { \"Name\":\"Amal\", \"Country\":\"India\" }, { \"Name\":\"Luke\", \"Country\":\"England\" }, { \"Name\":\"Tom\", \"Country\":\"Australia\" }, { \"Name\":\"Ram\", \"Country\":\"India\" }, { \"Name\":\"Jane\", \"Country\":\"France\" }]";
var arr = JArray.Parse(json);
var result = new JArray( arr.Where(r=> r.Value<string>("Name").EndsWith("e")));
Fiddle

Access child objects from nested JSON

I have a JSON string, which is srtuctered as per the below.
"total": 5,
"filtered": 5,
"items": [
{
"assignedProducts": [
"antivirus"
],
"cloned": false,
"device_encryption_status_unmanaged": false,
"java_id": "2408cf5b-669c-434e-ac4c-a08d93c40e6a",
"last_activity": "2019-09-20T12:36:22.175Z",
"last_user_id": "5c6bc52305f05316ba18db06",
"heartbeat_utm_name": null,
"group_full_name": null,
"is_adsync_group": false,
"is_caching_proxy": false,
"info": {
"processor_architecture": "x64",
"osMajorVersion": 10,
"computer_name": "INDPC01",
"isServer": false,
"osInstallationType": "Client",
"fqdn": "INDPC01",
"osName": "Windows 10 Pro N",
"id": "4280fcb5-66c9-34e4-cac4-0ad8394ce0a6",
"name": "INDPC01"
},
I am using the following code to get values from a JSON string.
var resultObjects = AllChildren(JObject.Parse(response.Content))
.First(c => c.Type == JTokenType.Array && c.Path.Contains("items"))
.Children<JObject>();
foreach (JObject result in resultObjects)
{
foreach (JProperty property in result.Properties())
{
ListBox1.Items.Add(property.Name.ToString() + " - " + property.Value.ToString());
}
}
}
private static IEnumerable<JToken> AllChildren(JToken json)
{
foreach (var c in json.Children())
{
yield return c;
foreach (var cc in AllChildren(c))
{
yield return cc;
}
}
}
I am able to get all the values for everything within "Items", however I am not sure how to access the child JSON objects, for example the information under "info"
You can access childs using indexer, e.g.:
// ...
foreach (JObject result in resultObjects)
{
var architecture = result["info"]["processor_architecture"].ToString();
// ...
}
Note that it might be an easier approach to convert the JSON to an object, e.g. using JsonConvert, instead using JObject.Parse.

JSON - LINQ Query for object or an array to group two data with same key value pair

Consider I have a JSON data as:
{
"entities":[
{
"republish": false,
"OrgID": "",
"createdby": "730",
"questions": [
{
"sequence": "5",
"QuestionId": "57BB6DDC-A90A-10EE-E224-EC658A825871",
"metadata": [
{
"key": "Group",
"value": 0
},
{
"key": "Part",
"value": "0"
}
]
},
{
"sequence": "4",
"QuestionId": "57BB6DDC-A90A-10EE-E224-EC658A825871",
"metadata": [
{
"key": "Group",
"value": 1
},
{
"key": "Part",
"value": "A"
}
]
},
{
"sequence": "3",
"QuestionId": "57BB6DDC-A90A-10EE-E224-EC658A825871",
"metadata": [
{
"key": "Group",
"value": 1
},
{
"key": "Part",
"value": "B"
}
]
}
]
}
]
}
As you can see I have a list of questions available and in each question, I have a metadata which holds Key-Value pair.
Above example demonstrates, I have 3 questions and out of it 2 question, metadata key-value is "Group 1".
Now I want to do is combine the questions with a same key-value pair and treat it as one.
so in my final case, I will have 2 questions instead of 3. And out of that one question will have two separate questions inside.
And I want to achieve this using Linq query. And if possible please use Newtonsoft for parse if needed. I have been stuck for long onto this.
Things I have done:
public virtual HttpResponseMessage AddQuestionsToStandardMaster(TaxonomyMetaData objQuestion)
{
List<ResponseEntity> objResponseList = new List<ResponseEntity>();
try
{
if (ModelState.IsValid)
{
foreach (var objEntity in objQuestion.Entities)
{
EntityResponse objentityresponse = new EntityResponse();
ResponseEntity objResponse = new ResponseEntity();
}
List<Question> objQuestionList = new List<Question>();
if (objEntity.Questions.Length > 0)
{
foreach (var item in objEntity.Questions)
{
int questionTypeid = 0;
dynamic objQuestionJson = JObject.Parse(item.ToString())
}
}
}
Question objCurrentQuestion = new Question();
Question objQuestionforDelete = new Question();
JObject itemToParese = new JObject();
string SingleQuestionJson = objQuestionJson.GetValue("QuestionData").ToString();
string questionstem = "";
Regex rgx = new Regex("/\'");
objCurrentQuestion.Sequence = Convert.ToInt32(objQuestionJson.GetValue("sequence"));
objCurrentQuestion.tag = objQuestionJson.tag.ToObject<JToken[]>(); ;
objCurrentQuestion.metadata = objQuestionJson.metadata.ToObject<JToken[]>();
objCurrentQuestion.SingleQuestionJson = rgx.Replace(SingleQuestionJson, "'");
objCurrentQuestion.QuestionsType = questionTypeid;
objCurrentQuestion.QuestionsId = new Guid(objQuestionJson.GetValue("QuestionId").ToString());
objCurrentQuestion.VersionNo = Convert.ToInt32(objQuestionJson.GetValue("VersionNo"));
objCurrentQuestion.DisplayQuestionId = Convert.ToString(objQuestionJson.GetValue("DisplayQuestionId"));
objCurrentQuestion.OriginalQuestionId = Convert.ToString(objQuestionJson.GetValue("OriginalQuestionId"));
objCurrentQuestion.PassageText = Convert.ToString(objQuestionJson.GetValue("passage_text"));
objCurrentQuestion.PassageCode = Convert.ToString(objQuestionJson.GetValue("passage_id"));
objCurrentQuestion.PassageTitle = Convert.ToString(objQuestionJson.GetValue("passage_title"));
objCurrentQuestion.IsPublished = Convert.ToByte(true);
objCurrentQuestion.ProductId = objEntity.ProductID;
foreach (var metadata in objCurrentQuestion.metadata)
{
switch (metadata["key"].ToString())
{
case "Group":
objCurrentQuestion.Group = Convert.ToInt32(metadata["value"].ToString());
break;
case "Part":
objCurrentQuestion.Part = metadata["value"].ToString();
break;
}
}
objQuestionList.Add(objCurrentQuestion);
int counter = 1;
//Here I get the data in a group which needs to coverted to JSOn and then replace the original JSON data with this. But I know this is irrelevant to what we need to achieve.
var yui = objQuestionList.Where(tma => tma.Group == counter).Select(t => t).GroupBy(s => new { s.Group }).Where(p => p.Count() > 1).ToList();
//After proper conversion I need to enter this data to a database.
I am a bit unclear on what do you mean by make it
combine the questions with a same key-value pair and treat it as one
But here is how you can group by the JSON into groups in metadata you can do a custom select to what ever you want.
var text = #"{
""entities"":[
{
""republish"": false,
""OrgID"": """",
""createdby"": ""730"",
""questions"": [
{
""sequence"": ""5"",
""QuestionId"": ""57BB6DDC-A90A-10EE-E224-EC658A825871"",
""metadata"": [
{
""key"": ""Group"",
""value"": 0
},
{
""key"": ""Part"",
""value"": ""0""
}
]
},
{
""sequence"": ""4"",
""QuestionId"": ""57BB6DDC-A90A-10EE-E224-EC658A825871"",
""metadata"": [
{
""key"": ""Group"",
""value"": 1
},
{
""key"": ""Part"",
""value"": ""A""
}
]
},
{
""sequence"": ""3"",
""QuestionId"": ""57BB6DDC-A90A-10EE-E224-EC658A825871"",
""metadata"": [
{
""key"": ""Group"",
""value"": 1
},
{
""key"": ""Part"",
""value"": ""B""
}
]
}
]
}
]
}";
var json = JObject.Parse(text);
var groupedData = from entity in json["entities"]
from question in entity["questions"]
group question by question["metadata"][0]["value"] into questionGroup
select questionGroup;
foreach (var data in groupedData)
{
Console.WriteLine("_____________________");
Console.WriteLine("Group");
Console.WriteLine(data.Key);
foreach (var question in data)
{
Console.WriteLine(question["QuestionId"]);
}
Console.WriteLine("_____________________");
}
If "Group" is not always the fist item in the array you can do
question["metadata"].First(md => md.Value<string>("key") == "Group")["value"]
to get it.

JSON SelectTokens expression

I have a JSON file (below). My goal is to, fill a dictionary with unit_ids (f.e. in this JSON unit_ids are: 153470, 153471 and 178903) and corresponding classtype_id (f.e. in this JSON corresponding classtype_id are: CW,CW, null). (unit_ids are uniqe)
So f.e. my dictionary might be: {[153470, CW],[153471, CW],[178903, null]}.
But there is a problem, in my current approach, because of the possibility of: "unit_id":null like f.e. in this file: "178903": null.
Dictionary<string, string> unit_id_translate = new Dictionary<string, string>();
var unitIdsTypes = JObject.Parse(unit_ids_json).SelectTokens("*.['classtype_id']");
var unitIdsNumbers = JObject.Parse(unit_ids_json);
List<String> tempForUID = new List<String>();
List<String> tempForVAL = new List<String>();
foreach (var unitIdType in unitIdsTypes)
{
tempForVAL.Add(unitIdType.ToString());
}
foreach (var unitIdNumber in unitIdsNumbers)
{
foreach (var tmp44 in unitIdNumber)
{
var trimmedNr = unitIdNumber.Cast<JProperty>().Select(o => o.Name);
foreach (var unitIdNr in trimmedNr)
{
tempForUID.Add(unitIdNr);
}
break;
}
}
for (int i = 0; i < tempForUID.Count(); i++)
{
unit_id_translate.Add(tempForUID.ElementAt(i), tempForVAL.ElementAt(i));
}
I need help, because my solution crashes (out of range exception) - there is more unit_ids than classtype_id - because of the null value.
What I can do to fix this ?
{
"153470": {
"topics": {
"en": ""
},
"classtype_id": "CW",
"learning_outcomes": {
"en": ""
},
"course_id": "06-DRSOLI0",
"course_name": {
"en": "Distributed operating systems"
},
"id": 153470,
"teaching_methods": {
"en": ""
}
},
"153471": {
"topics": {
"en": ""
},
"classtype_id": "CW",
"learning_outcomes": {
"en": ""
},
"course_id": "06-DPROLI0",
"course_name": {
"en": "Team project"
},
"id": 153471,
"teaching_methods": {
"en": ""
}
},
"178903": null,
}
You can do this easily with Enumerable.ToDictionary():
var unit_id_translate = JObject.Parse(unit_ids_json)
.Properties()
.ToDictionary(p => p.Name, p => (string)p.Value.SelectToken("classtype_id"));
Then
Console.WriteLine(JsonConvert.SerializeObject(unit_id_translate));
produces {"153470":"CW","153471":"CW","178903":null}.

Categories

Resources