Distinct Json Data c# - c#

i have created a method in asp.net core to get the parent name and its childs name in a single json array but i dont know how to do it what my method is giving me result is following
{
"parent": "Kitchen & Dining ",
"parentID": "QojQhZoUTkG1VhOMJVOBmw==",
"child": "Bakeware",
"childID": "BamlddxbUk2lx3uhaT4Hbg=="
},
{
"parent": "Hobbies",
"parentID": "yTCm3n8OCUSDQBzDt1f9Hw==",
"child": "Travel",
"childID": "CAqwuhPIL02T2ufP/Tl0CA=="
},
{
"parent": "Kitchen & Dining ",
"parentID": "QojQhZoUTkG1VhOMJVOBmw==",
"child": "Table Linen",
"childID": "EMz9RMY4RESyKtvFVAJTVQ=="
},
{
"parent": "Kitchen & Dining ",
"parentID": "QojQhZoUTkG1VhOMJVOBmw==",
"child": "Tools and Gadgets",
"childID": "FQtZRfDqtkGrn2II8HobZw=="
},
{
"parent": "Kitchen & Dining ",
"parentID": "QojQhZoUTkG1VhOMJVOBmw==",
"child": "Dinnerware",
"childID": "FXmgG6wf9UW9AC519gHuTw=="
},
{
"parent": "Hobbies",
"parentID": "yTCm3n8OCUSDQBzDt1f9Hw==",
"child": "Camping & Trecking",
"childID": "Kgj+Y1xdiUO2i79hxyDE6w=="
};
but i dont want data like this I want it like this :
{
"parent": "Kitchen & Dining ",
"parentID": "QojQhZoUTkG1VhOMJVOBmw==",
"ChildData": [
{
"child": "Travel",
"childID": "CAqwuhPIL02T2ufP/Tl0CA=="
},
{
"child": "Table Linen",
"childID": "EMz9RMY4RESyKtvFVAJTVQ"
},
{
"child": "Tools and Gadgets",
"childID": "FQtZRfDqtkGrn2II8HobZw=="
}
]
},
{
"parent": "Hobbies",
"parentID": "yTCm3n8OCUSDQBzDt1f9Hw==",
"ChildData":[
{"child": "Travel",
"childID": "CAqwuhPIL02T2ufP/Tl0CA=="},
{"child": "Camping & Trecking",
"childID": "Kgj+Y1xdiUO2i79hxyDE6w=="}
]
}
how can i do it. I am using following method to get the result from mysql in c# asp.net core
var data = await (from p in _db.ProductCategoryRelationship
join s in _db.Products
on p.ProductId equals s.Id
where p.CategroyId == id
select s).ToListAsync();
is there a way to tweak this method to get the required result or to create another method that can give me my result

Disclaimer: I haven't worked with asp.net-core, so I'm just assuming regular LINQ is available in that environment. I am also doing this from memory.
Assuming you have a way to convert your data into JSON that you haven't shown us, it looks like you have a basic data transformation problem. You need to convert one data structure to another.
And that is where LINQ comes in very handy.
// Group by the parent information, so the output is a sequence of
// collection records with a Key property (what you grouped on).
data2 = data.GroupBy(d => new { parent = d.parent, parentID = d.parentID})
// You can now create an anonymous type that has the parent information
// (as sub-fields in the Key property) and a collection of child
// records.
.Select(g => new { parent = g.Key.parent,
parentID = g.Key.parentID,
ChildData =
g.Select(r => new { r.child,
r.childID }).ToArray()
});
You might want to create a named type that follows this structure (rather than the anonymous type I used), and create it in your LINQ instead (rather than new-ing up an anonymous type).
class ParentType {
string parent { get;set;}
string parentID { get;set; }
ChildType[] ChildData { get;set; }
}
class ChildType {
string child { get;set; }
string childID { get;set }
}
Then the Select LINQ clause would look like this:
.Select(g => new ParentType() {
parent = g.Key.parent,
parentID = g.Key.parentID,
ChildData = g.Select( r => new ChildType() {
child = r.child,
childID = r.childID }).ToArray()
})
.ToArray();

Related

deserialize a dynamic json object to a class

