I've been wanting to deserialize automatically from a Cypher Result into their corresponding objects via neo4jclient.
These are the classes I want it to deserialize into
public class QuestionHub
{
public string Id { get; set; }
public string Title { get; set; }
public ICollection<Question> Questions {get;set;}
}
public class Question
{
public string Id { get; set; }
public string Title { get; set; }
public ICollection<Answer> Answers { get;set; }
}
public class Answer
{
public string Id { get; set; }
public string Value { get; set; }
}
I know for a fact that this code will put the corresponding QuestionHubs into a list of QuestionHubs. This is exactly what I want, but the problem is that those property that navigate to other classes, are not included.
var hubQuery = graphClient.Cypher
.Match("(qh:QuestionHub)-[:PART_OF]-(q:Question)-[:ANSWER]-(a:Answer)")
.ReturnDistinct<QuestionHub>("qh");
This is the result As you can see, the questions are not included.
Whenever I do this
var hubQuery = graphClient.Cypher
.Match("(qh:QuestionHub)-[:PART_OF]-(q:Question)-[:ANSWER]-(a:Answer)")
.ReturnDistinct<QuestionHub>("*");
I get the error
System.ArgumentException: 'Neo4j returned a valid response, however Neo4jClient was unable to deserialize into the object structure you supplied.
First, try and review the exception below to work out what broke.
If it's not obvious, you can ask for help at http://stackoverflow.com/questions/tagged/neo4jclient
Include the full text of this exception, including this message, the stack trace, and all of the inner exception details.
Include the full type definition of Neo4JTests.QuestionHub.
Include this raw JSON, with any sensitive values replaced with non-sensitive equivalents:
{ "columns":["a","q","qh"], "data":[[ {"data":{ "Value":"answer9aaf5134-9e73-4681-ba2f-e8224242ff19","Id":"9aaf5134-9e73-4681-ba2f-e8224242ff19" }},{"data":{ "Title":"questiond287a365-364a-4de0-b9f2-574893c1eaaa","Id":"d287a365-364a-4de0-b9f2-574893c1eaaa" }},{"data":{ "Title":"questionHub222a2fbe-6644-491a-b0a1-66df59f05f11","Id":"222a2fbe-6644-491a-b0a1-66df59f05f11" }} ]] } Arg_ParamName_Nam'
Inner Exception
InvalidOperationException: The deserializer is running in single column mode, but the response included multiple columns which indicates a projection instead. If using the fluent Cypher interface, use the overload of Return that takes a lambda or object instead of single string. (The overload with a single string is for an identity, not raw query text: we can't map the columns back out if you just supply raw query text.)
This error is probably because the cypher result gives multiple columns instead of one.
This is what I want to get
If I do this
var hubQuery = graphClient.Cypher
.Match("(u:User)-[:CREATOR_HUB]-(qh:QuestionHub)-[:PART_OF]-(q:Question)-[:ANSWER]-(a:Answer)")
.With("u, qh, q, COLLECT({Id: a.Id, Value: a.Value}) as answers")
.With("u, qh, COLLECT({Id: q.Id, Title: q.Title, Answers:answers}) as questions")
.With("{Creator: {Id:u.Id, FirstName: u.FirstName, LastName: u.LastName}," +
"Id: qh.Id, Title: qh.Title, Questions: questions} as result")
.ReturnDistinct<string>("result");
var hubQueryRes = await hubQuery .ResultsAsync;
List<QuestionHub> hubList = new List<QuestionHub>();
foreach (var hub in hubQueryRes )
{
hubList .Add(JsonConvert.DeserializeObject<QuestionHub>(hub));
}
I get what I want, but I needed to write all those .With
I want a way to automatically do that without all the .With
writing.
I'm looking for a way to automatically deserialize the Cypher result into their corresponding objects with nested objects included.
Is there a way to do this?
Thanks in advance!
You're 100% correct that the reason it didn't create your QuestionHub
instance is because the return was entirely in the wrong format for the client to cope with.
Unfortunately - your with
workaround is about the only way - As you're using it to return the output of Cypher into a format that the Json Deserializer can handle.
The best I can see to do would be this:
var query = gc.Cypher
.Match("(qh:QuestionHub)-[:PART_OF]-(q:Question)-[:ANSWER]-(a:Answer)")
.With("qh, q{.*, Answers: COLLECT(a)} AS qAndA")
.With("qh{.*, Questions: COLLECT(qAndA)} AS result")
.Return(result => result.As<QuestionHub>());
Bear in mind, you would also need your ICollection
to be either List
or IEnumerable
to deserialize properly - deserializing into ICollection
isn't supported.