Skip to content

Commit

Permalink
ITypeSerializer which substitutes JsonConverter for BoltGraphClient
Browse files Browse the repository at this point in the history
  • Loading branch information
arturosevilla committed May 15, 2018
1 parent c88ede8 commit a24bfb6
Show file tree
Hide file tree
Showing 16 changed files with 289 additions and 66 deletions.
2 changes: 2 additions & 0 deletions Neo4jClient.Full.Shared/BoltGraphClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ public BoltGraphClient(Uri uri, string username = null, string password = null,

JsonConverters = new List<JsonConverter>();
JsonConverters.AddRange(DefaultJsonConverters);
TypeSerializers = new List<ITypeSerializer>();
TypeSerializers.AddRange(DefaultSerializers);
JsonContractResolver = DefaultJsonContractResolver;

ExecutionConfiguration = new ExecutionConfiguration
Expand Down
14 changes: 12 additions & 2 deletions Neo4jClient.Shared/BoltGraphClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,13 @@ public partial class BoltGraphClient : IBoltGraphClient, IRawGraphClient, ITrans
new EnumValueConverter()
};

internal static readonly ITypeSerializer[] DefaultSerializers =
{
new NullableEnumValueSerializer(),
new TimeZoneInfoSerializer(),
new EnumValueSerializer()
};

private static readonly DefaultContractResolver DefaultJsonContractResolver = new DefaultContractResolver();

private readonly string password;
Expand Down Expand Up @@ -504,6 +511,8 @@ public void Connect(NeoServerConfiguration configuration = null)
connectTask.Wait();
}

public List<ITypeSerializer> TypeSerializers { get; }

/// <inheritdoc />
public Task ConnectAsync(NeoServerConfiguration configuration = null)
{
Expand Down Expand Up @@ -538,9 +547,10 @@ public Task ConnectAsync(NeoServerConfiguration configuration = null)
#else
return Task.CompletedTask;
#endif
}

}

/// <inheritdoc />
[Obsolete(NotValidForBolt)]
public List<JsonConverter> JsonConverters { get; }

/// <inheritdoc />
Expand Down
3 changes: 3 additions & 0 deletions Neo4jClient.Shared/IBoltGraphClient.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Neo4jClient.Cypher;
using Neo4jClient.Execution;
Expand All @@ -20,6 +21,8 @@ public interface IBoltGraphClient : ICypherGraphClient

bool IsConnected { get; }

List<ITypeSerializer> TypeSerializers { get; }

Task ConnectAsync(NeoServerConfiguration configuration = null);
}
}
4 changes: 4 additions & 0 deletions Neo4jClient.Shared/Neo4jClient.Shared.projitems
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,10 @@
<Compile Include="$(MSBuildThisFileDirectory)RootNode.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Serialization\BaseDeserializer.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Serialization\BoltDriver\DriverDeserializer.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Serialization\BoltDriver\EnumValueSerializer.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Serialization\BoltDriver\IDriverDeserializer.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Serialization\BoltDriver\NullableEnumValueSerializer.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Serialization\BoltDriver\TimeZoneInfoSerializer.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Serialization\DateDeserializerMethods.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Serialization\Json\CustomJsonDeserializer.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Serialization\Json\CustomJsonSerializer.cs" />
Expand All @@ -198,6 +201,7 @@
<Compile Include="$(MSBuildThisFileDirectory)Serialization\Json\PartialDeserializationContext.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Serialization\Json\TimeZoneInfoConverter.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Serialization\Json\TypeConverterBasedJsonConverter.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Serialization\ITypeSerializer.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Serialization\TypeMapping.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Transactions\BoltResponse.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Transactions\IInternalTransactionalGraphClient.cs" />
Expand Down
37 changes: 25 additions & 12 deletions Neo4jClient.Shared/Neo4jDriverExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using System.Runtime.Serialization;
using Neo4j.Driver.V1;
using Neo4jClient.Cypher;
using Neo4jClient.Serialization;
using Newtonsoft.Json;