I'm working with an external API to get some product information, the end points return some data in a static structure and others in dynamic depending on the product I'm inquiring.
For example if I'm requesting data for a soap I get the following JSON:
{ "id": 4623,
"brand": "Fa",
"category": "Cleansing/Washing/Soap – Body",
"photos": {
"name": "Photos",
"value": [ "https//test.com/1jpg"
]
},
"productname": {
"name": "Product Name",
"value": "Fa Shower Cream Yoghurt Vanilla Honey"
},
"warningstatement": {
"name": "Warning Statement",
"value": "Avoid contact with eyes."
},
"consumerusageinstructions": {
"name": "Consumer Usage Instructions",
"value": "Apply directly on skin."
}
and if I'm inquiring about a cheese I get the following JSON:
{
"id": 10838,
"brand": "Domty",
"category": "Cheese",
"photos": {
"name": "Photos",
"value": [ "https://test.com/2.jpg"
]
},
"productname": {
"name": "Product Name",
"value": "Domty White Low Salt Cheese"
},
"description": {
"name": "1312",
"value": "Highest premium quality"
},
"netcontent": {
"name": "Net Content",
"value": "900 gm"
}
and it goes on for every product they offer. I've no problem deserializing the static data like photos array, product name, brand, and id since they are applicable to every product, but the other dynamic properties are the ones I'm concerned about. Is there a way to deserialize to a class like this:
public class Info {
property string key { get; set;} // to hold description, consumerusageinstructions or what every key
property string name { get; set;}
property string value { get; set;}
}
and then add a collection of the class info to my product model?
One way is just to parse the Json and look at the actual entities: this example uses Json.Net:
var parsed = JObject.Parse(json);
var properties = parsed.Children().Cast<JProperty>();
foreach (var property in properties) {
// an alternative here would be to just have a list of names to ignore
if (!(property.Value is JObject jObject)) {
// skip the simple property/value pairs
continue;
}
if (property.Name == "productname") {
// skip product name
continue;
}
if (property.Value["value"] is JArray) {
// skip photos
continue;
}
// Add to ProductModel instance
Console.WriteLine($"{property.Name} => {property.Value["name"]} = {property.Value["value"]}");
}
Outputs:
warningstatement => Warning Statement = Avoid contact with eyes.
consumerusageinstructions => Consumer Usage Instructions = Apply directly on skin.
description => 1312 = Highest premium quality
netcontent => Net Content = 900 gm

How do I make JSON accessible from inside of many arrays?

I hope it doesn't seem like I want to be spoon-fed at this rate of asking questions, but this next question is a bit more difficult and I don't understand how to solve this at all.
{
"results": [
{
"id": "5d914028302b840050acbe62",
"picture": "https://utellyassets9-1.imgix.net/api/Images/4e4d50a0040fd4500193202edbafce6a/Redirect",
"name": "BoJack Horseman",
"locations": [
{
"icon": "https://utellyassets7.imgix.net/locations_icons/utelly/black_new/NetflixIVAUS.png?w=92&auto=compress&app_version=ae3576e2-0796-4eda-b953-80cadc8e2619_eww2020-05-08",
"display_name": "Netflix",
"name": "NetflixIVAUS",
"id": "5d81fe2fd51bef0f42268f0f",
"url": "https://www.netflix.com/title/70298933"
}
],
"provider": "iva",
"weight": 5654,
"external_ids": {
"iva_rating": null,
"imdb": {
"url": "https://www.imdb.com/title/tt3398228",
"id": "tt3398228"
},
"tmdb": {
"url": "https://www.themoviedb.org/movie/61222",
"id": "61222"
},
"wiki_data": {
"url": "https://www.wikidata.org/wiki/Q17733404",
"id": "Q17733404"
},
"iva": {
"id": "783721"
},
"gracenote": null,
"rotten_tomatoes": null,
"facebook": null
}
},
{
"id": "5e2ce07890c0e033a487e3d2",
"picture": "https://utellyassets9-1.imgix.net/api/Images/326d2853ff6885c41b9adb05278017f6/Redirect",
"name": "Dragon Ball Z: Bojack Unbound",
"locations": [
{
"icon": "https://utellyassets7.imgix.net/locations_icons/utelly/black_new/iTunesIVAUS.png?w=92&auto=compress&app_version=ae3576e2-0796-4eda-b953-80cadc8e2619_eww2020-05-08",
"display_name": "iTunes",
"name": "iTunesIVAUS",
"id": "5d80a9a5d51bef861d3740d3",
"url": "https://itunes.apple.com/us/movie/dragon-ball-z-bojack-unbound-subtitled-original-version/id1381102560"
}
],
"provider": "iva",
"weight": 0,
"external_ids": {
"iva_rating": null,
"imdb": {
"url": "https://www.imdb.com/title/tt0142238",
"id": "tt0142238"
},
"tmdb": {
"url": "https://www.themoviedb.org/movie/39105",
"id": "39105"
},
"wiki_data": {
"url": "https://www.wikidata.org/wiki/Q1255010",
"id": "Q1255010"
},
"iva": {
"id": "406043"
},
"gracenote": null,
"rotten_tomatoes": null,
"facebook": null
}
}
],
"updated": "2020-05-08T05:19:01+0100",
"term": "Bojack",
"status_code": 200,
"variant": "ivafull"
}
Alright, so the first 0: represents the option that the API returned (so in this case it's bojack horseman) and any following number (if it said 1: afterward) would be a different result.
I tried writing a second class to deal with results (resultoverall) that is controlled by the overall,
public class overall
{
[JsonProperty("status_code")]
public string status_code { get; set; }
[JsonProperty("term")]
public string term { get; set; }
[JsonProperty("updated")]
public string updated { get; set; }
[JsonProperty("results")]
public List<resultoverall> results { get; set; }
}
public partial class resultoverall
{
[JsonProperty("name")]
public string name { get; set; }
}
and tried
overall BestGamer = JsonConvert.DeserializeObject<overall>(thicky);
List<overall> ObjOrderList = JsonConvert.DeserializeObject<List<overall>>(thicky);
to be able to access data from both outside of the 0: and inside of the 0: (BestGamer / overall handles outside, resultoverall / Tapioca handles inside, but I get error about the type requiring a JSON array
even though it is already in an array format.
How do I set this up to access data inside of the 0:, for each number that appears (so if there is 2 options I can access info from both?
Using Console.WriteLine(BestGamer.updated); correctly gives me the info from the overall group, but using the Console.WriteLine(BestGamer.results.name); does not work, saying:
Error CS1061 'extension method 'name' accepting a first argument of type 'List<resultoverall>' could be found (are you missing a using directive or an assembly reference?)
You have a JSON object, not an array. Therefore you can't deserialize it as an array. That is correct. You are already deserializing it to an object. So after
overall BestGamer = JsonConvert.DeserializeObject<overall>(thicky);
You can access the objects in results like
var numberOfResults = BestGamer.results.Count;
var firstResult = BestGamer.results[0];
var firstResultsName = BestGamer.results[0].name;
// or loop through the results
foreach (var item in BestGamer.results)
{
// item.Name gives you the name of the current result
}

NEST How to find field on subdocument

I've a sample company JSON document structure like this:
[
{
"id": "id1",
"name": "company1",
"employees": [
{
"name": "employee1",
"education": "education1"
},
{
"name": "employee2",
"education": "education2"
}
]
}
]
and I'm doing queries like this:
GET companies/_search
{
"query": {
"bool": {
"must": [
{
"term": {
"employees.education": {
"value": "education1"
}
}
},
{
"term": {
"employees.education": {
"value": "education2"
}
}
}
]
}
}
}
The query is build using NEST:
var filters = new List<Func<QueryContainerDescriptor<Company>, QueryContainer>>();
foreach (var education in educationsToFilter)
{
filters.Add(fq => fq.Term(f => f.Employees.Suffix("education"), education));
}
var searchResponse = _client.Search<Company>(s => s
.Query(q => q
.Bool(bq => bq
.Filter(filters)
)
)
);
Is there a better way of finding the Term Field instead of using the Suffix-method? I would like a more type safe way.
You can resolve nested field name with this lambda expression:
fq.Term(f => f.Employees.FirstOrDefault().Education, education)
Hope that helps.

LINQ - Merge parent and child data

I am new to linq and trying to figure this list merge out. I am trying to merge 2 lists of parent/child data as follows:
{
"ParentsList1":
[
{
"Name": "Parent 1",
"Children": [
{
"Name": "Child 1",
},
{
"Name": "Child 2",
}
],
},
{
"Name": "Parent 2",
"Children": [
{
"Name": "Child 1",
}
],
}
]
}
{
"ParentsList2":
[
{
"Name": "Parent 1",
"Children": [
{
"Name": "Child 1",
},
{
"Name": "Child 2",
},
{
"Name": "Child 3",
}
],
},
{
"Name": "Parent 2",
"Children": [
{
"Name": "Child 2",
}
],
}
]
}
---------------Merged Output----------------
{
"MergedParentsList":
[
{
"Name": "Parent 1",
"Children": [
{
"Name": "Child 1",
},
{
"Name": "Child 2",
},
{
"Name": "Child 3",
}
],
},
{
"Name": "Parent 2",
"Children": [
{
"Name": "Child 2",
}
],
}
]
}
I would like the merge to remove/replace child entities. So far I have been stuck working with a union
List<ParentListModel> mergedParentLists = ParentsList1
.Union(ParentsList2)
.GroupBy(grp => new {grp.Name})
.Select(sel => sel.FirstOrDefault())
.ToList();
This seems to get the Parent list ok but cannot seem to sync up the child data...
It sounds like you're considering a person to be identical if they have the same Name, so if you join two lists then any parents who share the same name should be "merged" into one person, and in this process, their children should be merged in the same way (if they both have a child named "Child 1", then after the merge there will be only one "Child 1", but otherwise all children from both instances of the parent exist in the merged instance of the parent).
If this is the case, then I think what you want to do is Select new Person() from your group, where you take the group Key as the person's name, and add the children by using SelectMany on all the children in the group and then taking only the unique ones by using DistinctyBy(child.Name).
For example:
List<ParentListModel> mergedParents = parentList1
.Union(parentList2)
.GroupBy(parent => parent.Name)
.Select(group =>
new ParentListModel
{
Name = group.Key,
Children = group
.SelectMany(parent => parent.Children)
.DistinctBy(child => child.Name)
.ToList()
})
.ToList();
Also, there is some ambiguity in your question, where in the comments you mentioned that during a merge, parentList2 should "win". If by this you mean you want to only add parents from parentList1 that don't exist in parentList2, then you can simply do an AddRange where you add parents whose name doesn't exist in parentList2:
parentList2.AddRange(parentList1.Where(pl1Parent =>
parentList2.All(pl2Parent => pl2Parent.Name != pl1Parent.Name)));
The ambiguity exists because your example holds true for both of these scenarios. It would be better if you included an example that excluded one of these (for example, if Parent 1 in parentList1 had a child named Child 9...would that child exist in the final merge or not?).

Search for a nested value inside of a JSON.net object in C#

I've got a JSON stream coming back from a server, and I need to search for a specific value of the node "ID" using JSON.net to parse the data.
And I can almost make it work, but not quite because the results coming back are deeply nested in each other -- this is due to the fact that I'm getting a folder structure back. I've boiled the JSON down to a much simpler version. I'm getting this:
{
"data": {
"id": 0,
"name": "",
"childFolders": [{
"id": 19002,
"name": "Locker",
"childFolders": [{
"id": 19003,
"name": "Folder1",
"childFolders": [],
"childComponents": [{
"id": 19005,
"name": "route1",
"state": "STOPPED",
"type": "ROUTE"
}]
}, {
"id": 19004,
"name": "Folder2",
"childFolders": [],
"childComponents": [{
"id": 19008,
"name": "comm1",
"state": "STOPPED",
"type": "COMMUNICATION_POINT"
}, {
"id": 19006,
"name": "route2",
"state": "STOPPED",
"type": "ROUTE"
}, {
"id": 19007,
"name": "route3",
"state": "STOPPED",
"type": "ROUTE"
}]
}],
"childComponents": []
}],
"childComponents": []
},
"error": null
}
I can almost get there by going:
var objects = JObject.Parse(results);
var subobjects = objects["data"]["childFolders"][0]["childFolders"][1];
I can see in the debug view that it'll parse the object, but won't let me search within.
My ultimate goal is to be able to search for "route3" and get back 19007, since that's the ID for that route. I've found some results, but all of them assume you know how far nested the object is. The object I'm searching for could be 2 deep or 20 deep.
My ultimate goal is to be able to search for "route3" and get back 19007
You can use linq and Descendants method of JObject to do it:
var dirs = JObject.Parse(json)
.Descendants()
.Where(x=>x is JObject)
.Where(x=>x["id"]!=null && x["name"]!=null)
.Select(x =>new { ID= (int)x["id"], Name = (string)x["name"] })
.ToList();
var id = dirs.Find(x => x.Name == "route3").ID;
You can use the SelectToken or SelectTokens functions to provide a JPath to search for your desired node. Here is an example that would provide you the route based on name:
JObject.Parse(jsonData)["data"].SelectToken("$..childComponents[?(#.name=='route3')]")
You can find more documentation on JPath here
Simply write a recursive function:
private Thing FindThing(Thing thing, string name)
{
if (thing.name == name)
return thing;
foreach (var subThing in thing.childFolders.Concat(thing.childComponents))
{
var foundSub = FindThing(subThing, name);
if (foundSub != null)
return foundSub;
}
return null;
}
class RootObject
{
public Thing data { get; set; }
}
class Thing
{
public int id { get; set; }
public string name { get; set; }
public List<Thing> childFolders { get; set; } = new List<Thing>();
public List<Thing> childComponents { get; set; } = new List<Thing>();
}
And using it:
var obj = JsonConvert.DeserializeObject<RootObject>(jsonString);
var result = FindThing(obj.data, "route3");

Categories

Resources