diff --git a/src/Support.NewtonsoftJson/Converters/DateTimeOffsetConverterFactory.cs b/src/Support.NewtonsoftJson/Converters/DateTimeOffsetConverterFactory.cs index 2e8c670d98..358dace6e0 100644 --- a/src/Support.NewtonsoftJson/Converters/DateTimeOffsetConverterFactory.cs +++ b/src/Support.NewtonsoftJson/Converters/DateTimeOffsetConverterFactory.cs @@ -2,6 +2,7 @@ using ExRam.Gremlinq.Core.Transformation; using ExRam.Gremlinq.Core; using System.Globalization; +using static System.Globalization.DateTimeStyles; namespace ExRam.Gremlinq.Support.NewtonsoftJson { @@ -13,7 +14,7 @@ internal sealed class DateTimeOffsetConverterFactory : FixedTypeConverterFactory { { Value: DateTimeOffset dateTimeOffset } => dateTimeOffset, { Value: DateTime dateTime } => new DateTimeOffset(dateTime), - { Value: string dateTimeString } when DateTime.TryParse(dateTimeString, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal, out var parseResult) => parseResult, + { Value: string dateTimeString } when DateTimeOffset.TryParse(dateTimeString, CultureInfo.InvariantCulture, AdjustToUniversal | AssumeLocal, out var parseResult) => parseResult, { Type: JTokenType.Integer } => DateTimeOffset.FromUnixTimeMilliseconds(jValue.Value()), _ => default(DateTimeOffset?) }; diff --git a/src/Support.NewtonsoftJson/Converters/PropertyHeuristicConverterFactory.cs b/src/Support.NewtonsoftJson/Converters/PropertyHeuristicConverterFactory.cs deleted file mode 100644 index 07f453503d..0000000000 --- a/src/Support.NewtonsoftJson/Converters/PropertyHeuristicConverterFactory.cs +++ /dev/null @@ -1,48 +0,0 @@ -using Newtonsoft.Json.Linq; -using System.Diagnostics.CodeAnalysis; -using ExRam.Gremlinq.Core.Transformation; -using ExRam.Gremlinq.Core; -using ExRam.Gremlinq.Core.GraphElements; - -namespace ExRam.Gremlinq.Support.NewtonsoftJson -{ - internal sealed class PropertyHeuristicConverterFactory : IConverterFactory - { - private sealed class PropertyHeuristicConverter : IConverter - { - private readonly IGremlinQueryEnvironment _environment; - - public PropertyHeuristicConverter(IGremlinQueryEnvironment environment) - { - _environment = environment; - } - - public bool TryConvert(JObject jObject, ITransformer defer, ITransformer recurse, [NotNullWhen(true)] out TTarget? value) - { - if (jObject.LooksLikeVertexProperty()) - { - if (recurse.TryTransform(jObject, _environment, out VertexProperty? vProp) && vProp is TTarget target) - { - value = target; - return true; - } - } - else if (jObject.LooksLikeProperty()) - { - if (recurse.TryTransform(jObject, _environment, out Property? prop) && prop is TTarget target) - { - value = target; - return true; - } - } - - value = default; - return false; - } - } - - public IConverter? TryCreate(IGremlinQueryEnvironment environment) => typeof(JObject) == typeof(TSource) && typeof(TTarget).IsAssignableFrom(typeof(VertexProperty)) && !typeof(Property).IsAssignableFrom(typeof(TTarget)) - ? (IConverter)(object)new PropertyHeuristicConverter(environment) - : default; - } -} diff --git a/src/Support.NewtonsoftJson/Converters/ScalarToPropertyConverterFactory.cs b/src/Support.NewtonsoftJson/Converters/ScalarToPropertyConverterFactory.cs index dab7fec79e..e90cf49f74 100644 --- a/src/Support.NewtonsoftJson/Converters/ScalarToPropertyConverterFactory.cs +++ b/src/Support.NewtonsoftJson/Converters/ScalarToPropertyConverterFactory.cs @@ -4,6 +4,7 @@ using ExRam.Gremlinq.Core.Transformation; using ExRam.Gremlinq.Core; using System.Linq.Expressions; +using System.Reflection; namespace ExRam.Gremlinq.Support.NewtonsoftJson { @@ -15,19 +16,14 @@ private sealed class ScalarToPropertyConverter _constructor; - public ScalarToPropertyConverter(IGremlinQueryEnvironment environment) + public ScalarToPropertyConverter(IGremlinQueryEnvironment environment, ConstructorInfo constructor) { - if (typeof(TTargetProperty).GetConstructor(new[] { typeof(TTargetPropertyValue) }) is { } constructor) - { - var lambdaParam = Expression.Parameter(typeof(TTargetPropertyValue)); + var lambdaParam = Expression.Parameter(typeof(TTargetPropertyValue)); - _environment = environment; - _constructor = Expression - .Lambda>(Expression.New(constructor, lambdaParam), lambdaParam) - .Compile(); - } - else - throw new ArgumentException($"{typeof(TTargetProperty).Name} does not contain a constructor that takes a value of type {typeof(TTargetPropertyValue).Name}."); + _environment = environment; + _constructor = Expression + .Lambda>(Expression.New(constructor, lambdaParam), lambdaParam) + .Compile(); } public bool TryConvert(JValue serialized, ITransformer defer, ITransformer recurse, [NotNullWhen(true)] out TTargetProperty? value) @@ -46,8 +42,18 @@ public bool TryConvert(JValue serialized, ITransformer defer, ITransformer recur } } - public IConverter? TryCreate(IGremlinQueryEnvironment environment) => typeof(TSource) == typeof(JValue) && typeof(Property).IsAssignableFrom(typeof(TTarget)) && typeof(TTarget).IsGenericType - ? (IConverter?)Activator.CreateInstance(typeof(ScalarToPropertyConverter<,>).MakeGenericType(typeof(TTarget), typeof(TTarget).GetGenericArguments()[0]), environment) - : default; + public IConverter? TryCreate(IGremlinQueryEnvironment environment) + { + if (typeof(TSource) == typeof(JValue) && typeof(Property).IsAssignableFrom(typeof(TTarget)) && typeof(TTarget).IsGenericType) + { + if (typeof(TTarget).GetGenericArguments() is [var targetPropertyValueType]) + { + if (typeof(TTarget).GetConstructor(new[] { targetPropertyValueType }) is { } constructor) + return (IConverter?)Activator.CreateInstance(typeof(ScalarToPropertyConverter<,>).MakeGenericType(typeof(TTarget), targetPropertyValueType), environment, constructor); + } + } + + return default; + } } } diff --git a/src/Support.NewtonsoftJson/Extensions/GremlinQueryEnvironmentExtensions.cs b/src/Support.NewtonsoftJson/Extensions/GremlinQueryEnvironmentExtensions.cs index 40e6481810..f576a4ce2a 100644 --- a/src/Support.NewtonsoftJson/Extensions/GremlinQueryEnvironmentExtensions.cs +++ b/src/Support.NewtonsoftJson/Extensions/GremlinQueryEnvironmentExtensions.cs @@ -95,7 +95,6 @@ public static IGremlinQueryEnvironment UseNewtonsoftJson(this IGremlinQueryEnvir .Add(new ExtractPropertyValueConverterFactory()) .Add(new ScalarToPropertyConverterFactory()) - .Add(new PropertyHeuristicConverterFactory()) .Add(new VertexOrEdgeConverterFactory()) .Add(new LabelLookupConverterFactory()) diff --git a/test/Providers.CosmosDb.Tests/IntegrationTests.Group_with_key_and_value2.verified.txt b/test/Providers.CosmosDb.Tests/IntegrationTests.Group_with_key_and_value2.verified.txt index e6acfc9a04..8729e76bff 100644 --- a/test/Providers.CosmosDb.Tests/IntegrationTests.Group_with_key_and_value2.verified.txt +++ b/test/Providers.CosmosDb.Tests/IntegrationTests.Group_with_key_and_value2.verified.txt @@ -4,6 +4,6 @@ Country: PartitionKey, Language: PartitionKey, Person: 1586270616000, - TimeFrame: 28800000 + TimeFrame: 28800000.0 } ] \ No newline at end of file diff --git a/test/Providers.CosmosDb.Tests/IntegrationTests.Properties1.verified.txt b/test/Providers.CosmosDb.Tests/IntegrationTests.Properties1.verified.txt index be7cf65041..1b331f6fd6 100644 --- a/test/Providers.CosmosDb.Tests/IntegrationTests.Properties1.verified.txt +++ b/test/Providers.CosmosDb.Tests/IntegrationTests.Properties1.verified.txt @@ -243,7 +243,7 @@ Id: 12345678-9012-3456-7890-123456789012, Label: Duration, Properties: {}, - Value: 7200000 + Value: 7200000.0 }, { Id: 12345678-9012-3456-7890-123456789012, @@ -261,7 +261,7 @@ Id: 12345678-9012-3456-7890-123456789012, Label: StartTime, Properties: {}, - Value: 28800000 + Value: 28800000.0 }, { Id: 12345678-9012-3456-7890-123456789012, diff --git a/test/Providers.CosmosDb.Tests/IntegrationTests.Value.verified.txt b/test/Providers.CosmosDb.Tests/IntegrationTests.Value.verified.txt index 3d7ce84637..dff0e0eccf 100644 --- a/test/Providers.CosmosDb.Tests/IntegrationTests.Value.verified.txt +++ b/test/Providers.CosmosDb.Tests/IntegrationTests.Value.verified.txt @@ -80,10 +80,10 @@ en, PartitionKey, PartitionKey, - 7200000, + 7200000.0, false, PartitionKey, - 28800000, + 28800000.0, en, PartitionKey, 21, diff --git a/test/Providers.GremlinServer.Tests/IntegrationTests.DateTimeOffset_from_string_1.verified.txt b/test/Providers.GremlinServer.Tests/IntegrationTests.DateTimeOffset_from_string_1.verified.txt new file mode 100644 index 0000000000..86d6e487a6 --- /dev/null +++ b/test/Providers.GremlinServer.Tests/IntegrationTests.DateTimeOffset_from_string_1.verified.txt @@ -0,0 +1,3 @@ +[ + 2016-12-14 16:39:19.349 +0 +] \ No newline at end of file diff --git a/test/Providers.GremlinServer.Tests/IntegrationTests.DateTimeOffset_from_string_2.verified.txt b/test/Providers.GremlinServer.Tests/IntegrationTests.DateTimeOffset_from_string_2.verified.txt new file mode 100644 index 0000000000..332d086c9b --- /dev/null +++ b/test/Providers.GremlinServer.Tests/IntegrationTests.DateTimeOffset_from_string_2.verified.txt @@ -0,0 +1,3 @@ +[ + 2016-01-01 11:30 +0 +] \ No newline at end of file diff --git a/test/Providers.GremlinServer.Tests/IntegrationTests.DateTimeOffset_from_string_3.verified.txt b/test/Providers.GremlinServer.Tests/IntegrationTests.DateTimeOffset_from_string_3.verified.txt new file mode 100644 index 0000000000..90b2314a86 --- /dev/null +++ b/test/Providers.GremlinServer.Tests/IntegrationTests.DateTimeOffset_from_string_3.verified.txt @@ -0,0 +1,3 @@ +[ + 2007-12-03 09:15:30 +0 +] \ No newline at end of file diff --git a/test/Providers.GremlinServer.Tests/IntegrationTests.cs b/test/Providers.GremlinServer.Tests/IntegrationTests.cs index d6fed3ff5a..83ab3ceb6d 100644 --- a/test/Providers.GremlinServer.Tests/IntegrationTests.cs +++ b/test/Providers.GremlinServer.Tests/IntegrationTests.cs @@ -177,6 +177,24 @@ public Task Contains_case_insensitive_reverse() => _g .Where(x => x.Contains("LL", StringComparison.OrdinalIgnoreCase)) .Verify(); + [Fact] + public Task DateTimeOffset_from_string_1() => _g + .Inject("2016-12-14T16:39:19.349Z") + .Cast() + .Verify(); + + [Fact(Skip = "Won't work on CI and everything that is not located in my time zone.")] + public Task DateTimeOffset_from_string_2() => _g + .Inject("2016-01-01T12:30") + .Cast() + .Verify(); + + [Fact] + public Task DateTimeOffset_from_string_3() => _g + .Inject("2007-12-03T10:15:30+01:00") + .Cast() + .Verify(); + [Fact] public async Task Deserialization_of_typed_results_is_only_called_once() {