c# Json reading child object - c#

I am trying to read child elements in the Json object below. Below is the Json example. I want to read RecommendedCount and TotalReviewCount in testdata1, testdata2 and testdata3.
{
"HasErrors": false,
"Includes": {
"test ": {
"testdata1": {
"ReviewStatistics": {
"RecommendedCount": 0,
"TotalReviewCount": 2
}
},
"testdata2": {
"ReviewStatistics": {
"RecommendedCount": 0,
"TotalReviewCount": 2
}
},
"testdata3": {
"ReviewStatistics": {
"RecommendedCount": 0,
"TotalReviewCount": 2
}
}
}
}
}
I tried the code below.
RecommendedCount = apiResponse.Includes.Products[key].ReviewStatistics.RecommendedCount,
TotalReviewCount = apiResponse.Includes.Products[key].ReviewStatistics.TotalReviewCount
But this is very slow as the Json response has more than 1000 lines so it is taking time. I want to know is there any linq i can use to find the relevant data or any other methods i can use?
Thanks in advance.
var jObj = (JObject)JsonConvert.DeserializeObject(rawJson);
foreach (var child in jObj["test"].Children())
{
}
The above is the deserialize code i am trying to use but getting Object reference not set to an instance of an object. error

My solution:
JObject obj = JObject.Parse(jsonString);
var recList= obj.SelectTokens("$..ReviewStatistics.RecommendedCount").ToList();
var totalList= obj.SelectTokens("$..ReviewStatistics.TotalReviewCount").ToList();
Then you can get the data that you want. For example, if you want RecommendedCount from testdata2, you do like this
var dataYouWant = (int)recList[1];
References:
http://www.newtonsoft.com/json/help/html/LINQtoJSON.htm
Deserializing JSON to .NET object using Newtonsoft (or LINQ to JSON maybe?)
https://msdn.microsoft.com/en-us/library/cc197957(v=vs.95).aspx
If there is anything wrong, please feel free to correct my answer. Thanks!

I suspect the problem is in your Json you have a space in the field "test " which is causing a null reference exception.
I'm not entirely sure whether is it copy paste mistake or your getting Json string in this format.
Check this working code on removing the space.

Related

How to Loop on JSON JArray in c# when Array size becomes Zero

I have two auto generated JSON data as shown below. I want to get all the data in Colors Tag.
I Created a JArray of "Colors" and started looping on all data available, but when there is only one color available my code starts giving exceptions as "Colors" is not a JArray instead a JProperty. See sample code below.
What is the ideal way of handling this situation?
{
"UID":1234,
"Colors":["red", "blue", "green"]
}
{
"UID":1234,
"Colors":"green"
}
JObject jsonObject = (JObject)JsonConvert.DeserializeObject(jsonText, settings);
foreach (JObject reg in jsonObject["Colors"]) {
// Write to console.
}
First I would let your schema JSON "Colors" be an array because that can let your JSON be a strong schema.
if you can't modify the schema You can try to use JObject.Parse then use is to judge the type
var jsonObject = JObject.Parse(jsonText);
if (jsonObject["Colors"] is JArray)
{
foreach (JObject reg in jsonObject["Colors"]) {
// Write to console.
}
}
else if(jsonObject["Colors"] is JToken)
{
//jsonObject["Colors"]
}

Convert Nested JSON to CSV in C# via ChoETL

