I have some JSON data that looks like this:
[
{
"name": "foo",
"id": 1,
"child": {
name: "A",
id: 1,
}
},
{
"name": "bar",
"id": 1,
"child": {
name: "A",
id: 1,
}
},
{
"name": "baz",
"id": 2,
"child": {
name: "B",
id: 2,
}
},
{
"name": "alpha",
"id": 1,
"child": {
name: "A",
id: 1,
}
}
]
I'm loading it into my .NET project and trying to restructure it so that it returns a response that looks something like this:
[
{
"name": "A"
"id": 1,
"parents": [
{
"name": "foo",
"id": 1,
},
{
"name": "bar",
"id": 1,
},
{
"name": "alpha",
"id": 1,
}
]
},
{
"name": "B"
"id": 2,
"parents": [
{
"name": "baz",
"id": 2,
}
]
}
]
I was able to use LINQ to grab all the IDs that matched, but only for 1 ID.
var test = deserializedJsonData.Where(w => w.child.id == SomeID).Select(s => s).ToList()
The moment I tried to iterate "SomeID" inside of a loop to iterate it, it freezes and crashes.
Is there a more efficient way of doing this?
EDIT: Corrected typo as pointed out in comments
Regarding your requirement, I've created the objects.
public class Base
{
public int id { get; set; }
public string name { get; set; }
}
public class Data : Base
{
public Base child { get; set; }
}
public class RequiredData : Base
{
public List<Base> parents { get; set; }
}
Then created a comparer to distinct your child objects.
public class BaseComparer : IEqualityComparer<Base>
{
public bool Equals(Base x, Base y)
{
if (x.id.Equals(y.id) && (x.name.Equals(y.name)))
{
return true;
}
else
{
return false;
}
}
public int GetHashCode(Base obj)
{
unchecked
{
int hash = 100;
hash = hash * 10 + obj.id.GetHashCode();
hash = hash * 10 + obj.name.GetHashCode();
return hash;
}
}
}
Finally,
string rawData = File.ReadAllText(#"your_json_file_path");
var parsedData = JsonConvert.DeserializeObject<Data[]>(rawData);
var childs = parsedData.GroupBy(g => g.child.id).Select(s => new Base()
{
id = s.Key,
name = s.Where(w => w.child.id == s.Key).First().child.name
}).ToList();
var result = parsedData.GroupBy(s => s.child).Select(s =>
new RequiredData()
{
id = s.Key.id,
name = s.Key.name,
parents = parsedData.Where(w => w.child.id == s.Key.id).Select(x =>
new Base()
{
id = x.id,
name = x.name
}).ToList()
}).Distinct(new BaseComparer()).ToList();
var jsonResult = JsonConvert.SerializeObject(result);
jsonResult will contain your required output.
Note: I'm using Newtonsoft.Json library for JSON operations.
Related
I have a model class that allows users to specify the template which is used to create content with specified properties.
public class Model
{
public string Id { get; set; }
public string Alias { get; set; }
public ICollection<Field> Fields { get; set; }
}
public class Field
{
public FieldType Type { get; set; }
public string Alias { get; set; }
}
public enum FieldType
{
String = 1,
Email = 2,
Password = 3,
DateTime = 4,
Integer = 5,
Decimal = 6,
...
}
The content that is created based on this template will have the structure as below:
public class Entry
{
public string _id { get; set; }
public List<FieldValue> FieldValues { get; set; }
}
public class FieldValue
{
public object Value { get; set; }
public string Alias { get; set; }
public FieldType Type { get; set; }
}
So, I for example, can have data such as:
PersonModel
{
"id": "34752b15",
"alias": "person",
"fields": [
{
"type": 1,
"alias": "name"
},
{
"type": 2,
"alias": "email"
}
]
}
PersonEntries:
[
{
"_id": "426e",
"fieldValues": [
{
"alias": "name",
"value": "John",
"type": 1
},
{
"alias": "email",
"value": "john#stark.com",
"type": 2
}
]
},
{
"_id": "c6110fc3cc0e",
"fieldValues": [
{
"alias": "name",
"value": "Arya",
"type": 1
},
{
"alias": "email",
"value": "arya#stark.com",
"type": 2
}
]
}
]
Or:
ShapeModel
{
"id": "afd69af4",
"alias": "shape",
"fields": [
{
"type": 1,
"alias": "name"
},
{
"type": 6,
"alias": "area"
},
{
"type": 6,
"alias": "perimeter"
}
]
}
ShapeEntries:
[
{
"_id": "426e",
"fieldValues": [
{
"alias": "name",
"value": "my-square",
"type": 1
},
{
"alias": "area",
"value": 1,
"type": 6
},
{
"alias": "perimeter",
"value": 4,
"type": 6
}
]
},
{
"_id": "426enbv",
"fieldValues": [
{
"alias": "name",
"value": "my-triangle",
"type": 1
},
{
"alias": "area",
"value": 0.5,
"type": 6
},
{
"alias": "perimeter",
"value": 3,
"type": 6
}
]
}
]
and so on.
The idea is to create graphql schemas dynamically (on runtime) when a new model is created and allow users to query entries based on the model alias.
What I tried is:
public ISchema CreateEntrySchema(List<Entry> entries, Model model, IServiceCollection services)
{
var entryType = new ObjectGraphType() { Name = model.Alias };
entryType.Field("_id", new IdGraphType());
foreach (var field in model.Fields)
{
if (entryType.Fields.FirstOrDefault(f => f.Name == field.Alias) != null)
{
continue;
}
Type clrGraphType = field.Type.GetStandardType().GetGraphTypeFromType(isNullable: true);
IGraphType graphType = (IGraphType)Activator.CreateInstance(clrGraphType);
entryType.Field(field.Alias,
graphType,
resolve: context =>
{
var entry = (Entry)context.Source;
object firstOrDefault = entry?.FieldValues.FirstOrDefault(value => value.Alias == context.FieldDefinition.Name)?.Value;
return firstOrDefault;
});
}
var queryType = new ObjectGraphType();
queryType.Field(
model.Alias,
type: new ListGraphType(entryType),
arguments: new QueryArguments(ConstructArguments(model.Fields)),
resolve: context =>
{
List<KeyValuePair<string, ArgumentValue>> args = context.Arguments?.Where(pair => pair.Value.Value != null).ToList();
if (!args?.Any() ?? true)
{
return entries;
}
return args.Aggregate(entries, (current, pair) =>
current.Where(entry => entry.FieldValues.FirstOrDefault(field => field.Alias == pair.Key) != null && entry.FieldValues.FirstOrDefault(field => field.Alias == pair.Key).Value.Equals(pair.Value.Value))
.ToList());
});
ISchema schema = new Schema { Query = queryType };
schema.RegisterType(entryType);
services.AddGraphQL(a =>
{
a.AddSchema(schema).AddClrTypeMappings();
});
return schema;
}
The problem is that it is registering only the last schema. So, for example, if I will call this method like:
CreateEntrySchema(personEntries, personModel, services);
CreateEntrySchema(shapeEntries, shapeModel, services);
And query like this:
{
shape{
name
}
person{
name
}
}
The result is:
"GraphQL.Validation.Errors.FieldsOnCorrectTypeError: Cannot query field 'person' on type 'Object'. Did you mean 'shape'?",
Given 2 JSON strings:
[
{
"id":"BA",
"description":"BrandA",
"values":[
{
"id":"CategoryA",
"description":"CategoryA"
},
{
"id":"CategoryB",
"description":"CategoryB"
},
{
"id":"CategoryC",
"description":"CategoryC"
},
{
"id":"CategoryD",
"description":"CategoryD"
},
{
"id":"CategoryE",
"description":"CategoryE"
},
{
"id":"CategoryF",
"description":"CategoryF"
},
{
"id":"CategoryG",
"description":"CategoryG"
},
{
"id":"CategoryH",
"description":"CategoryH"
}
]
},
{
"id":"BB",
"description":"BrandB",
"values":[
{
"id":"CategoryA",
"description":"CategoryA"
},
{
"id":"CategoryB",
"description":"CategoryB"
},
{
"id":"CategoryC",
"description":"CategoryC"
}
]
}
]
AND
[
{
"id":"BA",
"description":"BrandA",
"values":[
{
"id":"CategoryA",
"description":"CategoryA"
},
{
"id":"CategoryC",
"description":"CategoryC"
}
]
},
{
"id":"BB",
"description":"BrandB",
"values":[
{
"id":"CategoryB",
"description":"CategoryB"
}
]
}
]
First one is the original. The second are the values that I want to remove from the original. So basically, if there is a match on brand and category between first and second JSON, regardless of the order of the elements, I want that match to be removed.
The expected result would be someting like this:
[
{
"id":"BA",
"description":"BrandA",
"values":[
{
"id":"CategoryB",
"description":"CategoryB"
},
{
"id":"CategoryD",
"description":"CategoryD"
},
{
"id":"CategoryE",
"description":"CategoryE"
},
{
"id":"CategoryF",
"description":"CategoryF"
},
{
"id":"CategoryG",
"description":"CategoryG"
},
{
"id":"CategoryH",
"description":"CategoryH"
}
]
},
{
"id":"BB",
"description":"BrandB",
"values":[
{
"id":"CategoryA",
"description":"CategoryA"
},
{
"id":"CategoryC",
"description":"CategoryC"
}
]
}
]
Catagory A and C in Brand A were removed as well as Category B in Brand B.
Based in some research, I was using https://github.com/wbish/jsondiffpatch.net, tried to work with it's functions, but so far I didn't manage to achieve the result I want. Also, to solve this by processing JSON direcly is not a must. If there is a simpler solution to achieve that by converting them to lists and use something like LINQ for example, it works for me as well (tried that, but didn't manage to find a way to do this comparison).
Thanks in advance.
If performance does not matter, the JsonSerializer and LINQ can be used:
Model
public class JsonModel
{
public class ValueModel
{
public string Id { get; set; }
public string Description { get; set; }
}
public string Id { get; set; }
public string Description { get; set; }
public IEnumerable<ValueModel> Values { get; set; }
}
Deserialize and LINQ
string json1Str = #"[...]";
string json2Str = #"[...]";
var opt = new System.Text.Json.JsonSerializerOptions { PropertyNameCaseInsensitive = true };
var json1 = System.Text.Json.JsonSerializer.Deserialize<List<JsonModel>>(json1Str, opt);
var json2 = System.Text.Json.JsonSerializer.Deserialize<List<JsonModel>>(json2Str, opt);
var result = json1.
Join(json2, j1 => j1.Id, j2 => j2.Id, (j1, j2) => new JsonModel
{
Id = j1.Id,
Description = j1.Description,
Values = j1.Values.Where(j1 => !j2.Values.Select(val => val.Id).Contains(j1.Id))
});
Console.WriteLine(System.Text.Json.JsonSerializer.Serialize(result, new System.Text.Json.JsonSerializerOptions { WriteIndented = true }));
}
Result
[
{
"Id": "BA",
"Description": "BrandA",
"Values": [
{
"Id": "CategoryB",
"Description": "CategoryB"
},
{
"Id": "CategoryD",
"Description": "CategoryD"
},
{
"Id": "CategoryE",
"Description": "CategoryE"
},
{
"Id": "CategoryF",
"Description": "CategoryF"
},
{
"Id": "CategoryG",
"Description": "CategoryG"
},
{
"Id": "CategoryH",
"Description": "CategoryH"
}
]
},
{
"Id": "BB",
"Description": "BrandB",
"Values": [
{
"Id": "CategoryA",
"Description": "CategoryA"
},
{
"Id": "CategoryC",
"Description": "CategoryC"
}
]
}
]
I have the following data returned from an API and I am trying to use linq to re-shape the data.
Is it actually possible to do this and get the below expected result.
"results": [
{
"Description": "Describe1",
"Cost": 5.00,
"Category": [
"Online",
"Games"
]
},
{
"Description": "Describe2",
"Cost": 4.00,
"Category": [
"Online",
"Games"
]
},
{
"Description": "Describe3",
"Cost": 3.00,
"Category": [
"Online",
"Grocery"
]
},
{
"Description": "Describe4",
"Cost": 3.00,
"Category": [
"Transport",
"Bus"
]
},
{
"Description": "Describe5",
"Cost": 3.00,
"Category": [
"Transport",
"Bus"
]
},
{
"Description": "Describe5",
"Cost": 10.00,
"Category": [
"Transport",
"Train"
]
}
}
The Final output I am trying to achieve from the above:
{ name : "Online",
data: [
{
name: Games,
value: 9.00
},
{
name : Grocery,
value: 3.00
}],
name : "Transport",
data: [
{
name: Bus,
value: 6.00
},
{
name : Train,
value: 10.00
}],
}
Grouping by FirstOrDefault is all easy enough for first level stats, but I can't see where to start for the sub-grouping!
Thanks in advance.
This should work:
class ResultItem {
public string Name { get; set; }
public double Value { get; set; }
}
class ResultGroup {
public string Name { get; set; }
public ResultItem[] Data { get; set; }
}
var results = items
.GroupBy(x => x.Category[0])
.Select(g => new ResultGroup {
Name = g.Key,
Data = g
.GroupBy(x => x.Category[1])
.Select(g2 => new ResultItem { Name = g2.Key, Value = g2.Sum(x => x.Cost) })
.ToArray()
});
Answering your question in two parts,
Part 1
The first part would consists of few steps
Deserialize existing Json to Custom Data Structures
Modify and create new data structures
The above steps could be achieved with Linq as following.
var parsedData = JsonConvert.DeserializeObject<RootObject>(str).Results.GroupBy(x=> x.Category.First())
.Select(x=>
new {
name= x.Key,
data = x.GroupBy(c=>c.Category.Last())
.Select(c=> new {value=c.Sum(f=>f.Cost),name=c.Key})
});
Where RootObject is defined as
public class Result
{
public string Description { get; set; }
public double Cost { get; set; }
public List<string> Category { get; set; }
}
public class RootObject
{
[JsonProperty("results")]
public List<Result> Results { get; set; }
}
Part 2
The second part has to be executed depended how you want the Json to be formated. If you observe the expected result given in OP, that doesn't look like a valid Json. Simplifying it for representational purposes, the Json string looks like following
{
name : "Online",
data: [....],
name : "Transport",
data: [...],
}
As observed, the name and data fields are duplicated within json. If you would like to have result in this particular manner, you need to Serialize the Json recieved in Part 1, and modify it using string manipulation. For example,
var jsonCollection = parsedData.Select(x=> Regex.Replace(JsonConvert.SerializeObject(x,Newtonsoft.Json.Formatting.Indented),#"^{|}$",string.Empty));
var finalResult = $"{{{string.Join(",",jsonCollection)}}}";
Output
{
"name": "Online",
"data": [
{
"value": 9.0,
"name": "Games"
},
{
"value": 3.0,
"name": "Grocery"
}
]
,
"name": "Transport",
"data": [
{
"value": 6.0,
"name": "Bus"
},
{
"value": 10.0,
"name": "Train"
}
]
}
If your desired result needs to be a valid json, then you could ensure it is an array. For example,
[
{
name : "Online",
data: [....]
},
{
name : "Transport",
data: [...],
}
]
Then you could serialize the result you got in Part 1 directly.
var finalResult = JsonConvert.SerializeObject(parsedData, Newtonsoft.Json.Formatting.Indented);
Output
[
{
"name": "Online",
"data": [
{
"value": 9.0,
"name": "Games"
},
{
"value": 3.0,
"name": "Grocery"
}
]
},
{
"name": "Transport",
"data": [
{
"value": 6.0,
"name": "Bus"
},
{
"value": 10.0,
"name": "Train"
}
]
}
]
Demo Code
You can achieve it in this simple way, Live demo here
var parsedJsonObject = JsonConvert.DeserializeObject<List<ObjectName>>(jsonObject);
var normalizedData = parsedJsonObject.SelectMany(pParent => pParent.Category, (pParent, pCategory) =>
new { pParent, pCategory }).Select(ParentAndCategory =>
new
{
Cost = ParentAndCategory.pParent.Cost,
Category = ParentAndCategory.pCategory,
}).ToList();
var aggregatedData = new List<ObjectName2>();
for(int i = 0; i < (normalizedData.Count - 1);)
{
aggregatedData.Add(new ObjectName2{ Cost = normalizedData[i].Cost, Category1 = normalizedData[i].Category, Category2 = normalizedData[i + 1].Category });
i += 2;
}
var result = aggregatedData.GroupBy(p => p.Category1)
.Select(g => new
{
name = g.Key,
data = g.GroupBy(p => p.Category2).Select(g2 =>
new { name = g2.Key, value = g2.Sum(p2 => p2.Cost) })
}).ToList();
foreach(var item in result)
Console.WriteLine(JsonConvert.SerializeObject(item));
Output
{"name":"Online","data":[{"name":"Games","value":9.00},{"name":"Grocery","value":3.00}]}
{"name":"Transport","data":[{"name":"Bus","value":6.00},{"name":"Train","value":10.00}]}
i want order multi list according to the condition by use other api.
Result (i use from other api)
{
"returned_data": {
"data": [
{
"firstName": "FirstNameAA",
"lastName": "LastNameAA",
"product": [
{
"license": "1AS131",
"carType": "478",
"contract": "0112345",
"amounttoCurrent": 3000
}
]
},
{
"firstName": "FirstNameAA",
"lastName": "LastNameAA",
"product": [
{
"license": "2AS345",
"carType": "465",
"contract": "10234521",
"amounttoCurrent": 12000
}
]
},
{
"firstName": "FirstNameBB",
"lastName": "LastNameBB",
"product": [
{
"license": "kdf9034",
"carType": "4234",
"contract": "8995412",
"amounttoCurrent": 1000
}
]
}
]
}
}
But I want to new result, new order each list by "firstName"
{
"returned_data": {
"data": [
{
"firstName": "FirstNameAA",
"lastName": "LastNameAA",
"product": [
{
"license": "1AS131",
"carType": "478",
"contract": "0112345",
"amounttoCurrent": 3000
},
{
"license": "2AS345",
"carType": "465",
"contract": "10234521",
"amounttoCurrent": 12000
}
]
},
{
"firstName": "FirstNameBB",
"lastName": "LastNameBB",
"product": [
{
"license": "kdf9034",
"carType": "4234",
"contract": "8995412",
"amounttoCurrent": 1000
}
]
}
]
}
}
Code c#
var newResult = resReturnListData.returned_data.data.GroupBy(x => x.firstName); >>> not work.
Please help me. thanks in advane.
Assuming your data structures are as follows,
public class Product
{
public string license { get; set; }
public string carType { get; set; }
public string contract { get; set; }
public int amounttoCurrent { get; set; }
}
public class Datum
{
public string firstName { get; set; }
public string lastName { get; set; }
public List<Product> product { get; set; }
}
public class ReturnedData
{
public List<Datum> data { get; set; }
}
public class RootObject
{
public ReturnedData returned_data { get; set; }
}
You can GroupBy to get the "data" result, and then, wrap it around using an anonymous object.
var resReturnListData = JsonConvert.DeserializeObject<RootObject>(jsonString);
var newResult = resReturnListData.returned_data.data
.GroupBy(x => x.firstName)
.Select(x => new Datum
{
firstName = x.Key,
lastName = x.Select(c => c.lastName).FirstOrDefault(),
product = x.SelectMany(c => c.product).ToList()
});
var finalObject = new RootObject
{
returned_data = new ReturnedData
{
data = newResult.ToList()
}
};
var jsonResult = JsonConvert.SerializeObject(finalObject,Newtonsoft.Json.Formatting.Indented);
Output Sample,
{
"returned_data": {
"data": [
{
"firstName": "FirstNameAA",
"lastName": "LastNameAA",
"product": [
{
"license": "1AS131",
"carType": "478",
"contract": "0112345",
"amounttoCurrent": 3000
},
{
"license": "2AS345",
"carType": "465",
"contract": "10234521",
"amounttoCurrent": 12000
}
]
},
{
"firstName": "FirstNameBB",
"lastName": "LastNameBB",
"product": [
{
"license": "kdf9034",
"carType": "4234",
"contract": "8995412",
"amounttoCurrent": 1000
}
]
}
]
}
}
You need to use proper group by and then select only product from group by result,
var newResult = resReturnListData.returned_data.data
.GroupBy(x => x.firstName)
.Select(g => new
{
firstName = g.Key,
lastName = g.Select(x => x.lastName).FirstOrDefault(),
product = g.SelectMany(x => x.product).ToList()
}).ToList();
If you want to group by your data with firstName and lastName then,
var newResult = resReturnListData.returned_data.data
.GroupBy(x => new { x.firstName, x.lastName })
.Select(g => new
{
firstName = g.Key.firstName,
lastName = g.Key.lastName,
product = g.SelectMany(x => x.product).ToList()
}).ToList();
Usage:
string json = "Your json here";
JObject jObject = JObject.Parse(json);
RootObject resReturnListData = jObject.ToObject<RootObject>();
jObject["returned_data"]["data"] = JToken.FromObject(newResult); //<= newResult comes from either one of above linq group by result
string newJson = jObject.ToString();
Console.WriteLine(newJson);
Output: (From Console)
Hi I try to get json data inside of the json. But my class is Employee my service creates json as com.myteam.rbffiyatlama2.Employee this prefix can be changeable so I have to write a solution to get an exact part of the json Like below but my below code is not working. I will send my node name to a method Getjsonobject(Employee emp) or Getjsonobject(Customer cust) or Getjsonobject(Student student) etc.
My Json:
{
"type": "SUCCESS",
"msg": "Container RBFFiyatlama2_1.0.1 successfully called.",
"result": {"execution-results": {
"results": [
{
"value": 2,
"key": ""
},
{
"value": {"com.myteam.rbffiyatlama2.Employee": {
"salary": 2400,
"age": 35,
"cofactor": 0.2
}},
"key": "t1"
},
{
"value": {"com.myteam.rbffiyatlama2.Employee": {
"salary": 4800,
"age": 35,
"cofactor": 0.2
}},
"key": "t2"
}
],
"facts": [
{
"value": {"org.drools.core.common.DefaultFactHandle": {"external-form": "0:50:1980606587:1980606587:100:DEFAULT:NON_TRAIT:com.myteam.rbffiyatlama2.Employee"}},
"key": "t1"
},
{
"value": {"org.drools.core.common.DefaultFactHandle": {"external-form": "0:51:2052360932:2052360932:99:DEFAULT:NON_TRAIT:com.myteam.rbffiyatlama2.Employee"}},
"key": "t2"
}
]
}}
}
class Program
{
static void Main(string[] args)
{
var employee1 = new Employee() { age = 35, cofactor = 0.2, salary = 2000 };
var employee2 = new Employee() { age = 35, cofactor = 0.2, salary = 4000 };
var list = new List<Employee>();
list.Add(employee1);
list.Add(employee2);
var uri = new Uri("http://localhost:8080/kie-server/services/rest/server/containers/instances/RBFFiyatlama2_1.0.1");
var kieclient = new KieRequestWrapper<Employee>(uri, "kieserver", "#test2018", MethodType.POST, "application/json").Add(list).Run();
Console.Write(kieclient.Content);
var match = Regex.Match(kieclient.Content, #"(?*.Employee{*})");
var result= MyParser.Parse(match, typeof(Employee)); //Desired
Console.Read();
}
}
public class Employee
{
public int age { get; set; }
public double cofactor { get; set; }
public int salary { get; set; }
}
You don't want to use XPath to get the data you need, you want to deserialize the the JSON string into an object and then get the data you need. There are many JSON serialization libraries out there, the most common one, AFAIK, is JSON.NET. You can look at how deserialization works here: https://www.newtonsoft.com/json/help/html/DeserializeObject.htm
Example:
public class Account
{
public string Email { get; set; }
public bool Active { get; set; }
public DateTime CreatedDate { get; set; }
public IList<string> Roles { get; set; }
}
string json = #"{
'Email': 'james#example.com',
'Active': true,
'CreatedDate': '2013-01-20T00:00:00Z',
'Roles': [
'User',
'Admin'
]
}";
Account account = JsonConvert.DeserializeObject<Account>(json);
Console.WriteLine(account.Email);
// james#example.com