namespace Neo4jClient
Expand All @@ -28,10 +29,16 @@ public static IStatementResult Run(this ITransaction session, CypherQuery query,
// ReSharper disable once InconsistentNaming
public static Dictionary<string, object> ToNeo4jDriverParameters(this CypherQuery query, IGraphClient gc)
{
return query.QueryParameters.ToDictionary(item => item.Key, item => Serialize(item.Value));
return query.QueryParameters
.ToDictionary(item => item.Key, item => Serialize(item.Value, GetConverters(gc)));
}

private static object Serialize(object value)
private static List<ITypeSerializer> GetConverters(IGraphClient gc)
{
return (gc as IBoltGraphClient)?.TypeSerializers ?? new List<ITypeSerializer>();
}

private static object Serialize(object value, List<ITypeSerializer> converters)
{
if (value == null)
{
Expand All @@ -43,29 +50,35 @@ private static object Serialize(object value)

if (typeInfo.IsGenericType && type.GetGenericTypeDefinition() == typeof(Dictionary<,>))
{
return SerializeDictionary(type, value);
return SerializeDictionary(type, value, converters);
}

if (typeInfo.IsClass && type != typeof(string))
{
if (typeInfo.IsArray || typeInfo.ImplementedInterfaces.Contains(typeof(IEnumerable)))
{
return SerializeCollection((IEnumerable)value);
return SerializeCollection((IEnumerable)value, converters);
}

return SerializeObject(type, value);
return SerializeObject(type, value, converters);
}

return SerializePrimitive(type, typeInfo, value);
}

private static object SerializeObject(Type type, object value)
private static object SerializeObject(Type type, object value, List<ITypeSerializer> converters)
{
var specializedSerializer = converters.FirstOrDefault(conv => conv.CanConvert(type));
if (specializedSerializer != null)
{
return specializedSerializer.Serialize(value);
}

return type.GetProperties(BindingFlags.Instance | BindingFlags.Public)
.Where(pi =>
!(pi.GetIndexParameters().Any() || pi.IsDefined(typeof(JsonIgnoreAttribute)) ||
pi.IsDefined(typeof(IgnoreDataMemberAttribute))))
.ToDictionary(GetPropertyName, pi => Serialize(pi.GetValue(value)));
.ToDictionary(GetPropertyName, pi => Serialize(pi.GetValue(value), converters));
}

private static string GetPropertyName(PropertyInfo pi)
Expand All @@ -83,9 +96,9 @@ private static string GetPropertyName(PropertyInfo pi)
return propertyNameFromAttribute ?? propertyNameFromJsonAttribute ?? pi.Name;
}

private static object SerializeCollection(IEnumerable value)
private static object SerializeCollection(IEnumerable value, List<ITypeSerializer> converters)
{
return value.Cast<object>().Select(Serialize).ToArray();
return value.Cast<object>().Select(item => Serialize(item, converters)).ToArray();
}

private static object SerializePrimitive(Type type, TypeInfo typeInfo, object instance)
Expand Down Expand Up @@ -125,7 +138,7 @@ private static string SerializeDateTimeOffset(DateTimeOffset dateTime)
}


private static object SerializeDictionary(Type type, object value)
private static object SerializeDictionary(Type type, object value, List<ITypeSerializer> converters)
{
var keyType = type.GetGenericArguments()[0];
if (keyType != typeof(string))
Expand All @@ -140,10 +153,10 @@ private static object SerializeDictionary(Type type, object value)
string key = item.Key;
object entry = item.Value;

serialized[key] = Serialize(entry);
serialized[key] = Serialize(entry, converters);
}