Does anyone know how to convert the below nested JSON to CSV via CHOETL (An ETL framework for .NET)? Thank you!
I'm using this code but it will only return the first equipment record.
CODE:
{
using (var json = new ChoJSONReader("./test.json"))
{
csv.Write(json.Cast<dynamic>().Select(i => new
{
EquipmentId = i.GpsLocation.Equipment[0].EquipmentId,
InquiryValue = i.GpsLocation.Equipment[0].InquiryValue,
Timestamp = i.GpsLocation.Equipment[0].Timestamp
}));
}
}
JSON:
"GpsLocation": {
"Equipment": [
{
"EquipmentId": "EQ00001",
"InquiryValue": [
"IV00001"
],
"Timestamp": "2020-01-01 01:01:01.01",
},
{
"EquipmentId": "EQ00002",
"InquiryValue": [
"IV00002"
],
"Timestamp": "2020-01-01 01:01:01.01"
}
]
}
}````
As others suggest, the issue is you are only looking at the first element of the array.
It appears that the easiest way to control what you serialise into CSV is by correctly defining your source objects from JSON. JSON Path expressions come in pretty handy.
What I ended up doing here is query all JSON to return an array of Equipment objects regardless of where they are in the hierarchy (which means you may need to filter it a bit better depending on your full JSON).
Then it's pretty easy to define each field based on JSON path and just pass the result to CSVWriter.
Also check out some gotchas that I outlined in the respective comment lines.
void Main()
{
var jsonString = "{\"GpsLocation\":{\"Equipment\":[{\"EquipmentId\":\"EQ00001\",\"InquiryValue\":[\"IV00001\"],\"Timestamp\":\"2020-01-01 01:01:01.01\"},{\"EquipmentId\":\"EQ00002\",\"InquiryValue\":[\"IV00002\"],\"Timestamp\":\"2020-01-01 01:01:01.01\"}]}}";
var jsonReader = new StringReader(jsonString);
var csvWriter = new StringWriter(); // outputs to string, comment out if you want file output
//var csvWriter = new StreamWriter(".\\your_output.csv"); // writes to a file of your choice
using (var csv = new ChoCSVWriter(csvWriter))
using (var json = new ChoJSONReader(jsonReader)
.WithJSONPath("$..Equipment[*]", true) // firstly you scope the reader to all Equipment objects. take note of the second parameter. Apparently you need to pass true here as otherwise it just won't return anythig
.WithField("EquipmentId", jsonPath: "$.EquipmentId", isArray: false) // then you scope each field in the array to what you want it to be. Since you want scalar values, pass `isArray: false` for better predictability
.WithField("InquiryValue", jsonPath: "$.InquiryValue[0]", isArray: false) // since your InquiryValue is actually an array, you want to obtain first element here. if you don't do this, fields names and values would go askew
.WithField("Timestamp", jsonPath: "$.Timestamp", fieldType: typeof(DateTime), isArray: false)) // you can also supply field type, otherwise it seems to default to `string`
{
csv.WithFirstLineHeader().Write(json);
}
Console.WriteLine(csvWriter.GetStringBuilder().ToString()); // comment this out if writing to file - you won't need it
}
Update summary:
Pivoted to update the code to rely on JSON Path scoping - this seems to allow for field name manipulation with pretty low effort
Looking at your comment, you could probably simplify your file writer a little bit - use StreamWriter instead of StringWriter - see updated code for example
Here is the working sample of producing CSV from your JSON
string json = #"{
""GpsLocation"": {
""Equipment"": [
{
""EquipmentId"": ""EQ00001"",
""InquiryValue"": [
""IV00001""
],
""Timestamp"": ""2020-02-01 01:01:01.01"",
},
{
""EquipmentId"": ""EQ00002"",
""InquiryValue"": [
""IV00002""
],
""Timestamp"": ""2020-01-01 01:01:01.01""
}
]
}
}";
StringBuilder csv = new StringBuilder();
using (var r = ChoJSONReader.LoadText(json)
.WithJSONPath("$.GpsLocation.Equipment")
.WithField("EquipmentId")
.WithField("InquiryValue", jsonPath: "InquiryValue[0]", fieldType: typeof(string))
.WithField("Timestamp", fieldType: typeof(DateTime))
)
{
using (var w = new ChoCSVWriter(csv)
.WithFirstLineHeader())
w.Write(r);
}
Console.WriteLine(csv.ToString());
Output:
EquipmentId,InquiryValue,Timestamp
EQ00001,IV00001,2/1/2020 1:01:01 AM
EQ00002,IV00002,1/1/2020 1:01:01 AM
Sample fiddle: https://dotnetfiddle.net/hJWtqH
Your code is sound, but the issue is that you're only writing the first variable in the array by using i.GpsLocation.Equipment[0]. Instead, try looping over everything by putting it into a for loop, and changing the [0] to your iterating variable inside of said loop.

