Parse JSON using Newtonsoft.Json.Linq.JToken.SelectToken - c#

I am trying to parse JSON to obtain the value of the VIN number in the following snippet. The SelectToken call is returning a null value.
I tried using the same token to test on the following websites and it works there
http://jsonpath.com
https://jsonpath.curiousconcept.com
I am unable to identify what is going on here. Is it because this token is not supported in Newtownsoft.Json, or is it something else
[Test]
public void Test()
{
string responseContent =
"{\"GetAdvisorWipDetailPageResponse\":{\"AdvisorDetailPage\":{\"TaxLabel\":\"VAT\",\"Currency\":{\"Code\":\"UKL\",\"Description\":\"Sterling\",\"Symbol\":\"£\",\"Precision\":\"2\"},\"LastMileage\":\"0\",\"NetAmount\":\"52.73\",\"VATAmount\":\"10.55\",\"TotalAmount\":\"63.28\",\"VATRate\":\"20.00\",\"RTSLabourRate\":\"55.50\",\"DateOut\":\"2017-09-06\",\"TimeOut\":\"16:00\",\"CustomerName\":\"S 4133166Portor\",\"CustomerDetails\":{\"Name\":\"S 4133166Portor\",\"Address\":\"1 Alvin Street\\\\nHungerford\\\\n\\\\n\\\\n\"},\"ListSection\":[{\"Type\":\"2\",\"SectionDesc\":\"Vehicle Details\",\"Cols\":\"2\",\"DisplayRows\":\"10\",\"Rows\":\"10\",\"ListRow\":[{\"WipNo\":\"20285\",\"Row\":{\"Col1\":\"Registration:\",\"Col2\":\"PRA4133166\"}},{\"WipNo\":\"20285\",\"Row\":{\"Col1\":\"VIN:\",\"Col2\":\"4133166\"}},{\"WipNo\":\"20285\",\"Row\":{\"Col1\":\"Make:\",\"Col2\":\"Citroen\"}},{\"WipNo\":\"20285\",\"Row\":{\"Col1\":\"Model:\",\"Col2\":\"C3 Picasso\"}},{\"WipNo\":\"20285\",\"Row\":{\"Col1\":\"Colour:\"}},{\"WipNo\":\"20285\",\"Row\":{\"Col1\":\"Fuel Type:\",\"Col2\":\"Petrol\"}},{\"WipNo\":\"20285\",\"Row\":{\"Col1\":\"Last known odometer:\",\"Col2\":\"0\"}},{\"WipNo\":\"20285\",\"Row\":{\"Col1\":\"Next service due:\",\"Col2\":\"1200 or 06/07/2018\"}},{\"WipNo\":\"20285\",\"Row\":{\"Col1\":\"Registration date:\",\"Col2\":\"06/09/2017\"}},{\"WipNo\":\"20285\",\"Row\":{\"Col1\":\"MOT due date:\",\"Col2\":\"06/07/2018\"}}],\"Variant\":{\"Class\":\"CAR\",\"Desc\":\"Car\",\"Variants\":{\"Variant\":[{\"Class\":\"BIKE\",\"Desc\":\"Motorcycle\"},{\"Class\":\"CAR\",\"Desc\":\"Car\"},{\"Class\":\"COMP\",\"Desc\":\"Component vehicle\"},{\"Class\":\"HGV\",\"Desc\":\"Heavy Goods Vehicle\"},{\"Class\":\"LCV\",\"Desc\":\"Light Commercial Vehicle\"}]}}},{\"Type\":\"2\",\"SectionDesc\":\"Today's Service Details\",\"Cols\":\"2\",\"DisplayRows\":\"6\",\"Rows\":\"6\",\"ListRow\":[{\"WipNo\":\"20285\",\"Row\":{\"Col1\":\"Repair number:\",\"Col2\":\"20285\"}},{\"WipNo\":\"20285\",\"Row\":{\"Col1\":\"Account:\",\"Col2\":\"C0002 - Service Retail Cash Sales\"}},{\"WipNo\":\"20285\",\"Row\":{\"Col1\":\"Notes:\",\"Col2\":\"Carry out repair :-\"}},{\"WipNo\":\"20285\",\"Row\":{\"Col1\":\"Goods value:\",\"Col2\":\"£52.73\"}},{\"WipNo\":\"20285\",\"Row\":{\"Col1\":\"VAT value:\",\"Col2\":\"£10.55\"}},{\"WipNo\":\"20285\",\"Row\":{\"Col1\":\"Total value:\",\"Col2\":\"£63.28\"}}]},{\"Type\":\"4\",\"SectionDesc\":\"Recent Service History\"}],\"VHCCompleted\":\"0\",\"Reminders\":\"0\",\"PreviousVHC\":{\"Available\":\"0\"}},\"Version\":\"3.228\",\"Partition\":\"972\",\"Startup\":\"2017-09-06T15:40\",\"RequestsServiced\":\"1\"}}";
string key = "GetAdvisorWipDetailPageResponse.AdvisorDetailPage.ListSection[0].ListRow[*].[?(#.Col1=='VIN:')].Col2";
var content = JObject.Parse(responseContent);
var value = content.SelectToken(key).ToString();
}
The un-escaped string is here
{"GetAdvisorWipDetailPageResponse":{"AdvisorDetailPage":{"TaxLabel":"VAT","Currency":{"Code":"UKL","Description":"Sterling","Symbol":"£","Precision":"2"},"LastMileage":"0","NetAmount":"52.73","VATAmount":"10.55","TotalAmount":"63.28","VATRate":"20.00","RTSLabourRate":"55.50","DateOut":"2017-09-06","TimeOut":"16:00","CustomerName":"S 4133166Portor","CustomerDetails":{"Name":"S 4133166Portor","Address":"1 Alvin Street\\nHungerford\\n\\n\\n"},"ListSection":[{"Type":"2","SectionDesc":"Vehicle Details","Cols":"2","DisplayRows":"10","Rows":"10","ListRow":[{"WipNo":"20285","Row":{"Col1":"Registration:","Col2":"PRA4133166"}},{"WipNo":"20285","Row":{"Col1":"VIN:","Col2":"4133166"}},{"WipNo":"20285","Row":{"Col1":"Make:","Col2":"Citroen"}},{"WipNo":"20285","Row":{"Col1":"Model:","Col2":"C3 Picasso"}},{"WipNo":"20285","Row":{"Col1":"Colour:"}},{"WipNo":"20285","Row":{"Col1":"Fuel Type:","Col2":"Petrol"}},{"WipNo":"20285","Row":{"Col1":"Last known odometer:","Col2":"0"}},{"WipNo":"20285","Row":{"Col1":"Next service due:","Col2":"1200 or 06/07/2018"}},{"WipNo":"20285","Row":{"Col1":"Registration date:","Col2":"06/09/2017"}},{"WipNo":"20285","Row":{"Col1":"MOT due date:","Col2":"06/07/2018"}}],"Variant":{"Class":"CAR","Desc":"Car","Variants":{"Variant":[{"Class":"BIKE","Desc":"Motorcycle"},{"Class":"CAR","Desc":"Car"},{"Class":"COMP","Desc":"Component vehicle"},{"Class":"HGV","Desc":"Heavy Goods Vehicle"},{"Class":"LCV","Desc":"Light Commercial Vehicle"}]}}},{"Type":"2","SectionDesc":"Today's Service Details","Cols":"2","DisplayRows":"6","Rows":"6","ListRow":[{"WipNo":"20285","Row":{"Col1":"Repair number:","Col2":"20285"}},{"WipNo":"20285","Row":{"Col1":"Account:","Col2":"C0002 - Service Retail Cash Sales"}},{"WipNo":"20285","Row":{"Col1":"Notes:","Col2":"Carry out repair :-"}},{"WipNo":"20285","Row":{"Col1":"Goods value:","Col2":"£52.73"}},{"WipNo":"20285","Row":{"Col1":"VAT value:","Col2":"£10.55"}},{"WipNo":"20285","Row":{"Col1":"Total value:","Col2":"£63.28"}}]},{"Type":"4","SectionDesc":"Recent Service History"}],"VHCCompleted":"0","Reminders":"0","PreviousVHC":{"Available":"0"}},"Version":"3.228","Partition":"972","Startup":"2017-09-06T15:40","RequestsServiced":"1"}}

