Combine two JSON structures into one in C# - c#

We have two similar but slightly different JSON structures. First JSON structure is for No Strata scenario – this is applicable for measureId “001” and “002” . Second JSON structure is for multi strata – this is applicable for "measureId": "003". For each testTIN, we need to combine these two measurements as shown in the expected JSON structure.
JSON 1 – No Strata
{
"testTIN": "123",
"measurements": [{
"measureId": "001",
"value": {
"IsEndToEndReported": true,
"PerformanceMet": 5
}
},
{
"measureId": "002",
"value": {
"IsEndToEndReported": true,
"PerformanceMet": 6
}
}
]
}
JSON 2 – Multi Strata
{
"testTIN": "123",
"measurements": [
{
"measureId": "003",
"value": {
"strata": [{
"IsEndToEndReported": true,
"PerformanceMet": 5,
"Stratum": "Level1"
},
{
"IsEndToEndReported": true,
"PerformanceMet": 6,
"Stratum": "Level2"
}
]
}
}
]
}
Expected JSON
{
"testTIN": "123",
"measurements": [{
"measureId": "001",
"value": {
"IsEndToEndReported": true,
"PerformanceMet": 5
}
},
{
"measureId": "002",
"value": {
"IsEndToEndReported": true,
"PerformanceMet": 6
}
},
{
"measureId": "003",
"value": {
"strata": [{
"IsEndToEndReported": true,
"PerformanceMet": 5,
"Stratum": "Level1"
},
{
"IsEndToEndReported": true,
"PerformanceMet": 6,
"Stratum": "Level2"
}
]
}
}
]
}
How to get this new JSON structure combining both the above JSON structures?
C# Code
//No Strata
List<FlattenedRawData> rowList_NoStrata = HelperMethodClasses.GetFlattenedRawData_NoStrata();
List<MeasurementSet__NoStrata> result_NoStrata = rowList_NoStrata.GroupBy(records => records.EffectiveTIN)
.Select(y => new MeasurementSet__NoStrata
{
testTIN = y.Key,
measurements = y.Select(i =>
new Measurement_NoStrata()
{
measureId = i.MeasureID,
value = new QualityMeasureValue_NoStrata
{
IsEndToEndReported = true,
PerformanceMet = i.PerformanceMetCount
}
})
.ToList()
})
.ToList();
//Multi Strata
List<FlattenedRawData> rowList_MultiStrata = HelperMethodClasses.GetFlattenedRawData_MultiStrata();
List<MeasurementSet__MultiStrata> resul_MultiStrata =
rowList_MultiStrata.GroupBy(groupBy1 => groupBy1.EffectiveTIN)
.Select(level1 => new MeasurementSet__MultiStrata
{
testTIN = level1.Key,
measurements = level1.GroupBy(groupBy2 => groupBy2.MeasureID).Select(level2 =>
new Measurement_MultiStrata()
{
measureId = level2.Key,
value = new QualityMeasureValue_MultiStrata()
{
strata = level2.Select(level3 => new Strata
{
IsEndToEndReported = true,
PerformanceMet = level3.PerformanceMetCount,
Stratum = level3.Stratum
}).ToList(),
}
}).ToList()
}).ToList();
string requestJson = Newtonsoft.Json.JsonConvert.SerializeObject(resul_MultiStrata[0]);
public class FlattenedRawData
{
public string EffectiveTIN { get; set; }
public string MeasureID { get; set; }
public int PerformanceMetCount { get; set; }
public string Stratum { get; set; }
}
public class Measurement_NoStrata
{
public string measureId { get; set; }
public QualityMeasureValue_NoStrata value { get; set; }
}
public class Measurement_MultiStrata
{
public string measureId { get; set; }
public QualityMeasureValue_MultiStrata value { get; set; }
}
public class QualityMeasureValue_NoStrata
{
public bool IsEndToEndReported { get; set; }
public int PerformanceMet { get; set; }
}
public class QualityMeasureValue_MultiStrata
{
public List<Strata> strata = new List<Strata>();
}
public class Strata
{
public bool IsEndToEndReported { get; set; }
public int PerformanceMet { get; set; }
public string Stratum { get; set; }
}
public class MeasurementSet__NoStrata
{
public string testTIN { get; set; }
public List<Measurement_NoStrata> measurements { get; set; }
}
public class MeasurementSet__MultiStrata
{
public string category { get; set; }
public string testTIN { get; set; }
public List<Measurement_MultiStrata> measurements { get; set; }
}
Update
Good References:
Newtonsoft Json.Net serialize JObject doesn't ignore nulls, even with the right settings

