public T Deserialize <T>(string content) where T : new() { var context = new DeserializationContext { Culture = culture, JsonConverters = (jsonConverters ?? new List <JsonConverter>(0)).Reverse().ToArray(), JsonContractResolver = jsonResolver }; content = CommonDeserializerMethods.ReplaceAllDateInstancesWithNeoDates(content); var reader = new JsonTextReader(new StringReader(content)) { DateParseHandling = DateParseHandling.None }; var target = new T(); if (target is IList) { var objType = target.GetType(); var json = JToken.ReadFrom(reader); target = (T)CommonDeserializerMethods.BuildList(context, objType, json.Root.Children(), new TypeMapping[0], 0); } else if (target is IDictionary) { var root = JToken.ReadFrom(reader).Root; target = (T)CommonDeserializerMethods.BuildDictionary(context, target.GetType(), root.Children(), new TypeMapping[0], 0); } else { var root = JToken.ReadFrom(reader).Root; CommonDeserializerMethods.Map(context, target, root, new TypeMapping[0], 0); } return(target); }
// ReSharper disable UnusedParameter.Local IEnumerable <TResult> ParseInSingleColumnMode(DeserializationContext context, JToken root, string[] columnNames, TypeMapping[] jsonTypeMappings) // ReSharper restore UnusedParameter.Local { if (columnNames.Count() != 1) { throw new 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.)"); } var resultType = typeof(TResult); var isResultTypeANodeOrRelationshipInstance = resultType.GetTypeInfo().IsGenericType&& (resultType.GetGenericTypeDefinition() == typeof(Node <>) || resultType.GetGenericTypeDefinition() == typeof(RelationshipInstance <>)); var mapping = jsonTypeMappings.SingleOrDefault(m => m.ShouldTriggerForPropertyType(0, resultType)); var newType = mapping == null ? resultType : mapping.DetermineTypeToParseJsonIntoBasedOnPropertyType(resultType); var dataArray = (JArray)root["data"]; var rows = dataArray.Children(); var dataPropertyNameInTransaction = resultFormat == CypherResultFormat.Rest ? "rest" : "row"; var results = rows.Select(row => { if (inTransaction) { var rowObject = row as JObject; if (rowObject == null) { throw new InvalidOperationException( "Expected the row to be a JSON object, but it wasn't."); } JToken rowProperty; if (!rowObject.TryGetValue(dataPropertyNameInTransaction, out rowProperty)) { throw new InvalidOperationException("There is no row property in the JSON object."); } row = rowProperty; } if (!(row is JArray)) { // no transaction mode and the row is not an array throw new InvalidOperationException( "Expected the row to be a JSON array of values, but it wasn't."); } var rowAsArray = (JArray)row; if (rowAsArray.Count > 1) { throw new InvalidOperationException(string.Format("Expected the row to only have a single array value, but it had {0}.", rowAsArray.Count)); } return(rowAsArray); } ) .Where(row => row.Count != 0) // No elements to parse .Select(row => { var elementToParse = row[0]; if (elementToParse is JObject) { var propertyNames = ((JObject)elementToParse) .Properties() .Select(p => p.Name) .ToArray(); var dataElementLooksLikeANodeOrRelationshipInstance = new[] { "data", "self", "traverse", "properties" }.All(propertyNames.Contains); if (!isResultTypeANodeOrRelationshipInstance && dataElementLooksLikeANodeOrRelationshipInstance) { elementToParse = elementToParse["data"]; } } var parsed = CommonDeserializerMethods.CreateAndMap(context, newType, elementToParse, jsonTypeMappings, 0); return((TResult)(mapping == null ? parsed : mapping.MutationCallback(parsed))); }); return(results); }
IEnumerable <TResult> DeserializeInternal(string content) { var context = new DeserializationContext { Culture = culture, JsonConverters = Enumerable.Reverse(client.JsonConverters ?? new List <JsonConverter>(0)).ToArray(), JsonContractResolver = client.JsonContractResolver }; content = CommonDeserializerMethods.ReplaceAllDateInstacesWithNeoDates(content); var reader = new JsonTextReader(new StringReader(content)) { DateParseHandling = DateParseHandling.None }; var root = JToken.ReadFrom(reader).Root; var columnsArray = (JArray)root["columns"]; var columnNames = columnsArray .Children() .Select(c => c.AsString()) .ToArray(); var jsonTypeMappings = new List <TypeMapping> { new TypeMapping { ShouldTriggerForPropertyType = (nestingLevel, type) => type.GetTypeInfo().IsGenericType&& type.GetGenericTypeDefinition() == typeof(Node <>), DetermineTypeToParseJsonIntoBasedOnPropertyType = t => { var nodeType = t.GetGenericArguments(); return(typeof(NodeApiResponse <>).MakeGenericType(nodeType)); }, MutationCallback = n => n.GetType().GetMethod("ToNode").Invoke(n, new object[] { client }) }, new TypeMapping { ShouldTriggerForPropertyType = (nestingLevel, type) => type.GetTypeInfo().IsGenericType&& type.GetGenericTypeDefinition() == typeof(RelationshipInstance <>), DetermineTypeToParseJsonIntoBasedOnPropertyType = t => { var relationshipType = t.GetGenericArguments(); return(typeof(RelationshipApiResponse <>).MakeGenericType(relationshipType)); }, MutationCallback = n => n.GetType().GetMethod("ToRelationshipInstance").Invoke(n, new object[] { client }) } }; switch (resultMode) { case CypherResultMode.Set: return(ParseInSingleColumnMode(context, root, columnNames, jsonTypeMappings.ToArray())); case CypherResultMode.Projection: jsonTypeMappings.Add(new TypeMapping { ShouldTriggerForPropertyType = (nestingLevel, type) => nestingLevel == 0 && type.GetTypeInfo().IsClass, DetermineTypeToParseJsonIntoBasedOnPropertyType = t => typeof(NodeOrRelationshipApiResponse <>).MakeGenericType(new[] { t }), MutationCallback = n => n.GetType().GetProperty("Data").GetGetMethod().Invoke(n, new object[0]) }); return(ParseInProjectionMode(context, root, columnNames, jsonTypeMappings.ToArray())); default: throw new NotSupportedException(string.Format("Unrecognised result mode of {0}.", resultMode)); } }
public IEnumerable <TResult> Deserialize(string content, bool isHttp) { try { var context = new DeserializationContext { Culture = culture, JsonConverters = Enumerable.Reverse(client.JsonConverters ?? new List <JsonConverter>(0)).ToArray(), JsonContractResolver = client.JsonContractResolver }; if (isHttp) { content = CommonDeserializerMethods.RemoveResultsFromJson(content); } content = CommonDeserializerMethods.ReplaceAllDateInstancesWithNeoDates(content); var reader = new JsonTextReader(new StringReader(content)) { DateParseHandling = DateParseHandling.None }; // Force the deserialization to happen now, not later, as there's // not much value to deferred execution here and we'd like to know // about any errors now return(inTransaction ? FullDeserializationFromTransactionResponse(reader, context, isHttp).ToArray() : DeserializeFromRoot(content, reader, context, isHttp).ToArray()); } catch (Exception ex) { // we want the NeoException to be thrown if (ex is NeoException) { throw; } const string messageTemplate = @"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 {0}. Include this raw JSON, with any sensitive values replaced with non-sensitive equivalents: {1}"; var message = string.Format(messageTemplate, typeof(TResult).FullName, content); // If it's a specifc scenario that we're blowing up about, put this front and centre in the message if (ex is DeserializationException deserializationException) { message = $"{deserializationException.Message}{Environment.NewLine}{Environment.NewLine}----{Environment.NewLine}{Environment.NewLine}{message}"; } throw new ArgumentException(message, nameof(content), ex); } }