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;
Related
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.
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
I am trying to implement a dynamic condition on a JSON Object.
After searching for a while, I am not able to find any resources which can meet my needs.
In the below fashion, I am getting the JsonObject dynamic string into jsonObject variable.
string inputFromAPI = client.GetStringAsync(URL).Result;
dynamic jsonObject = JValue.Parse(inputFromAPI);
I now have a condition which can change on need basis.For example I can have a condition which says
"inputFromAPI.DidWeCharge == true && inputFromAPI.DidWeHold == false"
The above line can change on the fly. Any inputs how to address this will be greatly appreciated.
I can't comment for clarification (don't have the rep yet), but if you need to be able to read the property name because it's changing you can do this. You would have to know the name though.
var JSONobj = JObject.Parse(json);
foreach (JToken child in JSONobj.Children())
{
var prop = child as JProperty;
var propertyName = prop.Name; // this will give you the name of the property
if (propertyName == "DidWeCharge")
{
var value = prop.Value; // Do something here with value?
}
if (propertyName == "DidWeHold")
{
var value = prop.Value; // Do something here with value?
}
var propertyType = prop.Value.Type; // this return the type as a JTokenType enum.
}
I don't know how nested your JSON is, so you may have to traverse further down with another foreach on the child by doing child.Children().
You can use ExpandoObject:
var expandoObj = JsonConvert.DeserializeObject<ExpandoObject>(jsonObject);
expandoObj.yourProperty
JsonConvert is from Newtonsoft.Json package.
You may be able to use Jpath:
using Newtonsoft.Json -
....
var json = #"
{
stuff : [
{
value : 1
},
{
value : 2
}
]
}";
var token = JToken.Parse(json);
var something = token.SelectTokens("$.stuff[?(#.value == 1)]").ToList();
This is my MongoDB Data structure:
public class Part : ICloneable
{
string _id;
ObservableCollection<DataElement> PartData;
ObservableCollection<DataElement> SensorData;
}
public class DataElement: ICloneable
{
string description;
string[] values;
}
Using Linq I want to read all Parts $projected/reduced on the elements of SensorData, PartData and ResultData with a specific description.
Example:
Part{
_id: id1,
PartData[
{description: "des1", values: "val1"},
{description: "des2", values: "val2"}
],
SensorData[
{description: "des3", values: "val5"},
{description: "des4", values: "val2"},
{description: "des5", values: "val2"}
]}
should be projected/reduced on all elements having the description "des2", "des4" and "des5", so that the data read looks like
Part{
_id: id1,
PartData[
{description: "des2", values: "val2"}
],
SensorData[
{description: "des4", values: "val2"},
{description: "des5", values: "val2"}
]}
Each description is unique, but not every Part contains all descriptions.
Is there an easy solution for doing this without any $unwind/SelectMany? Something like
Select(p => p.PartData[] where p.PartData.Description == specifiedDescription),
p => p.SensorData[] where p.SensorData.Description == specifiedDescription))
but including the complete array element, while excluding the others and for PartData and SensorData?
Edit:
After the answer by Veeram I tried to implement the following:
parts = db.GetCollection<Part>("Part");
var pipeline = parts.Aggregate()
.Project(p => new
{ PartData = p.PartData.Where(d => d.Description == specifiedDescription),
SensorData = p.SensorData.Where(s => s.Description == specifiedDescription)
}) ;
var query = pipeline.ToEnumerable().AsQueryable();
var returnParts = new ObservableCollection<Part>(query);
but this causes pipeline to be an anonymous IAggregateFluent<'a> instead of an IAggregateFluent<Part>, which makes query to be an anonymous IQueryable<'a> and therefore leading to the compile error "cannot convert from anonymous IQueryable<'a> to IQueryable<Part>" at the insertion of query as argument for the constructor of ObservableCollection<Part>().
Without the $select the variables aren't anonymous anymore, but of class <Part> and the compile error doesn't occur. Obviously the $select changes the class of the aggregation.
How is it possible to solve this error? My idea would be to make the $project without generating a new class, but rather resetting some fields of the current class <Part>, but how can this be achieved?
You can use $filter operator in $project stage with aggregation pipeline.
var pipeline =
collection.
Aggregate()
.Project(
p => new {
PartData= p.PartData.Where(d => d.Description == specifiedDescription),
SensorData= p.SensorData.Where(s=> s.Description == specifiedDescription)
}
);
{
"Date": "2016-12-15",
"Data": {
"A": 4.4023,
"AB": 1.6403,
"ABC": 2.3457
}
}
how can i get my keys A,Ab,ABC into an array? just the keys not value.
You could install json.net and use LINQ to JSON to query the properties:
var jsonString = #"{
""Date"": ""2016-12-15"",
""Data"": {
""A"": 4.4023,
""AB"": 1.6403,
""ABC"": 2.3457
}
}";
var root = JToken.Parse(jsonString);
var properties = root
// Select nested Data object
.SelectTokens("Data")
// Iterate through its children, return property names.
.SelectMany(t => t.Children().OfType<JProperty>().Select(p => p.Name))
.ToArray();
Console.WriteLine(String.Join(",", properties)); // Prints A,AB,ABC
Sample fiddle.