You can simply do a union (without creating complex POCO classes unless you really need it). Newtonsoft supports merging JSon's:
var dataObject1 = JObject.Parse(#"{
""testTIN"" : ""123"",
""measurements"": [{
""measureId"": ""001"",
""value"": {
""IsEndToEndReported"": true,
""PerformanceMet"": 5
}
},
{
""measureId"": ""002"",
""value"": {
""IsEndToEndReported"": true,
""PerformanceMet"": 6
}
}
]
}");
var dataObject2 = JObject.Parse(#"{
""testTIN"": ""123"",
""measurements"": [
{
""measureId"": ""003"",
""value"": {
""strata"": [{
""IsEndToEndReported"": true,
""PerformanceMet"": 5,
""Stratum"": ""Level1""
},
{
""IsEndToEndReported"": true,
""PerformanceMet"": 6,
""Stratum"": ""Level2""
}
]
}
}
]
}");
dataObject1.Merge(dataObject2, new JsonMergeSettings
{
// union array values together to avoid duplicates
MergeArrayHandling = MergeArrayHandling.Union
});
string json = dataObject1.ToString();
This will give an output:
{
"testTIN": "123",
"measurements": [
{
"measureId": "001",
"value": {
"IsEndToEndReported": true,
"PerformanceMet": 5
}
},
{
"measureId": "002",
"value": {
"IsEndToEndReported": true,
"PerformanceMet": 6
}
},
{
"measureId": "003",
"value": {
"strata": [
{
"IsEndToEndReported": true,
"PerformanceMet": 5,
"Stratum": "Level1"
},
{
"IsEndToEndReported": true,
"PerformanceMet": 6,
"Stratum": "Level2"
}
]
}
}
]
}

If your initial No Strata and multi strata measurement results are, in fact, already serialized as JSON structures, you can simply merge them together using JContainer.Merge(Object, JsonMergeSettings) with the merge setting MergeArrayHandling.Concat as follows:
// Get the initial measurement JSON measurements as strings.
IEnumerable<string> measturements = GetJsonMeasurements();
// And concatenate them together into a combined `JObject`:
var settings = new JsonMergeSettings { MergeArrayHandling = MergeArrayHandling.Concat };
var json = measturements.Aggregate(new JObject(),
(j, s) => { j.Merge(JObject.Parse(s), settings); return j; });
Here I am assuming the measurements have already been grouped by "testTIN" value. If not, this is easily added by parsing all the results to a JObject and grouping by "testTIN" value before aggregating as follows:
var settings = new JsonMergeSettings { MergeArrayHandling = MergeArrayHandling.Concat };
var json = measurements
.Select(j => JObject.Parse(j))
.GroupBy(j => (string)j["testTIN"])
.Select(g => g.Aggregate(new JObject(),
(j1, j2) => { j1.Merge(j2, settings); return j1; })
)
.ToList();
Alternatively, if your JSON results are stored in some collection of files, you can merge directly from the files as follows:
var settings = new JsonMergeSettings { MergeArrayHandling = MergeArrayHandling.Concat };
var json = fileNames.Aggregate(new JObject(),
(j, s) =>
{
using (var file = File.OpenText(s))
using (var reader = new JsonTextReader(file))
{
j.Merge(JToken.Load(reader), settings);
}
return j;
});
Demo fiddle with unit tests here.
Update
if you have an enumerable of objects and wish to create a combined JSON file by merging together their JSON representations, you can project each object to a JObject using JObject.FromObject, then merge those:
// Get the results
IEnumerable<MeasurementSet__NoStrata> measurements1 = GetNoStrataMeasurements(); // Get no-strata measurements.
IEnumerable<MeasurementSet__MultiStrata> measurements2 = GetMultiStrataMeasurements(); // Get multistrata measurements.
// Combine them into a single enumerable
IEnumerable<object> measurements = measurements1.Cast<object>()
.Concat(measurements2.Cast<object>());
// Select serialization and merge settings
var serializerSettings = new JsonSerializerSettings
{
// Put whatever you want here, e.g.
NullValueHandling = NullValueHandling.Ignore,
};
var mergeSettings = new JsonMergeSettings
{
// Required
MergeArrayHandling = MergeArrayHandling.Concat,
// Put whatever you want here, either Ignore or Merge
// https://www.newtonsoft.com/json/help/html/T_Newtonsoft_Json_Linq_MergeNullValueHandling.htm
MergeNullValueHandling = MergeNullValueHandling.Ignore, // Or Merge if you prefer
};
// Serialize and merge the results
var serializer = JsonSerializer.CreateDefault(serializerSettings);
var json = measurements
.Select(m => JObject.FromObject(m, serializer))
.GroupBy(j => (string)j["testTIN"])
.Select(g => g.Aggregate(new JObject(),
(j1, j2) => { j1.Merge(j2, mergeSettings); return j1; })
)
// Do we need to remove the `"category"` property?
// If so do it here.
.Select(o => { o.Remove("category"); return o; })
.ToList();
Demo fiddle #2 here.