ASP.NET getting indexed values when deserializing JSON into a dynamic object

So I have a JSON string that I am passing from an AJAX call to my controller. I have a list of indexed values that I am passing into a dynamic object.
I deserialize the JSON with
JsonConvert.DeserializeObject<dynamic>(s)
This is the output from that dynamic object:
"RolePermissions[0].RolePermissionId": "269",
"RolePermissions[0].HasAccess": "false",
"RolePermissions[1].RolePermissionId": "270",
"RolePermissions[1].HasAccess": "false",
"RolePermissions[2].RolePermissionId": "271",
"RolePermissions[2].HasAccess": "true",
"RolePermissions[3].RolePermissionId": "272",
"RolePermissions[3].HasAccess": "false"
When I try to access the a property of the object with
ssObj.RolePermissions[0].RolePermissionId
I get a RuntimeBinderException. I have tried to use JObject.Parse, which works great, but for some reason, the values in the array become out of order.
Any help would be greatly appreciated. Thanks!
When you try to do RolePermissions[0].RolePermissionId you are trying to access a nested collection containing an object with a property RolePermissionId at index 0. But your JSON doesn't represent a hierarchy of objects, it represents a single flat object with key/value pairs whose keys contain periods and brackets. Since c# identifiers don't allow such characters so you have no way to access such property values using dynamic directly.
Instead, your options include:
Take advantage of the fact that JsonConvert.DeserializeObject<dynamic>(s) actually returns a JObject and use its dictionary indexer:
var ssObj = JsonConvert.DeserializeObject<dynamic>(s);
var rolePermissionId = (string)ssObj["RolePermissions[0].RolePermissionId"];
If you prefer a slightly more typed solution, you could deserialize to a Dictionary<string, dynamic>:
var ssDict = JsonConvert.DeserializeObject<Dictionary<string, dynamic>>(s);
var rolePermissionId = (string)ssDict["RolePermissions[0].RolePermissionId"];
Or for a much more statically typed solution, parse explicitly to a JObject and use LINQ to JSON:
var jObj = JObject.Parse(s);
var rolePermissionId = (string)jObj["RolePermissions[0].RolePermissionId"];
Sample fiddle showing the various options.
If you are in control of the data being sent via AJAX then make sure the data sent is properly formatted.
In order to be able to deserialize variable s like:
var ssObj = JsonConvert.DeserializeObject<dynamic>(s);
and access the resulting object in this manner:
ssObj.RolePermissions[0].RolePermissionId
then the JSON value in s, based on your sample and desired behavior, would have to look like this:
{
"RolePermissions": [
{
"RolePermissionId": "269",
"HasAccess": "false"
},
{
"RolePermissionId": "270",
"HasAccess": "false"
},
{
"RolePermissionId": "271",
"HasAccess": "true"
},
{
"RolePermissionId": "272",
"HasAccess": "false"
}
]
}
This quick unit test showed that it is possible to get indexed values when deserializing JSON into a dynamic object
[TestClass]
public class UnitTest1 {
[TestMethod]
public void GetIndexedValuesWhenDeserializingJSONIntoDynamicObject() {
var s = #"
{
'RolePermissions': [
{
'RolePermissionId': '269',
'HasAccess': 'false'
},
{
'RolePermissionId': '270',
'HasAccess': 'false'
},
{
'RolePermissionId': '271',
'HasAccess': 'true'
},
{
'RolePermissionId': '272',
'HasAccess': 'false'
}
]
}
";
var ssObj = JsonConvert.DeserializeObject<dynamic>(s);
var result = ssObj.RolePermissions[0].RolePermissionId;
Assert.AreEqual("269", (string)result);
}
}
So you need to make sure you are sending well formatted JSON to your controller to achieve desired behavior.

C# - JObject.Parse - Invalid JSON