return serialized;
}
}
}
}
50 changes: 21 additions & 29 deletions Neo4jClient.Shared/Serialization/BaseDeserializer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ namespace Neo4jClient.Serialization
/// <summary>
/// Base class for deserializers
/// </summary>
public abstract class BaseDeserializer<TResult, TSerialized, TRecordCollection, TRecord, TField>
public abstract class BaseDeserializer<TResult, TSerialized, TRecordCollection, TRecord, TField, TTypeConverter>
{
private readonly IGraphClient client;

Expand Down Expand Up @@ -80,24 +80,16 @@ public IEnumerable<TResult> Deserialize(TSerialized results)
protected abstract object CastIntoPrimitiveType(Type primitiveType, TField field);
protected abstract bool TryCastIntoDateTime(TField field, out DateTime? dt);
protected abstract bool TryCastIntoDateTimeOffset(TField field, out DateTimeOffset? dt);
protected abstract Dictionary<string, PropertyInfo> GetPropertiesForType(DeserializationContext context,
protected abstract Dictionary<string, PropertyInfo> GetPropertiesForType(DeserializationContext<TTypeConverter> context,
Type targetType);
protected abstract bool IsNullArray(PropertyInfo propInfo, TField field);

protected abstract DeserializationContext<TTypeConverter> GenerateContext(TRecordCollection results,
CypherResultMode resultMode);
#endregion

#region Overridable Methods

protected virtual DeserializationContext GenerateContext(TRecordCollection results, CypherResultMode resultMode)
{
return new DeserializationContext
{
Culture = CultureInfo.InvariantCulture,
JsonConverters = Enumerable.Reverse(client.JsonConverters ?? new List<JsonConverter>(0)).ToArray(),
JsonContractResolver = client.JsonContractResolver
};
}

protected virtual TypeMapping GetTypeMapping(DeserializationContext context, Type type, int nestingLevel)
protected virtual TypeMapping GetTypeMapping(DeserializationContext<TTypeConverter> context, Type type, int nestingLevel)
{
return context.TypeMappings.FirstOrDefault(m => m.ShouldTriggerForPropertyType(nestingLevel, type));
}
Expand All @@ -117,15 +109,15 @@ protected virtual string GetStringFromField(TField field)
return field.ToString();
}

protected virtual bool TryDeserializeCustomType(DeserializationContext context, Type propertyType, TField field,
protected virtual bool TryDeserializeCustomType(DeserializationContext<TTypeConverter> context, Type propertyType, TField field,
out object deserialized)
{
deserialized = null;
return false;
}
#endregion

protected IEnumerable<TResult> Deserialize(TRecordCollection results, DeserializationContext context)
protected IEnumerable<TResult> Deserialize(TRecordCollection results, DeserializationContext<TTypeConverter> context)
{
switch (ResultMode)
{
Expand All @@ -138,17 +130,17 @@ protected IEnumerable<TResult> Deserialize(TRecordCollection results, Deserializ
}
}

public TResult DeserializeObject(DeserializationContext context, TField instance)
public TResult DeserializeObject(DeserializationContext<TTypeConverter> context, TField instance)
{
return (TResult)DeserializeObject(context, typeof(TResult), instance);
}

public object DeserializeObject(DeserializationContext context, Type resultType, TField instance)
public object DeserializeObject(DeserializationContext<TTypeConverter> context, Type resultType, TField instance)
{
return CoerceValue(context, resultType, instance, 0);
}

protected IEnumerable<TResult> DeserializeInSingleColumnMode(DeserializationContext context, TRecordCollection results)
protected IEnumerable<TResult> DeserializeInSingleColumnMode(DeserializationContext<TTypeConverter> context, TRecordCollection results)
{
var columns = GetColumnNames(results);
if (columns.Length != 1)
Expand All @@ -174,7 +166,7 @@ protected IEnumerable<TResult> DeserializeInSingleColumnMode(DeserializationCont
return result;
}

protected IEnumerable<TResult> DeserializeInProjectionMode(DeserializationContext context, TRecordCollection results)
protected IEnumerable<TResult> DeserializeInProjectionMode(DeserializationContext<TTypeConverter> context, TRecordCollection results)
{
var properties = typeof(TResult).GetProperties();
var propertiesDictionary = properties
Expand Down Expand Up @@ -238,7 +230,7 @@ protected IEnumerable<TResult> DeserializeInProjectionMode(DeserializationContex
}

TResult ReadProjectionRowUsingCtor(
DeserializationContext context,
DeserializationContext<TTypeConverter> context,
TRecord record,
string[] columnNames,
IDictionary<string, PropertyInfo> propertiesDictionary,
Expand All @@ -264,7 +256,7 @@ protected IEnumerable<TResult> DeserializeInProjectionMode(DeserializationContex
}

TResult ReadProjectionRowUsingProperties(
DeserializationContext context,
DeserializationContext<TTypeConverter> context,
TRecord record,
string[] columnNames,
IDictionary<string, PropertyInfo> propertiesDictionary)
Expand All @@ -285,7 +277,7 @@ protected IEnumerable<TResult> DeserializeInProjectionMode(DeserializationContex
return result;
}

private void SetPropertyValue(DeserializationContext context, PropertyInfo property, TField value, object instance, int nestingLevel)
private void SetPropertyValue(DeserializationContext<TTypeConverter> context, PropertyInfo property, TField value, object instance, int nestingLevel)
{
if (IsNull(value))
{
Expand All @@ -308,7 +300,7 @@ public FieldEntry(string key, TField value)
}
}

private object CoerceValue(DeserializationContext context, Type valueType, TField field, int nestingLevel, bool useTypeMappings = true)
private object CoerceValue(DeserializationContext<TTypeConverter> context, Type valueType, TField field, int nestingLevel, bool useTypeMappings = true)
{
if (IsNull(field))
{
Expand Down Expand Up @@ -442,7 +434,7 @@ private object CoerceValue(DeserializationContext context, Type valueType, TFiel
: CreateObject(context, valueType, field, nestingLevel + 1);
}

private object MutateObject(DeserializationContext context, TypeMapping mapping, Type propertyType,
private object MutateObject(DeserializationContext<TTypeConverter> context, TypeMapping mapping, Type propertyType,
TField field, int nestingLevel)
{
var newType = mapping.DetermineTypeToParseJsonIntoBasedOnPropertyType(propertyType);
Expand Down Expand Up @@ -477,7 +469,7 @@ private Type GetListItemType(Type collectionType, TypeInfo ti)
return itemType;
}

private IList BuildList(DeserializationContext context, Type collectionType, Type itemType, TypeInfo collectionTypeInfo,
private IList BuildList(DeserializationContext<TTypeConverter> context, Type collectionType, Type itemType, TypeInfo collectionTypeInfo,
IEnumerable<TField> items, int nestingLevel)
{
if (items == null)
Expand All @@ -501,7 +493,7 @@ private Type GetListItemType(Type collectionType, TypeInfo ti)
return collection;
}

private IDictionary BuildDictionary(DeserializationContext context, Type type, TField field, int nestingLevel)
private IDictionary BuildDictionary(DeserializationContext<TTypeConverter> context, Type type, TField field, int nestingLevel)
{
var dict = (IDictionary)Activator.CreateInstance(type);
var valueType = type.GetGenericArguments()[1];
Expand All @@ -514,7 +506,7 @@ private IDictionary BuildDictionary(DeserializationContext context, Type type, T
return dict;
}

private object CreateObject(DeserializationContext context, Type valueType, TField field, int nestingLevel)
private object CreateObject(DeserializationContext<TTypeConverter> context, Type valueType, TField field, int nestingLevel)
{
object instance;
try
Expand All @@ -531,7 +523,7 @@ private object CreateObject(DeserializationContext context, Type valueType, TFie
return Map(context, valueType, instance, field, nestingLevel);
}

private object Map(DeserializationContext context, Type targetType, object instance, TField field, int nestingLevel)
private object Map(DeserializationContext<TTypeConverter> context, Type targetType, object instance, TField field, int nestingLevel)
{
var props = GetPropertiesForType(context, targetType);
var entries = CastIntoDictionaryEntries(props, field)
Expand Down

0 comments on commit a24bfb6

Please sign in to comment.