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

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.

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?

Flatten Json & Ignore array index Using ChoETL

I have 1 json file and these lines of code:
Here's my code:
using (var r = new ChoJSONReader("data.json")
.Configure(c => c.ThrowAndStopOnMissingField = true)
.Configure(c => c.DefaultArrayHandling = true)
.Configure(c => c.FlattenNode = true)
.Configure(c => c.IgnoreArrayIndex = false)
.Configure(c => c.NestedKeySeparator = '.')
.Configure(c => c.NestedColumnSeparator = '.')
)
{
var dt = r.AsDataTable();
Console.WriteLine(dt.DumpAsJson());
}
My data.json file:
{
"BrandId": "998877665544332211",
"Categories": [
"112233445566778899"
],
"Contact": {
"Phone": [
{
"Value": "12346789",
"Description": {
"vi": "Phone"
},
"Type": 1
},
{
"Value": "987654321",
"Description": {
"vi": "Phone"
},
"Type": 1
}
]
}
}
After running this code, I got the output like this:
[
{
"BrandId": "998877665544332211",
"Contact.Phone.0.Value": "12346789",
"Contact.Phone.0.Description.vi": "Phone",
"Contact.Phone.0.Type": 1,
"Contact.Phone.1.Value": "987654321",
"Contact.Phone.1.Description.vi": "Phone",
"Contact.Phone.1.Type": 1,
"Category0": "112233445566778899"
}
]
The question here is how can I get some kind of output json without "0" at the flattened key node
Expected output:
[
{
"BrandId": "998877665544332211",
"Contact.Phone.Value": "12346789",
"Contact.Phone.Description.vi": "Phone",
"Contact.Phone.Type": 1,
"Category": "112233445566778899"
},
{
"BrandId": "998877665544332211",
"Contact.Phone.Value": "987654321",
"Contact.Phone.Description.vi": "Phone",
"Contact.Phone.Type": 1,
"Category": "112233445566778899"
}
]
I've research by many ways but it's results doesn't as same as my expected result.
Thanks for any kind of help
As your json is nested/complex in nature, you need to unpack and flatten into multiple simple data element rows using ChoETL/Linq as below.
ChoETLSettings.KeySeparator = '-';
using (var r = ChoJSONReader.LoadText(json)
.WithField("BrandId")
.WithField("Category", jsonPath: "Categories[0]", isArray: false)
.WithField("Phone", jsonPath: "Contact.Phone[*]")
)
{
var dt = r.SelectMany(rec => ((IList)rec.Phone).OfType<dynamic>().Select(rec1 =>
{
dynamic ret = new ChoDynamicObject();
ret["BrandId"] = rec.BrandId;
ret["Contact.Phone.Value"] = rec1.Value;
ret["Contact.Phone.Description.vi"] = rec1.Description.vi;
ret["Contact.Phone.Type"] = rec1.Type;
ret["Category"] = rec.Category;
return ret;
})).AsDataTable();
dt.DumpAsJson().Print();
}
Sample fiddle: https://dotnetfiddle.net/PHK8LO

Update mongoDB document in bulk in C#

I have multiple document like below in mongo collection(giving just one sample).
{
"_id": "5fdb",
"createddate": "2020-12-17",
"orders": [
{
"_id": "4c65",
"sourcesystemrecordid": null,
"accepteddate": "2020-12-19",
"fulfillment": [
{
"_id": "7d3ceb",
"createdby": "Azekry",
"systemid": "123",
"systemrecordname": "source1"
}
]
}
]
}
Under fulfilment, I want to update "systemid" value from "123" to "789" for all the documents where "systemrecordname" = "source1" and for that I have written below piece of code but the data is not updated. Can you help me on this?
foreach (var item in resultFromDatabse)
{
var ordr = item.orders;
foreach (var e in ordr)
{
var actualval = "789";
foreach (var ef in e.fulfillment.Where(x => x.systemrecordname == "source1"))
{
filter = builder.Eq("systemrecordname", ef.systemrecordname);
var update = Builders<EquipmentDemandPlan>.Update.Set("orders.$[e].fulfillment.$[ef].systemid", actualval);
UpdateOneModel<EquipmentDemandPlan> updateOne = new UpdateOneModel<EquipmentDemandPlan>(filter, update)
{
ArrayFilters = new List<ArrayFilterDefinition> {
new BsonDocumentArrayFilterDefinition<BsonDocument>(new BsonDocument("ef.systemrecordname", ef.systemrecordname))
}
};
bulkupdate.Add(updateOne);
}
}
}
collection.BulkWriteAsync(bulkupdate);
you only need arrayFilters in $set
db.collection.update({
"orders.fulfillment.systemrecordname": "source1"
},
{
$set: {
"orders.$[o].fulfillment.$[f].systemid": "789"
}
},
{
arrayFilters: [
{
"o._id": {
$exists: true
}
},
{
"f.systemrecordname": "source1"
}
],
multi: true
})
mongoplayground

Parse nested json and return the values as string[]

