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

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;

Related

Parse JSON using Newtonsoft.Json.Linq.JToken.SelectToken

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.)

How to Get a typed DTO with only some fields from Elasticsearch with Nest?

I'm trying to Get a result from Elasticsearch 1.7.0 using NEST 1.7.1. My documents contain many fields, but I'm interested in only one of them. I would prefer to get a typed result representing a partial document.
I'm using something along the lines of this code:
var settings = new ConnectionSettings(new Uri(url)).SetDefaultIndex("MyIndex");
var client = new ElasticClient(settings);
var result = client.Get<MyDtoPartial>(g => g
.Type("myDocType")
.Id("abcdefg123456")
.Fields("doc.subids")
);
Where MyDtoPartial currently looks like this:
public class MyDtoPartial
{
[JsonProperty("doc.subids")]
public IList<string> SubIds { get; set; }
// Other properties of my documents are not mapped, in this
// context I only want the SubIds.
}
In the debugger I can drill into result.Fields and see that the first in that dictionary has a value rendered by the debugger along these lines:
{[doc.subids, [ "12354adsf-123fasd", "2134fa34a-213123" ...
I can also see the Elasticsearch request that was made, which was like this:
http://myserver:12345/MyIndex/myDocType/abcdefg123456?fields=doc.subids
And it returns this type of json:
{
"_index": "MyIndex",
"_type": "myDocType",
"_id": "abcdefg123456",
"_version": 1,
"found": true,
"fields": {
"doc.subids": ["12354adsf-123fasd",
"2134fa34a-213123",
"adfasdfew324-asd"]
}
}
So I have a feelling my request is okay, because that is the kind of response I'd expect.
However, my goal was to get an instance of MyDtoPartial with a fully populated SubIds property. However, the result doesn't seem to contain any kind of property of type MyDtoPartial.
I've gone through the Nest Get docs, which actually led to the above code.
What is the proper way to Get a proper typed single document with only some fields from Elastic with Nest?
If you mention .Fields(...), Source will always be null. If you remove .Fields(...), then Source should be of type MyDtoPartial and give you the desired results. The reason you still get Source as null may be because in the mapping of myDocType, _source field is disabled. Check the definition of myDocType by executing GET <index name>/_mapping/myDocType. If _source is disabled, there is no way Nest will give you a concrete object of MyDtoPartial in its response for this type.
If you have _source enabled but only want to fetch a subset of fields, then you can use source filtering instead of fields to specify which fields you want and which ones you do not want to be returned in the response.
var result = client.Get<MyDtoPartial>(g => g
.Type("myDocType")
.Id("abcdefg123456")
.SourceInclude("doc.subids")
);
Now result.Source will be an object of MyDtoPartial where all fields except SubIds will be null and SubIds will have the expected value.

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);
}
}

Turn a JSON string into a dynamic object

I'm trying to create a dynamic object from a JSON string in C#. But i can't get it done.
Normally i would get a JSON string through a web service call but in this case I simply created a simple class which I turn into a JSON string. Then I try to turn it back into a dynamic object with the exact same structure as if it was an instance of the Entity class. But that's where I'm having trouble.
This is the class that i turn into a JSON string:
public class Entity
{
public String Name = "Wut";
public String[] Scores = {"aaa", "bbb", "ccc"};
}
Then in somewhere in my code i do this:
var ent = new Entity();
// The Serialize returns this:
// "{\"Name\":\"Wut\",\"Scores\":[\"aaa\",\"bbb\",\"ccc\"]}"
var json = new JavaScriptSerializer().Serialize(ent);
dynamic dynamicObject1 = new JavaScriptSerializer().DeserializeObject(json);
dynamic dynamicObject2 = Json.Decode(json);
When I debug this code then i see that the first dynamicObject1 returns a Dictionary. Not really what I'm after.
The second dynamicObject2 looks more like the Entity object. It has a property called Name with a value. It also has a dynamic array called Scores, but for some reason that list turns out to be empty...
Screenshot of empty Scores property in dynamic object:
So I'm not having any luck so far trying to cast a JSON string to a dynamic object. Anyone any idea how I could get this done?
Using Json.Net
dynamic dynamicObject1 = JsonConvert.DeserializeObject(json);
Console.WriteLine(dynamicObject1.Name);
Using Json.Net but deserializing into a ExpandableOBject, a type that is native to C#:
dynamic obj= JsonConvert.DeserializeObject<ExpandoObject>(yourjson);
If the target type is not specified then it will be convert to JObject type instead. Json object has json types attached to every node that can cause problems when converting the object into other dynamic type like mongo bson.

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