How to extract a field from JSON - c#

Using NewtonSoft, I am able to parse JSON into this:
var jtoken = JObject.Parse(stringStuff);
Console.WriteLine(jtoken.ToString());
gives this:
{
"data": {
"user": {
"email": "john#doe.com",
"external-id": "af36-e9fddecdb755"
},
"session-token": "G_uJNNxmLNtxcmM2ilB6P_dN67p9XXk3-yn8peUBi7k3xH50Ch7MUQ+C"
},
"context": "/sessions"
}
However, if I try to get the field "session-token" like this,
var token = jtoken.Value<string>("session-token");
token is null. Not sure what I am doing wrong?

a "session-token" property is nested inside of a "data" property
string token = (string) jtoken["data"]["session-token"]; // G_uJNNxmLNtxcmM2ilB6P_dN67p9XXk3-yn8peUBi7k3xH50Ch7MUQ+C

You need to run through the descendants of your root object, and find the JProperty with the name you want -
var token = root.Descendants()
.OfType<JProperty>()
.Where(x => x.Name == "session-token")
.FirstOrDefault()
?.Value
?.Value<string>();
if(token != null)
{
//token is "G_uJNNxmLNtxcmM2ilB6P_dN67p9XXk3-yn8peUBi7k3xH50Ch7MUQ+C"
}
It's a little awkward, the first .Value returns the JProperty's JToken, then the second .Value gets the string value.

Related

Check nested object contains any propeties with null value

I have a result response object which I need to deserialize and convert to a JSON object which looks like this:
var responseBody = await response.Content.ReadAsStringAsync();
var deviceSeqNrResponse = JsonConvert.DeserializeObject(responseBody);
and deviceSeqNrResponse looks like
{
"dataMod": 1,
"deviceId": "myDeviceID",
"seqNum": [
{
"ApplicationType": null,
"exampleId": 8
}
]
}
and I am trying to test this logic to see if there are any properties in "seqNum": [] which is nested in the result object. I tried .Contains and other approaches with no success.
I am using .NET 6.
What I am trying to achieve is that:
Assert there is no property with null values in "seqNum": [] equals to true.
Approach 1: Wih Newtonsoft.Json
Select the element "sequenceNumbers" via .SelectToken().
Get all the values from 1 (return nested arrays). Flatten the nested array via .SelectMany().
With .Any() to find any element from the result 2 with Type == JTokenType.Null.
Negate the result from 3 to indicate there is no element with the value: null.
JToken token = JToken.Parse(responseBody);
bool hasNoNullValueInSeqNumber = !token.SelectToken("sequenceNumbers")
.SelectMany(x => x.Values())
.Any(x => x.Type == JTokenType.Null);
Approach 2: With System.Reflection
Get all the public properties from the SequenceNumber class.
With .All() to evaluate all the objects in SequenceNumbers list doesn't contain any properties with value: null.
using System.Linq;
using System.Reflection;
bool hasNoNullValueInSeqNumber = typeof(SequenceNumber).GetProperties(BindingFlags.Public | BindingFlags.Instance)
.All(x => !deviceSeqNrResponse.SequenceNumbers.Any(y => x.GetValue(y) == null));
Demo # .NET Fiddle
Check this example:
string responseBody = #"{
'dataModelVersion': 1,
'deviceIdentifier': 'myDeviceID',
'sequenceNumbers': [
{
'consumingApplication': 'Gateway',
'configurationSequenceNumber': 8
},
null
]
}";
JToken token = JToken.Parse(responseBody);
if (token.SelectToken("sequenceNumbers").Any(item=>item.Type == JTokenType.Null))
{
Console.WriteLine("found.");
}
else
{
Console.WriteLine("not found.");
}
JObject deviceSeqNrResponse = (JObject)token;

Get only one variable from json response