Related

JSON .NET Not Deserializing Refs

I have the following minimal example:
class Program
{
static void Main()
{
var plan = new Plan
{
Steps =
{
new Step
{
Contexts =
{
new Context
{
Name = "1"
},
new Context
{
Name = "2"
}
}
}
}
};
var settings = new JsonSerializerSettings
{ PreserveReferencesHandling = PreserveReferencesHandling.Objects, Formatting = Formatting.Indented };
var json = JsonConvert.SerializeObject(plan, settings);
var deserialized = JsonConvert.DeserializeObject<Plan>(json, settings);
}
}
class Plan
{
public IEnumerable AllContexts => Steps.SelectMany(i => i.Contexts);
[JsonProperty(Order = int.MaxValue)]
public ICollection<Step> Steps { get; set; } = new List<Step>();
}
class Step
{
public ICollection<Context> Contexts { get; set; } = new List<Context>();
}
class Context
{
public string Name { get; set; }
}
In this example deserialized has lost its references upon deserialization and deserialized.AllContexts is a collection of 2 null values.
I can get this working by changing [JsonProperty(Order = int.MaxValue)] to [JsonProperty(Order = int.MinValue)] so the Steps are serialized first - but in my scenario I want the actual JSON to have all its properties on the flat AllContexts array and for the Steps to only have $refs like this:
{
"$id": "1",
"AllContexts": [
{
"$id": "2",
"Name": "1"
},
{
"$id": "3",
"Name": "2"
}
],
"Steps": [
{
"$id": "4",
"Contexts": [
{
"$ref": "2"
},
{
"$ref": "3"
}
]
}
]
}
This seems like a bug in JSON .NET - is there a way to work around it?

C# Lambda Expression for iterating through nested objects

I have below JSON which I have de-serialized into a C# object and would like to extract the values of INFO_CARD_ID, DOCUMENT_NUM and REVISION_NM at each iteration.
{
"result": {
"message": "Successfully performed search",
"columnlist": "INFO_CARD_ID,DOCUMENT_NUM,REVISION_NM,
"recordcount": 2,
"rows": [
{
"columns": [
{
"value": "UJ3P25HO3JBPLJZ2HU",
"key": "INFO_CARD_ID"
},
{
"value": "DOC1",
"key": "DOCUMENT_NUM"
},
{
"value": "05",
"key": "REVISION_NM"
}
]
},
{
"columns": [
{
"value": "JWQ5TCIV4JC2BCQ4C5",
"key": "INFO_CARD_ID"
},
{
"value": "DOC2",
"key": "DOCUMENT_NUM"
},
{
"value": "05",
"key": "REVISION_NM"
}
]
}
]
}
}
Here are the C# classes:
public class Columns
{
public string value { get; set; }
public string key { get; set; }
}
public class Rows
{
public IList<Columns> columns { get; set; }
}
public class Result
{
public string message { get; set; }
public string columnlist { get; set; }
public int recordcount { get; set; }
public IList<Rows> rows { get; set; }
}
public class searchOutputModel
{
public Result result { get; set; }
}
So far, I have been trying to extract the information on a list with below expression but with no luck, as the individual value is getting stored on each iteration and not all the values at each iteration at the same time.
searchResponse = JsonConvert.DeserializeObject<searchOutputModel>(json);
var dynamicList = searchResponse.result.rows.SelectMany(x => x.columns).Where(y => y.key.Contains("INFO_CARD_ID") || y.key.Contains("DOCUMENT_NUM") || y.key.Contains("REVISION_NM")).Select(y => {
dynamic myValue;
if (y.key.Contains("INFO_CARD_ID"))
myValue = new { INFO_CARD_ID = y.value };
else if (y.key.Contains("DOCUMENT_NUM"))
myValue = new { DOCUMENT_NUM = y.value };
else
myValue = new { REVISION_NM = y.value };
return myValue;
}).ToList();
Expected output:
INFO_CARD_ID DOCUMENT_NUM REVISION_NM
---------------------------------------------
UJ3P25HO3JBPLJZ2HU DOC1 05
JWQ5TCIV4JC2BCQ4C5 DOC2 05
I know I am doing wrong with iteration as it creates new list at each select, but I am not sure how can I achieve the solution that I am looking for. Any help is appreciated.
We just need to select one object per row, no need for SelectMany here. Each property can be gotten by using columns.FirstOrDefault.
I would advise you to create a proper class for this rather than using dynamic but it makes little difference to the logic below.
Not sure why you used Contains on the column.key check, I've used ==
var dynamicList = searchResponse.result.rows.Select(r =>
new {
INFO_CARD_ID = r.columns.FirstOrDefault(c => c.key == "INFO_CARD_ID")?.value,
DOCUMENT_NUM = r.columns.FirstOrDefault(c => c.key == "DOCUMENT_NUM")?.value,
REVISION_NM = r.columns.FirstOrDefault(c => c.key == "REVISION_NM")?.value,
}).ToList();
If you convert each Rows object to a Dictionary, you can use the Dictionary to create objects:
var ans = searchResponse.result.rows
.Select(r => r.columns.ToDictionary(c => c.key, c => c.value))
.Select(cd => new {
INFO_CARD_ID = cd["INFO_CARD_ID"],
DOCUMENT_NUM = cd["DOCUMENT_NUM"],
REVISION_NM = cd["REVISION_NM"]
});