The following JSONPath query string works with Json.NET:
string key = "GetAdvisorWipDetailPageResponse.AdvisorDetailPage.ListSection[0].ListRow[?(#.Row.Col1=='VIN:')].Row.Col2";
And returns, as a result, 4133166.
Working .Net fiddle.
Why does this work? If I simplify the ListRow section of your JSON, it looks like this:
{
"ListRow":[
{
"WipNo":"20285",
"Row":{
"Col1":"Registration:",
"Col2":"PRA4133166"
}
},
{
"WipNo":"20285",
"Row":{
"Col1":"VIN:",
"Col2":"4133166"
}
}
]
}
You are looking for an entry in the ListRow array for which Row.Col1 has a certain value, specifically "VIN:". When found, you want to select the value of Row.Col2. The JSONPath query string
ListRow[?(#.Row.Col1=='VIN:')].Row.Col2
Does this.
(In your query, you are trying to apply a filter to objects nested directly inside objects (specifically, filtering the value of Col inside the Row object inside the ListRow array item object). This is apparently not implemented in Json.NET as of 10.0.3; see Json.NET JSONPath query not returning expected results for further discussion.)

Related

How do I retrieve a specific neo4j node property using neo4jclient?

I have some data stored as a neo4j node. This node has some property that is not described by the associated C# class, and thus is not automatically mapped back to the class when the neo4jclient query returns.
As an example, this C# class:
public class Node {
public string name;
public int number;
public CustomClass data;
}
stored in neo4j, then retrieved with the following neo4jclient fluent code:
var query = client.Cypher
.Match("(n:Node)")
.Return(n => n.As<Node>())
.Results;
will populate a Node object with name and number, but leave a null reference to the CustomClass object.
To solve this problem I have serialized the CustomClass as a JSON string, and stored it in neo4j as a string property. In order to deserialize this JSON class, I need to retrieve the JSON string property from the Node stored in neo4j.
The neo4jclient documentation recommends the following:
.Return(() => new {
JSONString = Return.As<string>("matchedNode.JSONProperties")
})
however this is invalid code. The Return after JSONString = does not exist in that context.
See Answer.
How can I get the JSONPropeties string out of the database?
The given code works exactly as expected, you just need to include the correct neo4jclient reference. In this case it is
using Neo4jClient.Cypher;
with that, Return is no longer undefined. This is also where the All class is, if you need access to all matched elements.
Further to your answer, aside from adding the
using Neo4jClient.Cypher
You could also choose just to return the Node properties like so:
var query = client.Cypher
.Match("(n:Node)")
.Return(n => n.As<Node>().name) //<-- returning just the property
.Results;

C# asp.net web api returning List<BsonDocument> how to keep Mongodb serializer from adding Name Value fields?

Web api function that is generating a List that the web api is returning via Telerik DataSourceResult
List<BsonDocument> objs = _mongoStore.GetData(id, newTime);
DataSourceResult result = objs.ToDataSourceResult(request);
return Json(result);
When I look in a debugger at the objs variable I see it represented in the following way; which is how we want the Json returned web api data:
{ "Computer" : "Computer1", "OSVersion" : "Windows" }
However when the Json() function serializes the objs object it is adding Name Value fields such as the following:
[[{"Name":"Computer","Value":"Computer1"},{"Name":"OSVersion","Value":"Windows"}]]
This expansion to Name, Value, fields is breaking future Json parsing APIs we use. How do we serialize this so that it just uses the actual values for Name and Value vs. it adding a specific Name and Value field? I.E. We want the returned Json to resemble like what the debugger is seeing:
{ "Computer" : "Computer1", "OSVersion" : "Windows" }
I know that one could do something like:
string rawjson = objs.ToJson(new JsonWriterSettings {OutputMode = JsonOutput.Strict});
But not sure how one would use something like that globally so if you were to be returning a Telerik DataSourceResult such as:
DataSourceResult result = objs.ToDataSourceResult(request);
return Json(Result);
Would cause the serialization that happens to return proper Json without the Name Value added fields.
Thank you!
It looks like you can actually just take the result DataSourceResult object and perform Mongo .ToJson against it then return the web api as raw json string content.
So fixed up it would be something like
List<BsonDocument> objs = _mongoStore.GetData(id, newTime);
DataSourceResult result = objs.ToDataSourceResult(request);
var finalResult=result.ToJson(new JsonWriterSettings { OutputMode = JsonOutputMode.Strict });
return Content(finalResult);
Not sure if there is a better way but this seems to work. If someone else knows a better way happy to change that to the Answer.

Search for one key in multi-level JSON array

I receive JSON output from several different web services. I need to obtain some token data from each service however it's in a different array each time. E.g.:
Service 1:
{
"service_name": "service1",
"service1_data": {
"token_data": "WSD123456789"
}
}
Service 2:
{
"service_name": "service2",
"service2_data": {
"token_data": "QSD76662345"
}
}
So I'm looking for a way to search for the value of "token_data" no matter where in the arrays it may be. At the moment I have to get it manually like so:
json1["service1_data"]["token_data"]
If theres a simple way to do this it would be much appreciated. Thanks!
You could convert the JSON to XML and then use an xpath like '//token_data' to find the token, assuming a simple string search or regex is not an option.
byte[] bytes = Encoding.ASCII.GetBytes(json);
using (var stream = new MemoryStream(bytes))
{
var quotas = new XmlDictionaryReaderQuotas();
var jsonReader = JsonReaderWriterFactory.CreateJsonReader(stream, quotas);
var xml = XDocument.Load(jsonReader);
}
Xpath info can be found here
As suggested in the comments, you could use JSONPath.
In your scenario, $.service1_data.token_data.* should get you all the values you want.
This works only if the depth of token_data in the array is always the same.
The Newtonsoft.Json package - available on NuGet Install-Package Newtonsoft.Json allows you to walk Json objects using LINQ expressions.
var jsonText = #"{
""service_name"": ""service1"",
""service1_data"": {
""token_data"": ""WSD123456789""
}
}";
var jsonObject = JsonConvert.DeserializeObject<JToken>(jsonText);
System.Diagnostics.Debug.Assert(jsonObject["service1_data"].Value<string>("token_data") == "WSD123456789");
See the JToken reference to learn how to retrieve values from Json.

How to receive JSON data into Web API ApiController method?

I'm writing a Web API ApiController with several PUT methods that receive JSON data. The JSON is not deterministic and hence cannot be hard-mapped to a custom C# object, but needs to be received as Dictionaries/Sequences (Maps/Lists).
I have tried using an IDictionary for the data parm of the PUT method in the controller, and this sort of works -- the data appears to be mapped from JSON to the dictionary. However, it's necessary to declare the dictionary as <String,Object>, and there's no clear way to then retrieve the Object values as their appropriate types. (I've found a few suggested kluges in my searching, but they are just that.)
There is also a System.Json.JsonObject type which I finally managed to get loaded via NuGet, but when I use that the system does not appear to know how to map the data.
How is this typically done? How do you implement an ApiController method that receives generic JSON?
I can see three basic approaches:
Somehow make Dictionary/Sequence work with Object or some such.
Make something like System.Json.JsonObject work, perhaps by swizzling the routing info.
Receive the JSON as a byte array and then parse explicitly using one of the C# JSON toolkits available.
(As to how dynamic the data is, JSON objects may have missing entries or extraneous entries, and in some cases a particular entry may be represented as either a single JSON value or a JSON array of values. (Where "value" is JSON array, object, string, number, Boolean, or null.) In general, except for the array/not array ambiguity, the relation between keys and value types is known.)
(But I should note that this is a large project and I'll be receiving JSON strings from several other components by other authors. Being able to examine the received type and assert that it's as expected would be quite useful, and may even be necessary from a security standpoint.)
(I should add that I'm a relative novice with C# -- have only been working with it for about 6 months.)
You've got to know what kind of data you're expecting, but I have had success doing this in the past using dynamic typing.
Something like this:
[Test]
public void JsonTester()
{
string json = "{ 'fruit':'banana', 'color':'yellow' }";
dynamic data = JsonConvert.DeserializeObject(json);
string fruit = data["fruit"];
string color = data["color"];
Assert.That(fruit == "banana");
Assert.That(color == "yellow");
}
Edit:
You either need to know the type you want to deserialize to beforehand - in which case you can deserialize it to that type immediately.
Or you can deserialize it to a dynamic type, and then convert it to your static type once you know what you want to do with it.
using Newtonsoft.Json;
using NUnit.Framework;
public class DTO
{
public string Field1;
public int Field2;
}
public class JsonDeserializationTests
{
[Test]
public void JsonCanBeDeserializedToDTO()
{
string json = "{ 'Field1':'some text', 'Field2':45 }";
var data = JsonConvert.DeserializeObject<DTO>(json);
Assert.That(data.Field1 == "some text");
Assert.That(data.Field2 == 45);
}
[Test]
public void JsonCanBeDeserializedToDynamic_AndConvertedToDTO()
{
string json = "{ 'Field1':'some text', 'Field2':45 }";
var dynamicData = JsonConvert.DeserializeObject<dynamic>(json);
var data = new DTO { Field1 = dynamicData["Field1"], Field2 = dynamicData["Field2"] };
Assert.That(data.Field1 == "some text");
Assert.That(data.Field2 == 45);
}
}

RavenDb store and retrieve class-less objects (json)

I'm writing a application where the user can write json-code and store that json code with an Id and a Collection. In other words, they specify an Id, a Collection (string; [a-zA-Z0-9]) and a Data (json, can be anything that is valid json).
Up til now I've been using RavenDb for this, cause I thought a document-database would be perfect, but I've had some problems with querying.
One of the objects that needs to be stored and queried is the following:
{
"network": "some_network",
"names": ["name1","name2"],
"data": {"values":[],"keys":[]}
}
This object should be stored with some Id that is either specified, or auto-generated (if null is given), and a Collection (must always be specified), and then I need to be able to query it based on Collection, network and a single name.
For instance, I have the code query('users', '{"network":"some_network","names":"name1"}'), and I need that code to return this object (and any other object that matches it).
Also, I'm ok with changing database, but the database needs to be able to run in-process (self-hosted), and to be able to run without admin-rights without installation (in other words, it can't bind to hostname/ip like wcf does).
How can I achieve something like this?
I found the answer to this:
public string Query(string dynIndexName, string query)
{
using (var session = store.OpenSession())
{
var q = new IndexQuery();
q.Query = query;
var result = session.Advanced.DatabaseCommands.Query("dynamic/" + dynIndexName, q, new string[0]);
return "[" + String.Join(",", result.Results.Select(r => r.ToString())) + "]";
}
}
Before calling the Query-method I convert the json-query-object into a Lucene-query that looks like this: (network:"some_network" AND names:"name1"), and use that as a query-parameter to the database. The whole class for storing and retrieving can be found here: https://github.com/Alxandr/RunJS/blob/master/src/AddIns/Storage/StorageMbro.cs

Categories