How do I get properties in the array in this JSON string? - c#

In the JSON string below, how do I access the values of the "at" and "current_value" properties within the "datastreams" array ?
In this example, there is only one datastream but in reality there could be many. I need to access the datastream by "id" property. Once I figure out this issue, I plan to use a where clause with the id == to the id of the desired datastream.
I tried using the approach discussed here, under "JSON in Windows 8 – A Simpler Approach" but it's not working.
In this code, json contains the JSON returned from the service I'm calling. prop is populated with a JsonArray. current results in an exception with an inner message of "JSON value not found"
var json = JsonObject.Parse(responseBodyAsText);
var prop = json.GetNamedArray("datastreams");
var current = from p in prop
select new
{
datastream = p.GetObject().GetNamedString("datastreams"),
datetime = p.GetObject().GetNamedString("at"),
value = p.GetObject().GetNamedString("current_value")
};
Here is the JSON string:
{
"title":"X",
"status":"X",
"creator":"X",
"datastreams":
[
{
"at":"x",
"max_value":"X",
"current_value":"X",
"id":"X",
"min_value":"X"
}
],
"location":{"exposure":"x","domain":"x","disposition":"x","lat":X,"lon":-X},
"created":"X",
"tags":["X"],
"feed":"X",
"private":"X",
"id":X,
"description":"X",
"version":"X",
"updated":"X"
}

datastream = p.GetObject().GetNamedString("datastreams")
The code above should return an array of objects. You'll need to loop through the array of objects to check the value of each object's "at" and "current_value" properties.

datastream return an array as you can notice by [ ] so:
datetime = datastream[0].at
value = datastream[0].current_value

Related

Error when trying to extract values from a Json Object C#