Can you help me parse the values in 'realEstateProperties' attribute (ex. serial number and economyType.value). Here is a part of the json file:
{
"Entity1": {
"id": "5f514d20744a1fb",
"realEstateProperties": [
{
"serialNumber": "11",
"cadastralSections": [
{
"id": "5f514dc11a1e3",
"economyType": {
"value": "landRegisterPage.type",
}
}
],
"landRegisterPage": {
"id": "3456",
"landRegisterBook": {
"abbreviation": null
},
"note": "LRP",
"tags": null
},
"propertyTextBlock": null
}
],
"customFields": [],
Here is what I currently have:
public static string[] GetJsonValues(string jsonProperty)
{
testCaseName = "Entity1";
FixtureIncident.Root incObject = new FixtureIncident.Root();
incObject = LoadJSONFile();
JObject incJson = JObject.FromObject(incObject);
var attributes = incJson[testCaseName].ToList<JToken>();
var property = attributes.Find(i => i.ToObject<JProperty>().Name == jsonProperty) as JArray; //returns property=null
string[] Properties = property.ToObject<string[]>();
return Properties;
}
property returns null and I am not sure how to correct it.
I need to save all the realEstateProperties values in a string[].
I found some similar topics but I couldn't adjust my code so I make it works. Thanks in advance for your help.

Newtonsoft JSON - create JArray in JArray

I am trying to create JSON array using Newtonsoft JSON API but its giving me error. I want to achieve structure like
[
{
"id":"26",
"appsurvey":"1",
"fk_curriculumid":"70",
"status":"Completed",
"lastaccessedon":"2014-06-20 09:18:54",
"questions":[
{
"feedback":"6",
"questionid":"1"
},
{
"feedback":"8",
"questionid":"2"
},
{
"feedback":"1",
"questionid":"3"
}
],
"fk_clientid":"24",
"learnerid":"260"
}
]
I want ot add questions array for multiple time but it is giving me error
Can not add property questions to Newtonsoft.Json.Linq.JObject. Property with the same name already exists on object.
Here is my code:
JArray surveytrackingA = new JArray();
/*code to add
[
{"id":"26",
"appsurvey":"1",
"fk_curriculumid":"70",
"status":"Completed",
"lastaccessedon":"2014-06-20 09:18:54"}]
*/
for (int i = 0; i < surveytrackingA.Count; i++)
{
JObject surveytrackD = (JObject)surveytrackingA[i];
string queryOne = "select * from table101 where fk_curriculumid='"
+ surveytrackD["fk_curriculumid"].ToString()
+ "' and fk_surveyid='"
+ surveytrackD["appsurvey"].ToString() + "'";
JArray questionsA = new JArray();
using (var stmt = await App.localDB.PrepareStatementAsync(queryOne))
{
while (await stmt.StepAsync())
{
JObject questionD = new JObject();
questionD.Add("questionid", stmt.GetTextAt(5));
questionD.Add("feedback", stmt.GetTextAt(6));
questionsA.Add(questionD);
}
}
surveytrackD.Add("questions", questionsA); /*error occurred here when second question array is getting inserted in surveyTrackD*/
surveytrackingA.Add(surveytrackD);
}
Can anyone please correct me. Thanks in advance.
Update:
surveytrackD have the json data,
{
"fk_clientid": "24",
"learnerid": "260",
"appsurvey": "1",
"id": "26",
"fk_curriculumid": "70",
"status": "completed",
"lastaccessedon": "2014-06-20 09:18:54"
}
You can achieve the same result (JArray in JArray) using regular C# classes and at the end serialize to JSon.
I posted a sample in Github; here a fragment of the code that produces your expected output:
var Surveys = new List<SurveytrackD>();
Surveys.Add( new SurveytrackD { id = "26", appsurvey = "1", fk_curriculumid = "70", status = "Completed", learnerid = "240" } );
Surveys.Add( new SurveytrackD { id = "27", appsurvey = "1", fk_curriculumid = "71", status = "Completed", learnerid = "241" });
foreach (var survey in Surveys)
{
survey.questions = new List<Question>();
survey.questions.Add(new Question { questionid = "1", feedback = "0" });
survey.questions.Add(new Question { questionid = "2", feedback = "1" });
}
var json = JsonConvert.SerializeObject(Surveys, Formatting.Indented);
Console.WriteLine(json);
The output is:
[
{
"fk_clientid": null,
"learnerid": "240",
"appsurvey": "1",
"id": "26",
"fk_curriculumid": "70",
"status": "Completed",
"lastaccessedon": null,
"questions": [
{
"feedback": "0",
"questionid": "1"
},
{
"feedback": "1",
"questionid": "2"
}
]
},
{
"fk_clientid": null,
"learnerid": "241",
"appsurvey": "1",
"id": "27",
"fk_curriculumid": "71",
"status": "Completed",
"lastaccessedon": null,
"questions": [
{
"feedback": "0",
"questionid": "1"
},
{
"feedback": "1",
"questionid": "2"
}
]
}
]

Categories

Resources