I make a POST request and I get back json response with a lot of transactions in it like:
"Transactions": [
{
"ID": "1",
"Name" : "name",
"Code": "123aaa"
},
{
"ID": "2",
"Name" : "name1",
"Code": "12345bbb"
}
]
Is there any simple way to get one variable (for example first "Code" value)? Without entity classes etc?
I've tried this
var content = new StringContent(request, Encoding.UTF8, "application/json");
var response = await HttpClient.PostAsync(postURL, content);
var text = await response.Content.ReadAsStringAsync();
var code = JObject.Parse(text).Children().First().Values<string>("Code").First();
But it didn't work:
System.InvalidOperationException : Cannot access child value on Newtonsoft.Json.Linq.JValue.
Assuming that provided json is actually an object (i.e. string in question is wrapped in curly brackets) - you can use json path:
var code = JObject.Parse(text).SelectToken("$.Transactions[0].Code").ToString(); // results in "123aaa"
If you are keen on using LINQ to JSON you can do something like this:
var code = JObject.Parse(text)
.Descendants()
.OfType<JProperty>()
.Where(p => p.Name == "Code")
.Select(p => p.Value.ToString())
.FirstOrDefault();
But the easiest I think would be to use indexers:
var code = JObject.Parse(text)["Transactions"][0]["Code"].ToString();

C# find JSON value based only on Key name through multiple levels of array

I have a variety of input JSON formatted data which all contain a particular key-name terminalSize. This is the only piece I know. The total number of JSON trees or the exact depth of terminalSize inside the JSON tree will forever be an unknown and subject to change.
I'm looking for a C# solution to loop through every child of the JSON string and find terminalSize then fetch the value.
I've tried this with success but it will only work if terminalSize is in the first level of the JSON:
var list = JsonConvert.DeserializeObject<List<Dictionary<string, string>>>(jsonString);
var dict = list.SelectMany(d => d).ToDictionary(p => p.Key, p => p.Value);
var terminal = dict["terminalSize"];
Example 1.
{
"status": "success",
"data": {
"terminalSize": 3766505.46,
"totalTerminalSize": 3766505.46
},
"message": null
}
Example 2.
{
"lastUpdated": 1588020678,
"terminalData": {
"terminalSize": "451679852",
"totalTerminalSize": "2100000000"
},
"terminalValueSeries": {
"8x7": 2.33,
"8x6": 3.73,
"8x5": 4.49,
"8x4": 3.68,
"8x3": 13998,
"8x2": 274936,
"8x1": 5.09
}
}
Example 3.
{
"terminalSize": "492612346.17",
"terminalStatus": "online"
}
You can parse your JSON to a JToken, then use SelectToken with the recursive descent JsonPath operator .. to get the terminalSize anywhere in the JSON:
var terminalSize = (double?) JToken.Parse(json).SelectToken("$..terminalSize");
Fiddle: https://dotnetfiddle.net/5ziYbP
If there might be multiple terminalSize keys in the JSON, for example if you had an array of terminals, you can use SelectTokens instead and put the terminal sizes into a Dictionary keyed by path:
var sizes = JToken.Parse(json4)
.SelectTokens("$..terminalSize")
.ToDictionary(t => t.Path, t => (double)t);
Fiddle: https://dotnetfiddle.net/ivSM88
You could also use linq and filter the JProperty collection based on JProperty.Name. For example
var result = JObject.Parse(jsonString)
.DescendantsAndSelf()
.OfType<JProperty>()
.Single(x=>x.Name.Equals("terminalSize"))
.Value;
You may parse your JSON into JObject, then recursively go through all properties and sub objects to find a terminalSize value. There is no need to deserialize the entire JSON into specific object
var json = JObject.Parse(jsonString);
var result = GetTerminalSize(json);
double GetTerminalSize(JObject input)
{
foreach (var property in input.Properties())
{
if (property.Name == "terminalSize")
return property.Value.Value<double>();
if (property.Value.Type == JTokenType.Object)
return GetTerminalSize((JObject) property.Value);
//not sure, if the is a need to handle an array
if (property.Value.Type == JTokenType.Array)
foreach (var item in (JArray) property.Value)
return GetTerminalSize((JObject) item);
}
return 0;
}
It returns a correct value for all 3 examples