I have a Json string like below and this is only a small snippet. The number in quotation marks is a Unix Time which i will need to use to iterate over each object.
{
"result": {
"1534860000": [
"1534860000",
19,
41
],
"1534863600": [
"1534863600",
11,
16
],
"1534867200": [
"1534867200",
2,
5
]
}
}
But when I attempt to extract the data in the arrays I get an error:
System.InvalidOperationException: 'Cannot access child value on Newtonsoft.Json.Linq.JProperty.'
Code:
JObject jsonObj = JObject.Parse(response);
string unixTime = Helpers.ConvertToUnix(yesterday.AddHours(hour)).ToString();
foreach (var obj in jsonObj["result"])
{
var array = obj[unixTime]; //here is where the error occurs
}
Anyone able to shed some light on what I am missing?
If we simplify your example code a little to remove the unixTime element (let's just hardcode it for now), we end up with this:
JObject jsonObj = JObject.Parse(response);
string unixTime = "1534860000";
At this stage, we have jsonObj which refers to the root of the JSON object and has a single property of result. Repeating your foreach here for context:
foreach (var obj in jsonObj["result"])
{
var array = obj[unixTime]; //here is where the error occurs
}
You end up with obj referring to the JSON path of result.1534860000. The problem is you're then looking for a property 1534860000 at this JSON path (result.1534860000.1534860000), which does not exist.
You can just get the value directly, like so:
var array = obj["result"][unixTime]
Of course, this requires some error-checking for ensuring the path exists, etc, but it demonstrates the point.
After some help from Kirk Larkin I thought I would post a code snippet up.
JObject jsonObj = JObject.Parse(response);
int hour = 0;
string unixTime = Helpers.ConvertToUnix(yesterday.AddHours(hour)).ToString();
var array = jsonObj["result"][unixTime];
It now returns the contents of the array.

.NET API Can't Find Data I Want

I'm trying to save two variables out of a json request but i'm just trying to get the first one working this is my request:
HttpWebRequest request = (HttpWebRequest)WebRequest.Create("https://api.majestic.com/api/json?app_api_key=KEY&cmd=GetIndexItemInfo&items=1&item0=http://www.majestic.com&datasource=fresh");
{
WebResponse response = request.GetResponse();
using (Stream responseStream = response.GetResponseStream())
{
StreamReader reader = new StreamReader(responseStream, Encoding.UTF8);
JObject jObject = JObject.Parse(reader.ReadToEnd());
JToken Trusty = jObject["DataTables"]["Results"]["Data"][2];
var newdomain = new Identifier { domain = model.domain, contact = model.contact, contactname = model.contactname, price = model.price, type = model.type, TrustFlow = Int32.Parse(Trusty.ToString()), CitationFlow = 65, RI = model.RI, MJTopicsID = model.MJTopicsID, UserTableID = model.UserTableID };
ViewBag.newdomain = newdomain;
db.Identifiers.Add(newdomain);
This returns this error:
System.ArgumentOutOfRangeException: 'Index was out of range. Must be non-negative and less than the size of the collection.'
I've also tried
Token Trusty = jObject["DataTables"]["Results"]["Data"]["TrustFlow"][0];
this returns:
'Accessed JArray values with invalid key value: "TrustFlow". Int32 array index expected.'
This is the json I tried separating it myself as on the url it just came as one long line:
{
"Code":"OK","ErrorMessage":"","FullError":"","FirstBackLinkDate":"2017-08-17","IndexBuildDate":"2017-11-20 10:51:56","IndexType":1,"MostRecentBackLinkDate":"2017-11-18","QueriedRootDomains":0,"QueriedSubDomains":0,"QueriedURLs":1,"QueriedURLsMayExist":0,"ServerBuild":"2017-10-25 14:33:44","ServerName":"QUACKYO","ServerVersion":"1.0.6507.24412","UniqueIndexID":"20171120105156-FRESH",
"DataTables":{
"Results":{
"Headers":{
"MaxTopicsRootDomain":30,"MaxTopicsSubDomain":20,"MaxTopicsURL":10,"TopicsCount":3
},
"Data":[{
"RefDomainTypeProtocolHTTPS":"228","CitationFlow":42,"TrustFlow":29,"TrustMetric":29,"TopicalTrustFlow_Topic_0":"Health/Animal","TopicalTrustFlow_Value_0":26,"TopicalTrustFlow_Topic_1":"Business","TopicalTrustFlow_Value_1":25,"TopicalTrustFlow_Topic_2":"Computers/Internet/Domain Names","TopicalTrustFlow_Value_2":24
}
]}}}
What am I doing wrong? Thanks.
Your Data property is an array of size 1. Arrays are 0 index based. So you will access the first item as someArray[0] and second item as someArray[1] and so on
To read the int value stored inside the TrustFlow property of the first item in the Data array, you can do this
int trustFlow = jObject["DataTables"]["Results"]["Data"][0]["TrustFlow"].Value<int>();
This should work for the JSON Data you provided in the question. Keep in mind that this code expects the data to be in that structure . For example, if your Data array does not have any item, or your Results does not have a Data property, the code will crash ( probably with a null reference exception). You can add the null check yourself before trying to access the value as needed.

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.

Deserializing JSON Data with JSON.NET

I am having trouble working with an API, first time I've worked with one. I've managed to use GET to read the data I need and that part is working perfectly, now I need to deserialize the JSON data I am getting back, and I am using Newtonsoft's JSON .NET library. The problem seems to come when I deserialize the data as its exploding and the debugger isn't being helpful. I tried a few suggestions online but no go, so if someone can enlighten me it'd be appreciated. This is the code:
string url = "";
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
Stream receiveStream = response.GetResponseStream();
StreamReader readStream = new StreamReader(receiveStream, Encoding.UTF8);
string responseData = readStream.ReadToEnd();
var results = JsonConvert.DeserializeObject<dynamic>(responseData);
var id = results["id"].Value;
// var name = results.Name;
When I run it the debugger throws the following exception at the last line of code:
{"Accessed JArray values with invalid key value: \"id\". Array position index expected."}
I am fairly sure that ID exists in the data I am getting back.
Json data I am getting back from Smarty Streets:
[
{
"id": 0,
"candidate_index": 0,
"delivery_line_1": "1600 Amphitheatre Pkwy",
"last_line": "Mountain View CA 94043-1351",
"delivery_point_barcode": "940431351000",
"components": {
"primary_number": "1600",
"street_name": "Amphitheatre",
"street_suffix": "Pkwy",
"city_name": "Mountain View",
"state_abbreviation": "CA",
"zipcode": "94043",
"plus4_code": "1351",
"delivery_point": "00",
"delivery_point_check_digit": "0"
},
]
Your response is an array not a single object. So You should use
JArray results = JArray.Parse(responseData)
to parse the result (like results[0]["id"]). If you go the dynamic way then
dynamic results = JArray.Parse(responseData)
Now, you can use it like results[0].id
Another option is to "Paste JSON as classes" so it can be deserialised quick and easy.
Simply copy your entire JSON
In VS: Click Edit > Paste Special >
Paste JSON as classes
Here is a better explanation n piccas...
https://blogs.msdn.microsoft.com/webdev/2012/12/18/paste-json-as-classes-in-asp-net-and-web-tools-2012-2-rc/
Your result appears to be an array of objects, not an array itself. Try setting id to results["id"][0].Value and see if you get something.
See this: Accessed JArray values with invalid key value: "fields". Array position index expected

JSON.net access nested arrays, objects

how can I access in this JSON (http://www.pegelonline.wsv.de/webservices/rest-api/v2/stations.json?includeTimeseries=true&includeCurrentMeasurement=true) the nested array like
timeseries.shortname? I tried like this but it doesn't work.
string url = "http://www.pegelonline.wsv.de/webservices/rest-api/v2/stations.json?includeTimeseries=true&includeCurrentMeasurement=true";
HttpWebRequest request = HttpWebRequest.CreateHttp(url);
WebResponse response = await request.GetResponseAsync();
using (Stream stream = response.GetResponseStream())
{
JsonReader reader = new JsonTextReader(new StreamReader(stream));
dynamic info = JArray.Load(reader);
foreach (var item in info)
{
myModel.Add(new ItemModel()
{
uuid = item.uuid,
number = item.number,
city_longname = item.longname,
timeseries = item.timeseries.shortname
});
}
}
The 3 items works, but the last (timeseries) gives the following error: Cannot perform runtime binding on a null reference
The dynamic properties give you JToken objects. Using the Value property on those gives you the string representation. In order to get it type-safe you need to parse/convert. Since you did not provide your ItemModel class details I cannot help you here.
myModel.Add(new ItemModel()
{
uuid = item.uuid.Value,
number = item.number.Value,
city_longname = item.longname.Value
});
The timeseries property is a JArray object. You cannot get to the shortname property directly. You have to choose an index first (item.timeseries[5], for instance, give you the JObject instance you are after). The details regarding getting the actual values in a type-safe manner from above apply here as well.

Categories