C# return a single list into nested JSON API

I have a simple list which is returned from my DB to my MVC API, the sample query is
select school, class, pupil from area
With that I am adding to a list
foreach (DataRow row in table.Rows)
{
var area = new Area();
area.school = row.ItemArray[0].ToString();
area.class = row.ItemArray[1].ToString();
area.pupil = row.ItemArray[2].ToString();
returnedlist.Add(area);
}
at the moment the API just returns the list
{
"School": "",
"Class": "",
"Pupil": ""
},
{
"School": "",
"Class": "",
"Pupil": ""
},
However, I would ideally like it returned nested like the following
[
{
School: "Name",
Class: [
ClassName: '',
Pupil: [
PupilName: ''},
PupilName: ''},
PupilName: '']
},
{
School: "Name",
Class: [
ClassName: '',
Pupil: [
PupilName: ''},
PupilName: ''},
PupilName: '']
},
I may have butchered that but you get the general idea.
The class for the data again is very simple
public class Area
{
public string School { get; set; }
public string Class { get; set; }
public string Pupil { get; set; }
}
So far I have tried returning a list in a list etc, but without any luck.
Any help greatly appreciated.
To get the expected result, you could create class with this structure :
1 - Classes
public class NewArea
{
public string School { get; set; }
public List<Class> Classes { get; set; }
}
public class Class
{
public string Name { get; set; }
public List<string> Pupils { get; set; }
}
2 - First, group by school and for each grouped item, group by class:
List<NewArea> newAreas = returnedlist
.GroupBy(x => x.School)
.Select(x => new NewArea
{
School = x.Key,
Classes = x.GroupBy(y => y.Class).Select(z => new Class
{
Name = z.Key,
Pupils = z.Select(w => w.Pupil).ToList()
}).ToList()
}).ToList();
3 - Example for test :
List<Area> returnedlist = new List<Area>
{
new Area{School = "s1", Class="c1",Pupil="p1"},
new Area{School = "s1", Class="c1",Pupil="p2"},
new Area{School = "s1", Class="c2",Pupil="p1"},
new Area{School = "s1", Class="c2",Pupil="p2"},
new Area{School = "s2", Class="c1",Pupil="p1"},
};
Result
[
{
"School":"s1",
"Classes":[
{
"Name":"c1",
"Pupils":[
"p1",
"p2"
]
},
{
"Name":"c2",
"Pupils":[
"p1",
"p2"
]
}
]
},
{
"School":"s2",
"Classes":[
{
"Name":"c1",
"Pupils":[
"p1"
]
}
]
}
]
Namespaces :
using System.Linq;
using System.Collections.Generic;

How use linq multi list criteria?

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)

How can I use regex to extract child node from JSON structure?

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

Categories

Resources