I'm working with an API that is returning JSON.
I have a method that calls the api, and parses the JSON response for the desired nodes.
Up to this point everything has been working fine, except the latest JSON response appears to be malformed.
Other responses come back like:
{
"Keyword":"\"marhope\"",
"TermKey":null,
"Customers":[
{
"Memberships":[ ],
"CompanyId":0,
"ObjectId":112974,
"ObjectType":"Customer",
}
]
}
I use JObject.Parse to bring back the appropriate nodes by name.
The latest JSON response comes back as:
{
[
{
"AnimalId":9079117,
"SpeciesCode":"XX",
}
]
}
As you can see, there is no "name", and the JSON is slightly invalid.
How can I parse this. For the first example I was using the code below, but now that the JSON has no "name", I don't know how to approach this, thoughts?
JObject results = JObject.Parse(csr.SearchCustomer(1, 1, 870, term));
foreach (var resp in results["Customers"])
{
string obj = (string)resp["CompanyId"];
}
Jon Skeet is correct, the second JSON is invalid: you cannot have an array directly inside an object with no property name. The best course of action is to get the API developers to fix the JSON. However, if you're just looking for a quick and dirty workaround, you could strip off the the first and last brace from the invalid JSON and then parse it as an array using JArray.Parse.
string json = #"{
[
{
""AnimalId"":9079117,
""SpeciesCode"":""XX"",
}
]
}";
json = json.Substring(1, json.Length - 2);
JArray array = JArray.Parse(json);
foreach (JObject item in array.Children<JObject>())
{
Console.WriteLine("AnimalId: " + item["AnimalId"]);
Console.WriteLine("SpeciesCode: " + item["SpeciesCode"]);
}

converting graph api for xml

I'm having trouble converting a string of json facebook graph api, I used the facebook C# and json.Net.
But at conversion time it returns this error: Name can not begin with the '0 'character, hexadecimal value 0x30.
This is the code:
dynamic result = await _fb.GetTaskAsync ("me / feed");
FBxml JsonConvert.DeserializeXNode string = (result.ToString ()). ToString ();
It looks like there is a problem with portion of the json string as mentioned below (taken from your link http://jsfiddle.net/btripoloni/PaLC2/)
"story_tags": {
"0": [{
"id": "100000866891334",
"name": "Bruno Tripoloni",
"offset": 0,
"length": 15,
"type": "user"}]
},
Json cannot create class that begins with a numeric value such as '0'. Try creating the classes using the link http://json2csharp.com/ you will get an idea.
To solve this problem you can create a dynamic object and go through each properties OR create a JsonConverter and write your code in the ReadJson to convert the "0" to a meaningful name. May be this can help you http://blog.maskalik.com/asp-net/json-net-implement-custom-serialization
If this is not your problem then update the question with more information like class structure of FBxml, call stack of the exception (from which line of the json code is throwing the exception), Json version etc.
As keyr says, the problem is with those JSON properties that have numeric names. In XML names can contain numeric characters but cannot begin with one: XML (see the Well-formedness and error-handling section).
My idea was to recursively parse the JSON with JSON.Net, replacing properties that had numeric names:
var jsonObject = JObject.Parse(json);
foreach (var obj in jsonObject)
{
Process(obj.Value);
}
XDocument document = JsonConvert.DeserializeXNode(jsonObject.ToString());
....
private static void Process(JToken jToken)
{
if (jToken.Type == JTokenType.Property)
{
JProperty property = jToken as JProperty;
int value;
if (int.TryParse(property.Name, out value))
{
JToken token = new JProperty("_" + property.Name, property.Value);
jToken.Replace(token);
}
}
if (jToken.HasValues)
{
//foreach won't work here as the call to jToken.Replace(token) above
//results in the collection modifed error.
for(int i = 0; i < jToken.Values().Count(); i++)
{
JToken obj = jToken.Values().ElementAt(i);
Process(obj);
}
}
}
This seemed to work well, prefixing numeric names with _. At this line:
XDocument document = JsonConvert.DeserializeXNode(jsonObject.ToString());
it crashed with an error saying that invalid/not well formed XML had been created. I don't have the actual error with me, but you can run the above code to replicate it.
I think from here you may need to revisit converting the JSON to XML in the first place. Is this a specific requirement?

Categories