From 0abb148c59d0aafdd8fb205e1a99d574f37f2123 Mon Sep 17 00:00:00 2001 From: Stefan Bertels Date: Fri, 20 Dec 2019 11:23:39 +0100 Subject: [PATCH] adds more tests and partial fix for union serialization --- LanguageExt.CodeGen/CodeGenUtil.cs | 14 +-- .../UnionCustomJsonSerializerTests.cs | 107 ++++++++++++++++++ LanguageExt.Tests/UnionJsonSerializerTests.cs | 40 +++++++ LanguageExt.Tests/UnionTests.cs | 23 ---- 4 files changed, 151 insertions(+), 33 deletions(-) create mode 100644 LanguageExt.Tests/UnionCustomJsonSerializerTests.cs create mode 100644 LanguageExt.Tests/UnionJsonSerializerTests.cs delete mode 100644 LanguageExt.Tests/UnionTests.cs diff --git a/LanguageExt.CodeGen/CodeGenUtil.cs b/LanguageExt.CodeGen/CodeGenUtil.cs index 8de67c3a9..71b0ea1a5 100644 --- a/LanguageExt.CodeGen/CodeGenUtil.cs +++ b/LanguageExt.CodeGen/CodeGenUtil.cs @@ -1980,7 +1980,7 @@ static MemberDeclarationSyntax[] MakeSerialisationMembers(string typeName, List< Identifier(typeName)) .WithModifiers( TokenList( - Token(SyntaxKind.ProtectedKeyword))) + Token(SyntaxKind.PrivateKeyword))) .WithParameterList( ParameterList( SeparatedList( @@ -2213,9 +2213,7 @@ public static TypeDeclarationSyntax MakeCaseType( Token(SyntaxKind.CommaToken), SimpleBaseType(thisComparableType), Token(SyntaxKind.CommaToken), - SimpleBaseType(comparableType), - Token(SyntaxKind.CommaToken), - SimpleBaseType(serializableType) + SimpleBaseType(comparableType) }))), BaseSpec.Abstract => type.WithBaseList( @@ -2228,9 +2226,7 @@ public static TypeDeclarationSyntax MakeCaseType( Token(SyntaxKind.CommaToken), SimpleBaseType(thisComparableType), Token(SyntaxKind.CommaToken), - SimpleBaseType(comparableType), - Token(SyntaxKind.CommaToken), - SimpleBaseType(serializableType) + SimpleBaseType(comparableType) }))), _ => type.WithBaseList( @@ -2241,9 +2237,7 @@ public static TypeDeclarationSyntax MakeCaseType( Token(SyntaxKind.CommaToken), SimpleBaseType(thisComparableType), Token(SyntaxKind.CommaToken), - SimpleBaseType(comparableType), - Token(SyntaxKind.CommaToken), - SimpleBaseType(serializableType) + SimpleBaseType(comparableType) }))) }; diff --git a/LanguageExt.Tests/UnionCustomJsonSerializerTests.cs b/LanguageExt.Tests/UnionCustomJsonSerializerTests.cs new file mode 100644 index 000000000..fddcf6a1d --- /dev/null +++ b/LanguageExt.Tests/UnionCustomJsonSerializerTests.cs @@ -0,0 +1,107 @@ +using System; +using System.Linq; +using System.Reflection; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using Xunit; + +namespace LanguageExt.Tests +{ + public partial class UnionCustomJsonSerializerTests + { + [Union] + [MyUnion] + abstract partial class UnionTestClass + { + public abstract UnionTestClass A(); + public abstract UnionTestClass B(int i); + public abstract UnionTestClass C(int i); + public abstract UnionTestClass D(int i, int j); + } + + [AttributeUsage(AttributeTargets.Class, Inherited = false)] + public class MyUnionAttribute : Attribute + { + } + + public class UnionJsonReadConverter : JsonConverter + { + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) => throw new NotImplementedException(); + + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + string typeName = null; + JObject typeValueToken = null; + if (reader.TokenType != JsonToken.StartObject) throw new Exception(); + while (reader.Read()) + { + if (reader.TokenType == JsonToken.EndObject) break; + if (reader.TokenType != JsonToken.PropertyName) throw new Exception(); + if ((string) reader.Value == "Type") + { + if (!reader.Read()) throw new Exception(); + if (typeName != null) throw new Exception(); + if (reader.TokenType != JsonToken.String) throw new Exception(); + typeName = (string) reader.Value; + } + else if ((string) reader.Value == "Value") + { + if (typeValueToken != null) throw new Exception(); + if (!reader.Read()) throw new Exception(); + if (reader.TokenType != JsonToken.StartObject) throw new Exception(); + typeValueToken = JObject.Load(reader); + } + } + + var type = objectType.Assembly.GetTypes().Where(_ => objectType.IsAssignableFrom(_) && _.Name == typeName).Single(); + var result = typeValueToken.ToObject(type, serializer); + return result; + } + + public override bool CanWrite => false; + public override bool CanConvert(Type objectType) => objectType.IsClass && objectType.GetCustomAttribute() != null; + } + + public class UnionJsonWriteConverter : JsonConverter + { + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + var t = value.GetType(); + writer.WriteStartObject(); + writer.WritePropertyName("Type"); + writer.WriteValue(t.Name); + writer.WritePropertyName("Value"); + writer.WriteStartObject(); + foreach (var fieldInfo in t.GetFields(BindingFlags.Instance | BindingFlags.Public)) + { + writer.WritePropertyName(fieldInfo.Name); + serializer.Serialize(writer, fieldInfo.GetValue(value)); + } + writer.WriteEndObject(); + writer.WriteEndObject(); + } + + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) => + new NotImplementedException(); + + public override bool CanRead => false; + + public override bool CanConvert(Type objectType) => objectType.IsClass && objectType.BaseType?.BaseType?.GetCustomAttribute() != null; + } + + [Fact] + public void UnionFromJson() + { + var json = @"{""Type"":""B"",""Value"":{""I"":42}}"; + var x = JsonConvert.DeserializeObject(json, new UnionJsonReadConverter()) as B; + Assert.Equal(42, x?.I); + } + + [Fact] + public void UnionToJson() + { + var x = JsonConvert.SerializeObject(UnionTestClassCon.B(42), new UnionJsonWriteConverter()); + Assert.Equal(@"{""Type"":""B"",""Value"":{""I"":42}}", x); + } + } +} diff --git a/LanguageExt.Tests/UnionJsonSerializerTests.cs b/LanguageExt.Tests/UnionJsonSerializerTests.cs new file mode 100644 index 000000000..2582e6af0 --- /dev/null +++ b/LanguageExt.Tests/UnionJsonSerializerTests.cs @@ -0,0 +1,40 @@ +using System.Runtime.Serialization; +using Newtonsoft.Json; +using Xunit; + +namespace LanguageExt.Tests +{ + public partial class UnionJsonSerializerTests + { + [Union] + public abstract partial class LightControl + { + public abstract LightControl OnOff(bool enabled); + public abstract LightControl Dimmer(int value); + } + + [Fact] + public void UnionInstanceFromJson() + { + var json = @"{""Value"":100}"; + var x = JsonConvert.DeserializeObject(json); + Assert.Equal(100, x.Value); + } + + [Fact] + public void UnionInstanceToJson() + { + Assert.Equal(@"{""Value"":100}", JsonConvert.SerializeObject(LightControlCon.Dimmer(100))); + } + + [Fact] + public void UnionRoundTrip() + { + Assert.Equal(LightControlCon.OnOff(true), JsonConvert.DeserializeObject(JsonConvert.SerializeObject(LightControlCon.OnOff(true)))); + Assert.Equal(LightControlCon.OnOff(false), JsonConvert.DeserializeObject(JsonConvert.SerializeObject(LightControlCon.OnOff(false)))); + Assert.Equal(LightControlCon.Dimmer(10), JsonConvert.DeserializeObject(JsonConvert.SerializeObject(LightControlCon.Dimmer(10)))); + Assert.Equal(LightControlCon.Dimmer(90), JsonConvert.DeserializeObject(JsonConvert.SerializeObject(LightControlCon.Dimmer(90)))); + + } + } +} diff --git a/LanguageExt.Tests/UnionTests.cs b/LanguageExt.Tests/UnionTests.cs deleted file mode 100644 index 94817ac1f..000000000 --- a/LanguageExt.Tests/UnionTests.cs +++ /dev/null @@ -1,23 +0,0 @@ -using Newtonsoft.Json; -using Xunit; - -namespace LanguageExt.Tests -{ - public partial class UnionTests - { - [Union] - public abstract partial class LightControl - { - public abstract LightControl OnOff(bool enabled); - public abstract LightControl Dimmer(int value); - } - - [Fact] - public void FromJson() - { - var json = @"{""Value"": 100}"; - var x = JsonConvert.DeserializeObject(json); - Assert.Equal(100, x.Value); - } - } -}