Cannot access child value on Newtonsoft.Json.Linq.JProperty -Error happens while checking JObject using LinQ

I have a JObject object which has Json data. I need to collect all KeyValuePairs of whichever has "state": true. Before I read the value, I want to make sure that the JObject has at least one KeyValuePairs with JToken (Value) has "state": true.
Below is my JSON:
{
"AAA": {
"state": false,
"version": "1.1.14202.0",
"result": null,
"update": "20171018"
},
"BBB": {
"state": true,
"version": "3.10.1.18987",
"result": null,
"update": "20171018"
},
"CCC": {
"state": true,
"version": "1.1.1.2",
"result": null,
"update": "20171018"
}
}
And the below is the code currently I'm checking with, which is throwing an exception saying Cannot access child value on Newtonsoft.Json.Linq.JProperty:
JObject jsonData = //JSON data;
List<JToken> tokens = jsonData .Children().ToList();
if (tokens.Any(each => each["state"].ToString().ToLower().Contains("true")))
{
List<JToken> tokensWithStateTrue = tokens.Where(each => each["state"].ToString().ToLower().Contains("true")).ToList();
}
Please help me and correct the LinQ statement to read only JTokens with state as true.
This worked for me, looks like you're missing an extra call to Children() to access the properties you need.
//parse JSON and grab it's children.
var jsonData = JObject.Parse(json).Children();
List<JToken> tokens = jsonData .Children().ToList();
or
List<JToken> tokens = jsonData .Children().Children().ToList();
if (tokens.Any(each => each["state"].ToString().ToLower().Contains("true")))
{
List<JToken> tokensWithStateTrue = tokens.Where(each => each["state"].ToString().ToLower().Contains("true")).ToList();
}
Alternatively you could do this. The code below will return a dictionary with only your states with true values. Otherwise, it will return an empty dictionary if you have no true values.
var dictionaryTokensWithTrueValues = jsonData.Children()
.Select(u => u as JProperty)
.Where(v => v.Value["state"].ToString().ToLower().Contains("true"))
.ToDictionary(k => k.Name, v => v.Value);
//check if you have any true values
if (dictionaryTokensWithTrueValues.Count() > 0)
{
//do something with true states here
var accessBBB = dictionaryTokensWithTrueValues["BBB"]; //{{"state": true,"version": "3.10.1.18987","result": null,"update": "20171018"}}
}
else
{
//no true states. Do something else
}

Find and retrieve keyed value from anywhere in JSON string

I'm retrieving JSON which may vary in content items and want to find values associated with particular keys, e.g. in this case "geo_latitude" and "geo_longitude". For clarity, I'm only including those two items in this JSON sample. (It's typically much longer and in no particular order).
[
{
"id": 524616,
"key": "geo_latitude",
"value": "36.1069652"
},
{
"id": 524617,
"key": "geo_longitude",
"value": "-112.1129972"
}
]
This was the best I was able to come up with to parse the JSON. Is there a better way?
var metadataObj = JArray.Parse(postMetadataJsonStr);
var latContainer = metadataObj.Descendants()
.OfType<JObject>()
.Where(x => x["key"] != null &&
x["key"].Value<string>() == "geo_latitude").Select(y => y["value"]);
var latTokenJValue = latContainer.FirstOrDefault();
if (latTokenJValue == null) return;
var latitude = latTokenJValue.ToString();
I would convert your json to a dictionary
var metadataObj = JArray.Parse(postMetadataJsonStr);
var dict = metadataObj.ToDictionary(x => (string)x["key"], x => (double)x["value"]);
Console.WriteLine(dict["geo_latitude"]);

Categories