From 871b93109c28ab9eabade9cb69e499ec1d3eee1a Mon Sep 17 00:00:00 2001 From: Lucas Pedroza Date: Wed, 3 Jul 2024 17:15:23 +0200 Subject: [PATCH 1/2] Add remaining type-based deserializers --- .../Serialization/Deserializer.Tests.cs | 8 ++ Fauna.Test/Serialization/RoundTrip.Tests.cs | 54 +++++++++++++ Fauna.Test/Serialization/TestClasses.cs | 6 ++ Fauna/Linq/Deserializers.cs | 4 +- Fauna/Mapping/FieldInfo.cs | 5 +- Fauna/Serialization/ClassDeserializer.cs | 5 +- Fauna/Serialization/Deserializer.cs | 12 +-- Fauna/Serialization/IDeserializer.cs | 3 + Fauna/Serialization/NumberDeserializers.cs | 78 ++++++++++++++----- 9 files changed, 145 insertions(+), 30 deletions(-) diff --git a/Fauna.Test/Serialization/Deserializer.Tests.cs b/Fauna.Test/Serialization/Deserializer.Tests.cs index 033ce261..c8b71c6b 100644 --- a/Fauna.Test/Serialization/Deserializer.Tests.cs +++ b/Fauna.Test/Serialization/Deserializer.Tests.cs @@ -10,6 +10,7 @@ namespace Fauna.Test.Serialization; public class DeserializerTests { private readonly MappingContext ctx; + private const string DocumentWithShortWire = @"{""@doc"":{""id"":""123"",""coll"":{""@mod"":""MyColl""},""ts"":{""@time"":""2023-12-15T01:01:01.0010010Z""},""a_short"":{""@int"":""42""}}}"; public DeserializerTests() { @@ -317,6 +318,13 @@ public void DeserializeDocumentGeneric() Assert.AreEqual("name_value", actual["name"]); } + [Test] + public void DeserializeDocumentAsClassWithShort() + { + var actual = Deserialize(DocumentWithShortWire); + Assert.AreEqual((short)42, actual.AShort); + } + [Test] public void DeserializeNonNullDocumentGeneric() { diff --git a/Fauna.Test/Serialization/RoundTrip.Tests.cs b/Fauna.Test/Serialization/RoundTrip.Tests.cs index 0b69712b..a0b5f147 100644 --- a/Fauna.Test/Serialization/RoundTrip.Tests.cs +++ b/Fauna.Test/Serialization/RoundTrip.Tests.cs @@ -14,12 +14,18 @@ public class RoundTripTests private const string LongWire = @"{""@long"":""42""}"; private const string DoubleWire = @"{""@double"":""42""}"; private const string DoubleWithDecimalWire = @"{""@double"":""42.2""}"; + private const string TrueWire = "true"; + private const string FalseWire = "false"; + private const string DateTimeWire = @"{""@time"":""2023-12-15T01:01:01.0010011Z""}"; + private const string DateWire = @"{""@date"":""2023-12-15""}"; + private const string ModuleWire = @"{""@mod"":""Foo""}"; private const string DocumentWire = @"{""@doc"":{""id"":""123"",""coll"":{""@mod"":""MyColl""},""ts"":{""@time"":""2023-12-15T01:01:01.0010010Z""},""user_field"":""user_value""}}"; private const string DocumentRefWire = @"{""@ref"":{""id"":""123"",""coll"":{""@mod"":""MyColl""}}}"; private const string NullDocumentWire = @"{""@ref"":{""id"":""123"",""coll"":{""@mod"":""MyColl""},""exists"":false,""cause"":""not found""}}"; private const string NamedDocumentWire = @"{""@doc"":{""name"":""Foo"",""coll"":{""@mod"":""MyColl""},""ts"":{""@time"":""2023-12-15T01:01:01.0010010Z""},""user_field"":""user_value""}}"; private const string NullNamedDocumentWire = @"{""@ref"":{""name"":""Foo"",""coll"":{""@mod"":""MyColl""},""exists"":false,""cause"":""not found""}}"; private const string NamedDocumentRefWire = @"{""@ref"":{""name"":""Foo"",""coll"":{""@mod"":""MyColl""}}}"; + private const string ObjectWithShortWire = @"{""a_short"":{""@int"":""42""}}"; public static string Serialize(object? obj) { @@ -115,6 +121,54 @@ public void RoundTripDouble() Assert.AreEqual(DoubleWithDecimalWire, serialized); } + [Test] + public void RoundTripTrue() + { + var deserialized = Deserialize(TrueWire); + var serialized = Serialize(deserialized); + Assert.AreEqual(TrueWire, serialized); + } + + [Test] + public void RoundTripFalse() + { + var deserialized = Deserialize(FalseWire); + var serialized = Serialize(deserialized); + Assert.AreEqual(FalseWire, serialized); + } + + [Test] + public void RoundTripDateTime() + { + var deserialized = Deserialize(DateTimeWire); + var serialized = Serialize(deserialized); + Assert.AreEqual(DateTimeWire, serialized); + } + + [Test] + public void RoundTripDateOnly() + { + var deserialized = Deserialize(DateWire); + var serialized = Serialize(deserialized); + Assert.AreEqual(DateWire, serialized); + } + + [Test] + public void RoundTripModule() + { + var deserialized = Deserialize(ModuleWire); + var serialized = Serialize(deserialized); + Assert.AreEqual(ModuleWire, serialized); + } + + [Test] + public void RoundTripClassWithShort() + { + var deserialized = Deserialize(ObjectWithShortWire); + var serialized = Serialize(deserialized); + Assert.AreEqual(ObjectWithShortWire, serialized); + } + [Test] public void RoundTripClassAsDocumentIsNotSupported() { diff --git a/Fauna.Test/Serialization/TestClasses.cs b/Fauna.Test/Serialization/TestClasses.cs index dffde9f8..61e3ec77 100644 --- a/Fauna.Test/Serialization/TestClasses.cs +++ b/Fauna.Test/Serialization/TestClasses.cs @@ -22,6 +22,12 @@ class ClassForDocument [Field("user_field")] public string? UserField { get; set; } } +[Object] +class ClassWithShort +{ + [Field("a_short")] public short AShort { get; set; } +} + [Object] class ClassForDocumentWithIdString { diff --git a/Fauna/Linq/Deserializers.cs b/Fauna/Linq/Deserializers.cs index 2d6f3150..d41037ff 100644 --- a/Fauna/Linq/Deserializers.cs +++ b/Fauna/Linq/Deserializers.cs @@ -48,6 +48,6 @@ public ProjectionDeserializer(IEnumerable fields) return values; } - private SerializationException UnexpectedToken(TokenType tokenType) => - new SerializationException($"Unexpected token while deserializing LINQ element: {tokenType}"); + private new static SerializationException UnexpectedToken(TokenType tokenType) => + new($"Unexpected token while deserializing LINQ element: {tokenType}"); } diff --git a/Fauna/Mapping/FieldInfo.cs b/Fauna/Mapping/FieldInfo.cs index bb1151ab..3808250f 100644 --- a/Fauna/Mapping/FieldInfo.cs +++ b/Fauna/Mapping/FieldInfo.cs @@ -50,7 +50,10 @@ internal IDeserializer Deserializer if (_deserializer is null) { _deserializer = Fauna.Serialization.Deserializer.Generate(_ctx, Type); - if (IsNullable && _deserializer.GetType().GetGenericTypeDefinition() != typeof(NullableStructDeserializer<>)) + if (IsNullable && (!_deserializer.GetType().IsGenericType || + (_deserializer.GetType().IsGenericType && + _deserializer.GetType().GetGenericTypeDefinition() != + typeof(NullableStructDeserializer<>)))) { var deserType = typeof(NullableDeserializer<>).MakeGenericType(new[] { Type }); var deser = Activator.CreateInstance(deserType, new[] { _deserializer }); diff --git a/Fauna/Serialization/ClassDeserializer.cs b/Fauna/Serialization/ClassDeserializer.cs index 8ffcc03a..f9d52c74 100644 --- a/Fauna/Serialization/ClassDeserializer.cs +++ b/Fauna/Serialization/ClassDeserializer.cs @@ -156,7 +156,6 @@ private void TrySetName(object instance, string name) } } - private SerializationException UnexpectedToken(TokenType tokenType) => - new SerializationException( - $"Unexpected token while deserializing into class {_info.Type.Name}: {tokenType}"); + private new SerializationException UnexpectedToken(TokenType tokenType) => + new($"Unexpected token while deserializing into class {_info.Type.Name}: {tokenType}"); } diff --git a/Fauna/Serialization/Deserializer.cs b/Fauna/Serialization/Deserializer.cs index 82d8ca7f..03e7b9f8 100644 --- a/Fauna/Serialization/Deserializer.cs +++ b/Fauna/Serialization/Deserializer.cs @@ -11,10 +11,10 @@ public static class Deserializer /// /// The dynamic data deserializer. /// - public static IDeserializer Dynamic { get; } = DynamicDeserializer.Singleton; + public static IDeserializer Dynamic => DynamicDeserializer.Singleton; private static readonly CheckedDeserializer _object = new(); - private static readonly CheckedDeserializer _string = new(); + private static readonly StringDeserializer _string = new(); private static readonly ByteDeserializer _byte = new(); private static readonly SByteDeserializer _sbyte = new(); private static readonly ShortDeserializer _short = new(); @@ -24,10 +24,10 @@ public static class Deserializer private static readonly LongDeserializer _long = new(); private static readonly FloatDeserializer _float = new(); private static readonly DoubleDeserializer _double = new(); - private static readonly CheckedDeserializer _dateOnly = new(); - private static readonly CheckedDeserializer _dateTime = new(); - private static readonly CheckedDeserializer _bool = new(); - private static readonly CheckedDeserializer _module = new(); + private static readonly DateOnlyDeserializer _dateOnly = new(); + private static readonly DateTimeDeserializer _dateTime = new(); + private static readonly BooleanDeserializer _bool = new(); + private static readonly ModuleDeserializer _module = new(); private static readonly DocumentDeserializer _doc = new(); private static readonly DocumentDeserializer _namedDoc = new(); private static readonly DocumentDeserializer _docRef = new(); diff --git a/Fauna/Serialization/IDeserializer.cs b/Fauna/Serialization/IDeserializer.cs index 2a2f215c..b4afffe5 100644 --- a/Fauna/Serialization/IDeserializer.cs +++ b/Fauna/Serialization/IDeserializer.cs @@ -18,4 +18,7 @@ public abstract class BaseDeserializer : IDeserializer Deserialize(context, ref reader); public abstract T Deserialize(MappingContext context, ref Utf8FaunaReader reader); + + protected static SerializationException UnexpectedToken(TokenType token) => + new($"Unexpected token while deserializing: {token}"); } diff --git a/Fauna/Serialization/NumberDeserializers.cs b/Fauna/Serialization/NumberDeserializers.cs index 4e346864..01017dad 100644 --- a/Fauna/Serialization/NumberDeserializers.cs +++ b/Fauna/Serialization/NumberDeserializers.cs @@ -1,16 +1,26 @@ using Fauna.Mapping; +using Fauna.Types; namespace Fauna.Serialization; +internal class StringDeserializer : BaseDeserializer +{ + public override string? Deserialize(MappingContext ctx, ref Utf8FaunaReader reader) => + reader.CurrentTokenType switch + { + TokenType.String => reader.GetString(), + _ => throw UnexpectedToken(reader.CurrentTokenType) + }; +} + internal class ByteDeserializer : BaseDeserializer { public override byte Deserialize(MappingContext context, ref Utf8FaunaReader reader) => reader.CurrentTokenType switch { TokenType.Int => reader.GetByte(), - _ => throw new SerializationException( - $"Unexpected token while deserializing: {reader.CurrentTokenType}"), + _ => throw UnexpectedToken(reader.CurrentTokenType) }; } @@ -20,8 +30,7 @@ public override sbyte Deserialize(MappingContext context, ref Utf8FaunaReader re reader.CurrentTokenType switch { TokenType.Int => reader.GetUnsignedByte(), - _ => throw new SerializationException( - $"Unexpected token while deserializing: {reader.CurrentTokenType}"), + _ => throw UnexpectedToken(reader.CurrentTokenType) }; } @@ -32,8 +41,7 @@ public override short Deserialize(MappingContext context, ref Utf8FaunaReader re reader.CurrentTokenType switch { TokenType.Int or TokenType.Long => reader.GetShort(), - _ => throw new SerializationException( - $"Unexpected token while deserializing: {reader.CurrentTokenType}"), + _ => throw UnexpectedToken(reader.CurrentTokenType) }; } @@ -43,8 +51,7 @@ public override ushort Deserialize(MappingContext context, ref Utf8FaunaReader r reader.CurrentTokenType switch { TokenType.Int or TokenType.Long => reader.GetUnsignedShort(), - _ => throw new SerializationException( - $"Unexpected token while deserializing: {reader.CurrentTokenType}"), + _ => throw UnexpectedToken(reader.CurrentTokenType) }; } @@ -54,8 +61,7 @@ public override int Deserialize(MappingContext context, ref Utf8FaunaReader read reader.CurrentTokenType switch { TokenType.Int or TokenType.Long => reader.GetInt(), - _ => throw new SerializationException( - $"Unexpected token while deserializing: {reader.CurrentTokenType}"), + _ => throw UnexpectedToken(reader.CurrentTokenType) }; } @@ -65,8 +71,7 @@ public override uint Deserialize(MappingContext context, ref Utf8FaunaReader rea reader.CurrentTokenType switch { TokenType.Int or TokenType.Long => reader.GetUnsignedInt(), - _ => throw new SerializationException( - $"Unexpected token while deserializing: {reader.CurrentTokenType}"), + _ => throw UnexpectedToken(reader.CurrentTokenType) }; } @@ -76,8 +81,7 @@ public override long Deserialize(MappingContext context, ref Utf8FaunaReader rea reader.CurrentTokenType switch { TokenType.Int or TokenType.Long => reader.GetLong(), - _ => throw new SerializationException( - $"Unexpected token while deserializing: {reader.CurrentTokenType}"), + _ => throw UnexpectedToken(reader.CurrentTokenType) }; } @@ -87,8 +91,7 @@ public override float Deserialize(MappingContext context, ref Utf8FaunaReader re reader.CurrentTokenType switch { TokenType.Int or TokenType.Long or TokenType.Double => reader.GetFloat(), - _ => throw new SerializationException( - $"Unexpected token while deserializing: {reader.CurrentTokenType}"), + _ => throw UnexpectedToken(reader.CurrentTokenType) }; } @@ -98,7 +101,46 @@ public override double Deserialize(MappingContext context, ref Utf8FaunaReader r reader.CurrentTokenType switch { TokenType.Int or TokenType.Long or TokenType.Double => reader.GetDouble(), - _ => throw new SerializationException( - $"Unexpected token while deserializing: {reader.CurrentTokenType}"), + _ => throw UnexpectedToken(reader.CurrentTokenType) + }; +} + +internal class BooleanDeserializer : BaseDeserializer +{ + public override bool Deserialize(MappingContext context, ref Utf8FaunaReader reader) => + reader.CurrentTokenType switch + { + TokenType.True or TokenType.False => reader.GetBoolean(), + _ => throw UnexpectedToken(reader.CurrentTokenType) + }; +} + +internal class DateOnlyDeserializer : BaseDeserializer +{ + public override DateOnly Deserialize(MappingContext context, ref Utf8FaunaReader reader) => + reader.CurrentTokenType switch + { + TokenType.Date => reader.GetDate(), + _ => throw UnexpectedToken(reader.CurrentTokenType) + }; +} + +internal class DateTimeDeserializer : BaseDeserializer +{ + public override DateTime Deserialize(MappingContext context, ref Utf8FaunaReader reader) => + reader.CurrentTokenType switch + { + TokenType.Time => reader.GetTime(), + _ => throw UnexpectedToken(reader.CurrentTokenType) + }; +} + +internal class ModuleDeserializer : BaseDeserializer +{ + public override Module Deserialize(MappingContext context, ref Utf8FaunaReader reader) => + reader.CurrentTokenType switch + { + TokenType.Module => reader.GetModule(), + _ => throw UnexpectedToken(reader.CurrentTokenType) }; } From 7e9331e68a1babf94de9d923c6ca25b585d0c6d3 Mon Sep 17 00:00:00 2001 From: Lucas Pedroza Date: Wed, 3 Jul 2024 17:16:05 +0200 Subject: [PATCH 2/2] Rename NumberDeserializers -> TypeDeserializers --- .../{NumberDeserializers.cs => TypeDeserializers.cs} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename Fauna/Serialization/{NumberDeserializers.cs => TypeDeserializers.cs} (100%) diff --git a/Fauna/Serialization/NumberDeserializers.cs b/Fauna/Serialization/TypeDeserializers.cs similarity index 100% rename from Fauna/Serialization/NumberDeserializers.cs rename to Fauna/Serialization/TypeDeserializers.cs