Json nested objects deserialization - c#

I would like to dynamically create c# object based on the the consumed Json. I do not care about this json but simply would like pick out properties that I need.
I dont know if its best practice to use dynamic keyword and based on the created object I can create a whole new json structure.
How can I desearilize nested objects. Example json:
{
'name': 'James',
'Profile': {
'body': {
'id': 'Employee1',
'type': 'Employee',
'attributes': {
props...
},
'job': {
props..
},
'team': [],
},
}
Here is a simple attempt.
var test = JsonConvert.DeserializeObject<dynamic>(json);
Console.WriteLine(test.ToString());
I can create object list however when I begin Parent/Child objects I cant seem to get it to work.
Below JSON works fine as an output:
'body': {
'id': 'Employee1',
'type': 'Employee',
'attributes': {
props...
},
'job': {
props..
},
'team': [],
}
Desired Output
{
'name': 'James',
'Profile': {
'body': {
'id': 'Employee1',
'type': 'Employee',
'attributes': {
props...
},
'job': {
props..
},
'team': [],
},
'Profile': {
'body': {
'id': 'Employee2',
'type': 'Employee',
'attributes': {
props...
},
'job': {
props..
},
'team': [],
}
}
How can I deserialize nested objects?

You can use the json class with following this line of code
dynamic data = Json.Decode(json);

You can use JsonConvert:
dynamic myNewObject = JsonConvert.DeserializeObject(json);
And then, you can access json properties like this:
myNewObject.Profile.body.id

No repro. The string is already deserialized. test contains the entire graph. The expected output is produced as long as a valid JSON string is used :
This snippet :
var json=#"{
'name': 'James',
'Profile': {
'body': {
'id': 'Employee1',
'type': 'Employee',
'attributes': {
'a':3
},
'job': {
'a':3
},
'team': [],
},
}";
var x=JsonConvert.DeserializeObject<object>(json);
Console.WriteLiine(x.ToString());
Produces :
{
"name": "James",
"Profile": {
"body": {
"id": "Employee1",
"type": "Employee",
"attributes": {
"a": 3
},
"job": {
"a": 3
},
"team": []
}
}
}
From the comments though, it looks like the actual JSON string isn't a valid JSON string and throws an exception :
I get Newtonsoft.Json.JsonReaderException: 'Additional text encountered after finished reading JSON content: ,. Path '', line 21, position 13.'
This error points to the actual location in the JSON string that caused the problem. Perhaps there's an extra character there? Perhaps the text contains multiple JSON strings instead of one? The following string is not valid JSON - JSON can't have more than one root objects :
{ "a":"b"}
{ "c":"d"}
Logging tools, event processing and analytic software though store multiple JSON strings per file this way because it only needs an append operation, doesn't require parsing the entire text to produce results and makes partitioning easier. That's still invalid JSON though that has to be read and processed one line at a a time.
You many find this described as "streaming JSON", precisely because you can process the data in a streaming fashion. Instead of loading and parsing a 100MB event file, you can load and process a single line at a time eg:
IEnumerable<string> lines=File.ReadLines(pathToBigFile);
foreach(var line in lines)
{
var myObject=JsonConvert.DeserializeObject<someType>(line);
//do something with that object
}
File.ReadLines returns an IEnumerable<string> that only loads a single line on each iteration. Instead of loading the entire 100MB file, only one line is loaded and parsed each time.

Related

Add a list in an existing JSON value

So I have below call to a method where my argument is a json string of this type
var jsonWithSearchData = await querySearchData(jsonOut);
jsonOut ->
[
{
"data": {
"_hash": null,
"kind": "ENY",
"id": "t123",
"payload": {
"r:attributes": {
"lok:934": "#0|I"
},
"r:relations": {
"lok:1445": "15318",
"lok:8538": "08562"
},
"r:searchData": "",
"r:type": [
"5085"
]
},
"type": "EQT",
"version": "d06"
}
}
]
The querySearchData() returns me two list something like this :["P123","P124","P987"] and ["Ba123","KO817","Daaa112"]
I want to add this list in my r:searchData key above. The key inside my searchData i.e. r:Porelation and ClassA and ClassB remains static. So I would like my searchData in my input Json to finally become something like this.
"r:searchData": {
"r:Porelation":{
"ClassA": ["P123","P124","P987"],
"ClassB": ["Ba123","KO817","Daaa112"]
}
},
How can I do this? What I tried:
JArray jfinObject = JArray.Parse(jobjects);
jfinObject["r:searchData"]["r:relations"]["ClassA"] = JArray.Parse(ListofCode.ToString());
And I get below error:
System.Private.CoreLib: Exception while executing function: Function1.
Newtonsoft.Json: Accessed JArray values with invalid key value:
"r:searchData". Int32 array index expected.
There are a few ways you can add a node/object/array to existing json.
One option is to use Linq-to-Json to build up the correct model.
Assuming you have the json string described in your question, the below code will add your desired json to the r:searchData node:
var arr = JArray.Parse(json); // the json string
var payloadNode = arr[0]["data"]["payload"];
// use linq-to-json to create the correct object
var objectToAdd = new JObject(
new JProperty("r:Porelation",
new JObject(
new JProperty("r:ClassA", array1),
new JProperty("r:ClassB", array2))));
payloadNode["r:searchData"] = objectToAdd;
where array1 and array2 above could come from a linq query (or just standard arrays).
// Output:
{
"data": {
"_hash": null,
"kind": "ENY",
"id": "t123",
"payload": {
"r:attributes": {
"lok:934": "#0|I"
},
"r:relations": {
"lok:1445": "15318",
"lok:8538": "08562"
},
"r:searchData": {
"r:Porelation": {
"r:ClassA": [
"P123",
"P456"
],
"r:ClassB": [
"Ba123",
"Ba456"
]
}
},
"r:type": [
"5085"
]
},
"type": "EQT",
"version": "d06"
}
}
Online demo
Another option is to create the json from an object, which could be achieved using JToken.FromObject(). However, this will only work if you have property names which are also valid for C# properties. So, this won't work for your desired property names as they contain invalid characters for C# properties, but it might help someone else:
// create JToken with required data using anonymous type
var porelation = JToken.FromObject(new
{
ClassA = new[] { "P123", "P456" }, // replace with your arrays here
ClassB = new[] { "Ba123", "Ba456" } // and here
});
// create JObject and add to original array
var newObjectToAdd = new JObject(new JProperty("r:Porelation", porelation));
payloadNode["r:searchData"] = newObjectToAdd;

C# How to combine multiple JSON arrays that are part of a List?

What has been done so far?
I am working on dividing up the number of records into 3 batches and processing them in parallel to increase the performance. However, after processing the batches in parallel I would also like to save the outcome (JSON string) of the processed records in a variable.
As you can see below, I first initialize the variable as List of string and then run the foreach loop which saves the processed outcome as mentioned below.
List<string> responseOutcome = new List<string>();
Parallel.ForEach(recordBatches, batch => {
responseOutcome.Add(response1.Content);
});
Result in List responseOutcome comes as:
responseOutcome[0]
[
{
"Name": "Sample1",
"ID": "123"
},
{
"Name": "Sample2",
"ID": "394"
}
],
responseOutcome[1]
[
{
"Name": "Sample5",
"ID": "384"
},
{
"Name": "Sample6",
"ID": "495"
}
],
responseOutcome[2]
[
{
"Name": "Sample3",
"ID": "473"
},
{
"Name": "Sample4",
"ID": "264"
}
]
What I would like to achieve?
Now I would like to take the value of responseOutcome which is multiple arrays of JSON string and merge them into one big JSON string.
Final Output
[
{
"Name": "Sample1",
"ID": "123"
},
{
"Name": "Sample2",
"ID": "394"
},
{
"Name": "Sample5",
"ID": "384"
},
{
"Name": "Sample6",
"ID": "495"
},
{
"Name": "Sample3",
"ID": "473"
},
{
"Name": "Sample4",
"ID": "264"
}
]
I looked into several similar cases but they weren't nearly similar. Like:
How do I merge multiple json objects
How do I combine two arrays from two JObjects in Newtonsoft JSON.Net?
Any help/guidance will be great!!
Using Newtonsoft, you can create a JArray from each of your responses. Then you can flatten the hierarchy using linq's SelectMany method and re-serialize the object.
Try this:
var obj = responses.Select(r => JArray.Parse(r.Trim(','))).SelectMany(token => token);
string json = JsonConvert.SerializeObject(obj, Formatting.Indented);
There are probably more efficient ways to do this if you want to do pure string manipulation, but using Newtonsoft, I would deserialize, merge and then re-serialize.
Create a small POCO model:
public class ResponseOutcomeModel
{
public string ID { get; set; }
public string Name { get; set; }
}
Then deserialize to this model, merge and reserialize to JSON as a single list.
var outcomeList = new List<ResponseOutcomeModel>();
foreach (var i in responseOutcome)
{
outcomeList.AddRange(JsonConvert.DeserializeObject<List<ResponseOutcomeModel>>(i.Trim().TrimEnd(',')));
}
var finalJson = JsonConvert.SerializeObject(outcomeList);
Note, the Time/TrimEnd is used if the trailing commas in your example are really there in your responseOutcome array (at the end of each element in the array). The call to DeserializeObject will complain if you leave the commas in there.

How to get sys_id from the result(incident_response) from below c# code

I have the following JSON(modified) which is read from a HttpWebRequest response stream, and I want to extract the value of "sys_id":
{
"result": {
"number": "INC0012618",
"opened_by": {
"link": "//XXX.service-now.com/api/now/v1/table/sys_user/38723e5ddb42f200deb8f6fcbf96196d",
"value": "38723e5ddb42f200deb8f6fcbf96196d"
},
"sys_created_on": "2017-07-20 14:41:52",
"sys_domain": {
"link": "://XXX.service-now.com/api/now/v1/table/sys_user_group/global",
"value": "global"
},
"u_incident_summary": "",
"business_service": {
"link": "://xxx.service-now.com/api/now/v1/table/cmdb_ci_service/IT services",
"value": "IT services"
},
"work_end": "",
"caller_id": {
"link": "://xxxx.service-now.com/api/now/v1/table/sys_user/71f5455d4f735e000d7f0ed11310c719",
"value": "71f5455d4f735e000d7f0ed11310c719"
},
"close_code": "",
"assignment_group": {
"link": "://xxx.service-now.com/api/now/v1/table/sys_user_group/9e158987dba27a007ea0f4e9bf961983",
"value": "9e158987dba27a007ea0f4e9bf961983"
},
"sys_id": "7fb4e50edb8c0b007ea0f4e9bf9619ba",
"u_outage_start_time": ""
}
}
This is what I have tried so far, where incident_responce is a string containing the JSON above:
var jObject = JObject.Parse(incident_responce);
var value = (string)jObject["sys_id"];
Console.WriteLine(value);
But, it didn't work, I think because there is "result" at the start. How can I retrieve this value?
As you suspected, your initial attempt fails because "sys_id" is nested inside the "result" object:
{ "result": { ... "sys_id":"7fb4e50edb8c0b007ea0f4e9bf9619ba" } }
It's easier to see this if you indent and format your JSON, for instance by uploading it to https://jsonformatter.curiousconcept.com/.
Such a nested value can be queried directly by using JToken.SelectToken():
var root = JToken.Parse(incident_responce);
var value = (string)root.SelectToken("result.sys_id");
SelectToken() supports querying for values deep within the JSON container hierarchy using JSONPath syntax. If the "result.sys_id" token is not found, null is returned.
Alternatively, you could use Json.NET's support for querying JSON hierarchies using dynamic functionality:
dynamic root = JToken.Parse(incident_responce);
var value = (string)root.result.sys_id;
However, if the "result" token is not found, a RuntimeBinderException will be thrown instead of a null value returned.
Working .Net fiddle.

Linq query to Json string

starting from a JObject I can get the array that interests me:
JArray partial = (JArray)rssAlbumMetadata["tracks"]["items"];
First question: "partial" contains a lot of attributes I'm not interested on.
How can I get only what I need?
Second question: once succeeded in the first task I'll get a JArray of duplicated items. How can I get only the unique ones ?
The result should be something like
{
'composer': [
{
'id': '51523',
'name': 'Modest Mussorgsky'
},
{
'id': '228918',
'name': 'Sergey Prokofiev'
},
]
}
Let me start from something like:
[
{
"id": 32837732,
"composer": {
"id": 245,
"name": "George Gershwin"
},
"title": "Of Thee I Sing: Overture (radio version)"
},
{
"id": 32837735,
"composer": {
"id": 245,
"name": "George Gershwin"
},
"title": "Concerto in F : I. Allegro"
},
{
"id": 32837739,
"composer": {
"id": 245,
"name": "George Gershwin"
},
"title": "Concerto in F : II. Adagio"
}
]
First question:
How can I get only what I need?
There is no magic, you need to read the whole JSON string and then query the object to find what you are looking for. It is not possible to read part of the JSON if that is what you need. You have not provided an example of what the data looks like so not possible to specify how to query.
Second question which I guess is: How to de-duplicate contents of an array of object?
Again, I do not have full view of your objects but this example should be able to show you - using Linq as you requested:
var items = new []{new {id=1, name="ali"}, new {id=2, name="ostad"}, new {id=1, name="ali"}};
var dedup = items.GroupBy(x=> x.id).Select(y => y.First()).ToList();
Console.WriteLine(dedup);

JSON.NET reader problem

i got some problem when i generate .json file from spring .json ,and i got this format
{ "models": [
{
"id":1,
"modelName":"dfdsf",
"created":{
"userFullname":"demo",
"time":"150301",
"date":"20110208",
"userId":"123"
},
"remark":"dsfdf",
"updated":"~unique-id~1"
},
{
"id":2,
"modelName":"test",
"created":{
"userFullname":"test",
"time":"150301",
"date":"20110210",
"userId":"124"
},
"remark":"test",
"updated":{
"userFullname":"test",
"time":"150301",
"date":"20110209",
"userId":"test"
}
}
]}
first time i used JObject Parse for convert
JObject job = JObject.Parse(fullJson);
and the other hand i used jtoken to focus "models"
JToken jDetail = job["models"];
but the problem is {[{ xxx }]} it look like jarray i don't have any idea to convert it
i ever use JArray, JsonTextReader but it doesn't work.
could suggestion some? because if i pass this ll i set some value to object.
thank you for every idea.
string fullJson = File.ReadAllText("TextFile1.txt"); // for brevity
var job = JObject.Parse(fullJson);
var models = job.Value<JArray>("models");
Console.WriteLine(models[0]);
result:
{
"id": 1,
"modelName": "dfdsf",
"created": {
"userFullname": "demo",
"time": "150301",
"date": "20110208",
"userId": "123"
},
"remark": "dsfdf",
"updated": "~unique-id~1"
}

Categories