From 60fad3dd9f21e5047b1328b34497a4206eab982a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Oryszak?= Date: Sun, 26 Apr 2020 17:48:17 +0200 Subject: [PATCH] Support System.Text.Json (#11) * Support System.Text.Json * Fix templates formatting * Consolidate summary comments --- .../StronglyTypedIdAttribute.cs | 14 +- .../StronglyTypedIdJsonConverter.cs | 11 + .../BaseSyntaxTreeGenerator.cs | 181 ++++++++++++ .../GuidSyntaxTreeGenerator.cs | 271 ++++++++++-------- .../IntSyntaxTreeGenerator.cs | 260 +++++++++-------- .../StringSyntaxTreeGenerator.cs | 262 +++++++++-------- .../StronglyTypedIdGenerator.cs | 26 +- .../templates/GuidId.cs | 18 +- .../templates/IntId.cs | 18 +- .../templates/StringId.cs | 18 +- test/StronglyTypedId.Tests/GeneratedId1.cs | 7 - test/StronglyTypedId.Tests/GeneratedId2.cs | 10 - .../GeneratedStronglyTypedIdTests.cs | 105 ------- test/StronglyTypedId.Tests/GuidIdTests.cs | 147 ++++++++++ test/StronglyTypedId.Tests/IntId.cs | 8 - test/StronglyTypedId.Tests/IntIdTests.cs | 72 +++-- .../NoJsonGeneratedId.cs | 5 - test/StronglyTypedId.Tests/StringId.cs | 8 - test/StronglyTypedId.Tests/StringIdTests.cs | 79 +++-- .../StronglyTypedId.Tests.csproj | 1 + test/StronglyTypedId.Tests/Types/GuidId.cs | 20 ++ test/StronglyTypedId.Tests/Types/IntId.cs | 17 ++ test/StronglyTypedId.Tests/Types/StringId.cs | 17 ++ version.props | 2 +- 24 files changed, 1038 insertions(+), 539 deletions(-) create mode 100644 src/StronglyTypedId.Attributes/StronglyTypedIdJsonConverter.cs create mode 100644 src/StronglyTypedId.Generator/BaseSyntaxTreeGenerator.cs delete mode 100644 test/StronglyTypedId.Tests/GeneratedId1.cs delete mode 100644 test/StronglyTypedId.Tests/GeneratedId2.cs delete mode 100644 test/StronglyTypedId.Tests/GeneratedStronglyTypedIdTests.cs create mode 100644 test/StronglyTypedId.Tests/GuidIdTests.cs delete mode 100644 test/StronglyTypedId.Tests/IntId.cs delete mode 100644 test/StronglyTypedId.Tests/NoJsonGeneratedId.cs delete mode 100644 test/StronglyTypedId.Tests/StringId.cs create mode 100644 test/StronglyTypedId.Tests/Types/GuidId.cs create mode 100644 test/StronglyTypedId.Tests/Types/IntId.cs create mode 100644 test/StronglyTypedId.Tests/Types/StringId.cs diff --git a/src/StronglyTypedId.Attributes/StronglyTypedIdAttribute.cs b/src/StronglyTypedId.Attributes/StronglyTypedIdAttribute.cs index 04b48ee02..6f4adbb05 100644 --- a/src/StronglyTypedId.Attributes/StronglyTypedIdAttribute.cs +++ b/src/StronglyTypedId.Attributes/StronglyTypedIdAttribute.cs @@ -12,17 +12,20 @@ public class StronglyTypedIdAttribute : Attribute /// /// If true generates a JsonConverter for the strongly typed ID (requires a reference to Newtonsoft.Json in the project) /// The to use to store the strongly-typed ID value. Defaults to + /// JSON library used to serialize/deserialize strongly-typed ID value. Defaults to public StronglyTypedIdAttribute( bool generateJsonConverter = true, - StronglyTypedIdBackingType backingType = StronglyTypedIdBackingType.Guid) + StronglyTypedIdBackingType backingType = StronglyTypedIdBackingType.Guid, + StronglyTypedIdJsonConverter jsonConverter = StronglyTypedIdJsonConverter.NewtonsoftJson) { GenerateJsonConverter = generateJsonConverter; BackingType = backingType; + JsonConverter = jsonConverter; } /// - /// If true generates a JsonConverter for the strongly typed ID - /// (requires a reference to Newtonsoft.Json in the project) + /// If true generates a JsonConverter for the strongly-typed ID + /// (requires a reference to Newtonsoft.Json and/or System.Text.Json in the project) /// public bool GenerateJsonConverter { get; } @@ -31,4 +34,9 @@ public StronglyTypedIdAttribute( /// public StronglyTypedIdBackingType BackingType { get; } + /// + /// JSON library used to serialize/deserialize strongly-typed ID value + /// + public StronglyTypedIdJsonConverter JsonConverter { get; } + } diff --git a/src/StronglyTypedId.Attributes/StronglyTypedIdJsonConverter.cs b/src/StronglyTypedId.Attributes/StronglyTypedIdJsonConverter.cs new file mode 100644 index 000000000..f477753af --- /dev/null +++ b/src/StronglyTypedId.Attributes/StronglyTypedIdJsonConverter.cs @@ -0,0 +1,11 @@ +using System; + +/// +/// JSON library used to serialize/deserialize strongly-typed ID value +/// +[Flags] +public enum StronglyTypedIdJsonConverter +{ + NewtonsoftJson = 1, + SystemTextJson = 2 +} \ No newline at end of file diff --git a/src/StronglyTypedId.Generator/BaseSyntaxTreeGenerator.cs b/src/StronglyTypedId.Generator/BaseSyntaxTreeGenerator.cs new file mode 100644 index 000000000..48a5cd857 --- /dev/null +++ b/src/StronglyTypedId.Generator/BaseSyntaxTreeGenerator.cs @@ -0,0 +1,181 @@ +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using System.Collections.Generic; +using System.Linq; +using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; + +namespace StronglyTypedId.Generator +{ + public abstract class BaseSyntaxTreeGenerator + { + // Generated using https://roslynquoter.azurewebsites.net/ + public SyntaxList CreateStronglyTypedIdSyntax(StructDeclarationSyntax original, bool generateJsonConverter, StronglyTypedIdJsonConverter jsonProvider) + { + // Get the name of the decorated member + var idName = original.Identifier.ValueText; + var typeConverterName = idName + "TypeConverter"; + + var attributes = new List() { GetTypeConverterAttribute(typeConverterName) }; + var members = GetMembers(idName).Append(GetTypeConverter(typeConverterName, idName)); + + if (generateJsonConverter) + { + if (jsonProvider.HasFlag(StronglyTypedIdJsonConverter.NewtonsoftJson)) + { + var jsonConverterName = idName + "NewtonsoftJsonConverter"; + attributes.Add(GetNewtonsoftJsonConverterAttribute(jsonConverterName)); + members = members.Append(GetNewtonsoftJsonConverter(jsonConverterName, idName)); + } + + if (jsonProvider.HasFlag(StronglyTypedIdJsonConverter.SystemTextJson)) + { + var jsonConverterName = idName + "SystemTextJsonConverter"; + attributes.Add(GetSystemTextJsonConverterAttribute(jsonConverterName)); + members = members.Append(GetSystemTextJsonConverter(jsonConverterName, idName)); + } + } + + return SingletonList( + StructDeclaration(idName) + .WithAttributeLists(List(attributes)) + .WithModifiers( + TokenList( + new[] + { + Token(SyntaxKind.ReadOnlyKeyword), + Token(SyntaxKind.PartialKeyword) + })) + .WithBaseList( + BaseList( + SeparatedList( + new SyntaxNodeOrToken[] + { + SimpleBaseType( + QualifiedName( + IdentifierName("System"), + GenericName( + Identifier("IComparable")) + .WithTypeArgumentList( + TypeArgumentList( + SingletonSeparatedList( + IdentifierName(idName)))))), + Token(SyntaxKind.CommaToken), + SimpleBaseType( + QualifiedName( + IdentifierName("System"), + GenericName( + Identifier("IEquatable")) + .WithTypeArgumentList( + TypeArgumentList( + SingletonSeparatedList( + IdentifierName(idName)))))) + }))) + .WithMembers(List(members)) + .WithCloseBraceToken( + Token( + TriviaList(), + SyntaxKind.CloseBraceToken, + TriviaList()))); + } + + protected abstract IEnumerable GetMembers(string idName); + + #region Type Converter + + protected abstract ClassDeclarationSyntax GetTypeConverter(string typeConverterName, string idName); + + protected AttributeListSyntax GetTypeConverterAttribute(string typeConverterName) + { + return AttributeList( + SingletonSeparatedList( + Attribute( + QualifiedName( + QualifiedName( + IdentifierName("System"), + IdentifierName("ComponentModel")), + IdentifierName("TypeConverter"))) + .WithArgumentList( + AttributeArgumentList( + SingletonSeparatedList( + AttributeArgument( + TypeOfExpression( + IdentifierName(typeConverterName)))))))); + } + + #endregion + + + #region Json Converters + + protected ClassDeclarationSyntax GetJsonConverter(string jsonConverterName, string idName, StronglyTypedIdJsonConverter jsonProvider) + { + switch (jsonProvider) + { + case StronglyTypedIdJsonConverter.SystemTextJson: + return GetSystemTextJsonConverter(jsonConverterName, idName); + case StronglyTypedIdJsonConverter.NewtonsoftJson: + default: + return GetNewtonsoftJsonConverter(jsonConverterName, idName); + } + } + + protected abstract ClassDeclarationSyntax GetNewtonsoftJsonConverter(string jsonConverterName, string idName); + + protected abstract ClassDeclarationSyntax GetSystemTextJsonConverter(string jsonConverterName, string idName); + + protected AttributeListSyntax GetJsonConverterAttribute(string jsonConverterName, StronglyTypedIdJsonConverter jsonProvider) + { + switch (jsonProvider) + { + case StronglyTypedIdJsonConverter.SystemTextJson: + return GetSystemTextJsonConverterAttribute(jsonConverterName); + case StronglyTypedIdJsonConverter.NewtonsoftJson: + default: + return GetNewtonsoftJsonConverterAttribute(jsonConverterName); + } + } + + protected AttributeListSyntax GetNewtonsoftJsonConverterAttribute(string jsonConverterName) + { + return AttributeList( + SingletonSeparatedList( + Attribute( + QualifiedName( + QualifiedName( + IdentifierName("Newtonsoft"), + IdentifierName("Json")), + IdentifierName("JsonConverter"))) + .WithArgumentList( + AttributeArgumentList( + SingletonSeparatedList( + AttributeArgument( + TypeOfExpression( + IdentifierName(jsonConverterName)))))))); + } + + protected AttributeListSyntax GetSystemTextJsonConverterAttribute(string jsonConverterName) + { + return AttributeList( + SingletonSeparatedList( + Attribute( + QualifiedName( + QualifiedName( + QualifiedName( + QualifiedName( + IdentifierName("System"), + IdentifierName("Text")), + IdentifierName("Json")), + IdentifierName("Serialization")), + IdentifierName("JsonConverter"))) + .WithArgumentList( + AttributeArgumentList( + SingletonSeparatedList( + AttributeArgument( + TypeOfExpression( + IdentifierName(jsonConverterName)))))))); + } + + #endregion + } +} \ No newline at end of file diff --git a/src/StronglyTypedId.Generator/GuidSyntaxTreeGenerator.cs b/src/StronglyTypedId.Generator/GuidSyntaxTreeGenerator.cs index e4d8a898a..41e2be046 100644 --- a/src/StronglyTypedId.Generator/GuidSyntaxTreeGenerator.cs +++ b/src/StronglyTypedId.Generator/GuidSyntaxTreeGenerator.cs @@ -1,86 +1,14 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; -using System; using System.Collections.Generic; -using System.Linq; -using System.Text; using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; namespace StronglyTypedId.Generator { - public static class GuidSyntaxTreeGenerator + public class GuidSyntaxTreeGenerator : BaseSyntaxTreeGenerator { - // Generated using https://roslynquoter.azurewebsites.net/ - public static SyntaxList CreateStronglyTypedIdSyntax(StructDeclarationSyntax original, bool generateJsonConverter) - { - // Get the name of the decorated member - var idName = original.Identifier.ValueText; - var typeConverterName = idName + "TypeConverter"; - var jsonConverterName = idName + "JsonConverter"; - - var typeConverterAttribute = GetTypeConverterAttribute(typeConverterName); - - var attributes = generateJsonConverter - ? List( - new AttributeListSyntax[] - { - typeConverterAttribute, - GetJsonConverterAttribute(jsonConverterName) - }) - : SingletonList(typeConverterAttribute); - - var members = GetMembers(idName) - .Append(GetTypeConverter(typeConverterName, idName)); - if (generateJsonConverter) - { - members = members.Append(GetJsonConverter(jsonConverterName, idName)); - } - - return SingletonList( - StructDeclaration(idName) - .WithAttributeLists(attributes) - .WithModifiers( - TokenList( - new[] - { - Token(SyntaxKind.ReadOnlyKeyword), - Token(SyntaxKind.PartialKeyword) - })) - .WithBaseList( - BaseList( - SeparatedList( - new SyntaxNodeOrToken[] - { - SimpleBaseType( - QualifiedName( - IdentifierName("System"), - GenericName( - Identifier("IComparable")) - .WithTypeArgumentList( - TypeArgumentList( - SingletonSeparatedList( - IdentifierName(idName)))))), - Token(SyntaxKind.CommaToken), - SimpleBaseType( - QualifiedName( - IdentifierName("System"), - GenericName( - Identifier("IEquatable")) - .WithTypeArgumentList( - TypeArgumentList( - SingletonSeparatedList( - IdentifierName(idName)))))) - }))) - .WithMembers(List(members)) - .WithCloseBraceToken( - Token( - TriviaList(), - SyntaxKind.CloseBraceToken, - TriviaList()))); - } - - private static IEnumerable GetMembers(string idName) + protected override IEnumerable GetMembers(string idName) { yield return PropertyDeclaration( QualifiedName( @@ -419,7 +347,7 @@ private static IEnumerable GetMembers(string idName) Token(SyntaxKind.SemicolonToken)); } - private static ClassDeclarationSyntax GetJsonConverter(string jsonConverterName, string idName) + protected override ClassDeclarationSyntax GetNewtonsoftJsonConverter(string jsonConverterName, string idName) { return ClassDeclaration(jsonConverterName) .WithBaseList( @@ -625,7 +553,162 @@ private static ClassDeclarationSyntax GetJsonConverter(string jsonConverterName, )); } - private static ClassDeclarationSyntax GetTypeConverter(string typeConverterName, string idName) + protected override ClassDeclarationSyntax GetSystemTextJsonConverter(string jsonConverterName, string idName) + { + return ClassDeclaration(jsonConverterName) + .WithModifiers( + TokenList( + Token(SyntaxKind.PublicKeyword))) + .WithBaseList( + BaseList( + SingletonSeparatedList( + SimpleBaseType( + QualifiedName( + QualifiedName( + QualifiedName( + QualifiedName( + IdentifierName("System"), + IdentifierName("Text")), + IdentifierName("Json")), + IdentifierName("Serialization")), + GenericName( + Identifier("JsonConverter")) + .WithTypeArgumentList( + TypeArgumentList( + SingletonSeparatedList( + IdentifierName(idName))))))))) + .WithMembers( + List( + new MemberDeclarationSyntax[]{ + MethodDeclaration( + IdentifierName(idName), + Identifier("Read")) + .WithModifiers( + TokenList( + new []{ + Token(SyntaxKind.PublicKeyword), + Token(SyntaxKind.OverrideKeyword)})) + .WithParameterList( + ParameterList( + SeparatedList( + new SyntaxNodeOrToken[]{ + Parameter( + Identifier("reader")) + .WithModifiers( + TokenList( + Token(SyntaxKind.RefKeyword))) + .WithType( + QualifiedName( + QualifiedName( + QualifiedName( + IdentifierName("System"), + IdentifierName("Text")), + IdentifierName("Json")), + IdentifierName("Utf8JsonReader"))), + Token(SyntaxKind.CommaToken), + Parameter( + Identifier("typeToConvert")) + .WithType( + QualifiedName( + IdentifierName("System"), + IdentifierName("Type"))), + Token(SyntaxKind.CommaToken), + Parameter( + Identifier("options")) + .WithType( + QualifiedName( + QualifiedName( + QualifiedName( + IdentifierName("System"), + IdentifierName("Text")), + IdentifierName("Json")), + IdentifierName("JsonSerializerOptions")))}))) + .WithBody( + Block( + SingletonList( + ReturnStatement( + ObjectCreationExpression( + IdentifierName(idName)) + .WithArgumentList( + ArgumentList( + SingletonSeparatedList( + Argument( + InvocationExpression( + MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + IdentifierName("System"), + IdentifierName("Guid")), + IdentifierName("Parse"))) + .WithArgumentList( + ArgumentList( + SingletonSeparatedList( + Argument( + InvocationExpression( + MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + IdentifierName("reader"), + IdentifierName("GetString"))))))))))))))), + MethodDeclaration( + PredefinedType( + Token(SyntaxKind.VoidKeyword)), + Identifier("Write")) + .WithModifiers( + TokenList( + new []{ + Token(SyntaxKind.PublicKeyword), + Token(SyntaxKind.OverrideKeyword)})) + .WithParameterList( + ParameterList( + SeparatedList( + new SyntaxNodeOrToken[]{ + Parameter( + Identifier("writer")) + .WithType( + QualifiedName( + QualifiedName( + QualifiedName( + IdentifierName("System"), + IdentifierName("Text")), + IdentifierName("Json")), + IdentifierName("Utf8JsonWriter"))), + Token(SyntaxKind.CommaToken), + Parameter( + Identifier("value")) + .WithType( + IdentifierName(idName)), + Token(SyntaxKind.CommaToken), + Parameter( + Identifier("options")) + .WithType( + QualifiedName( + QualifiedName( + QualifiedName( + IdentifierName("System"), + IdentifierName("Text")), + IdentifierName("Json")), + IdentifierName("JsonSerializerOptions")))}))) + .WithBody( + Block( + SingletonList( + ExpressionStatement( + InvocationExpression( + MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + IdentifierName("writer"), + IdentifierName("WriteStringValue"))) + .WithArgumentList( + ArgumentList( + SingletonSeparatedList( + Argument( + MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + IdentifierName("value"), + IdentifierName("Value"))))))))))})); + } + + protected override ClassDeclarationSyntax GetTypeConverter(string typeConverterName, string idName) { return ClassDeclaration(typeConverterName) .WithBaseList( @@ -829,41 +912,5 @@ private static ClassDeclarationSyntax GetTypeConverter(string typeConverterName, })))))) })); } - - private static AttributeListSyntax GetJsonConverterAttribute(string jsonConverterName) - { - return AttributeList( - SingletonSeparatedList( - Attribute( - QualifiedName( - QualifiedName( - IdentifierName("Newtonsoft"), - IdentifierName("Json")), - IdentifierName("JsonConverter"))) - .WithArgumentList( - AttributeArgumentList( - SingletonSeparatedList( - AttributeArgument( - TypeOfExpression( - IdentifierName(jsonConverterName)))))))); - } - - private static AttributeListSyntax GetTypeConverterAttribute(string typeConverterName) - { - return AttributeList( - SingletonSeparatedList( - Attribute( - QualifiedName( - QualifiedName( - IdentifierName("System"), - IdentifierName("ComponentModel")), - IdentifierName("TypeConverter"))) - .WithArgumentList( - AttributeArgumentList( - SingletonSeparatedList( - AttributeArgument( - TypeOfExpression( - IdentifierName(typeConverterName)))))))); - } } } \ No newline at end of file diff --git a/src/StronglyTypedId.Generator/IntSyntaxTreeGenerator.cs b/src/StronglyTypedId.Generator/IntSyntaxTreeGenerator.cs index f705d1961..adea8400d 100644 --- a/src/StronglyTypedId.Generator/IntSyntaxTreeGenerator.cs +++ b/src/StronglyTypedId.Generator/IntSyntaxTreeGenerator.cs @@ -1,87 +1,14 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; -using System; using System.Collections.Generic; -using System.Linq; -using System.Text; using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; namespace StronglyTypedId.Generator { - - public static class IntSyntaxTreeGenerator + public class IntSyntaxTreeGenerator : BaseSyntaxTreeGenerator { - // Generated using https://roslynquoter.azurewebsites.net/ - public static SyntaxList CreateStronglyTypedIdSyntax(StructDeclarationSyntax original, bool generateJsonConverter) - { - // Get the name of the decorated member - var idName = original.Identifier.ValueText; - var typeConverterName = idName + "TypeConverter"; - var jsonConverterName = idName + "JsonConverter"; - - var typeConverterAttribute = GetTypeConverterAttribute(typeConverterName); - - var attributes = generateJsonConverter - ? List( - new AttributeListSyntax[] - { - typeConverterAttribute, - GetJsonConverterAttribute(jsonConverterName) - }) - : SingletonList(typeConverterAttribute); - - var members = GetMembers(idName) - .Append(GetTypeConverter(typeConverterName, idName)); - if (generateJsonConverter) - { - members = members.Append(GetJsonConverter(jsonConverterName, idName)); - } - - return SingletonList( - StructDeclaration(idName) - .WithAttributeLists(attributes) - .WithModifiers( - TokenList( - new[] - { - Token(SyntaxKind.ReadOnlyKeyword), - Token(SyntaxKind.PartialKeyword) - })) - .WithBaseList( - BaseList( - SeparatedList( - new SyntaxNodeOrToken[] - { - SimpleBaseType( - QualifiedName( - IdentifierName("System"), - GenericName( - Identifier("IComparable")) - .WithTypeArgumentList( - TypeArgumentList( - SingletonSeparatedList( - IdentifierName(idName)))))), - Token(SyntaxKind.CommaToken), - SimpleBaseType( - QualifiedName( - IdentifierName("System"), - GenericName( - Identifier("IEquatable")) - .WithTypeArgumentList( - TypeArgumentList( - SingletonSeparatedList( - IdentifierName(idName)))))) - }))) - .WithMembers(List(members)) - .WithCloseBraceToken( - Token( - TriviaList(), - SyntaxKind.CloseBraceToken, - TriviaList()))); - } - - private static IEnumerable GetMembers(string idName) + protected override IEnumerable GetMembers(string idName) { yield return PropertyDeclaration( PredefinedType @@ -544,7 +471,7 @@ private static IEnumerable GetMembers(string idName) Token(SyntaxKind.SemicolonToken)); } - private static ClassDeclarationSyntax GetJsonConverter(string jsonConverterName, string idName) + protected override ClassDeclarationSyntax GetNewtonsoftJsonConverter(string jsonConverterName, string idName) { return ClassDeclaration(jsonConverterName) .WithBaseList @@ -834,7 +761,150 @@ private static ClassDeclarationSyntax GetJsonConverter(string jsonConverterName, IdentifierName("reader")))))))))))))})); } - private static ClassDeclarationSyntax GetTypeConverter(string typeConverterName, string idName) + protected override ClassDeclarationSyntax GetSystemTextJsonConverter(string jsonConverterName, string idName) + { + return ClassDeclaration(jsonConverterName) + .WithModifiers( + TokenList( + Token(SyntaxKind.PublicKeyword))) + .WithBaseList( + BaseList( + SingletonSeparatedList( + SimpleBaseType( + QualifiedName( + QualifiedName( + QualifiedName( + QualifiedName( + IdentifierName("System"), + IdentifierName("Text")), + IdentifierName("Json")), + IdentifierName("Serialization")), + GenericName( + Identifier("JsonConverter")) + .WithTypeArgumentList( + TypeArgumentList( + SingletonSeparatedList( + IdentifierName(idName))))))))) + .WithMembers( + List( + new MemberDeclarationSyntax[]{ + MethodDeclaration( + IdentifierName(idName), + Identifier("Read")) + .WithModifiers( + TokenList( + new []{ + Token(SyntaxKind.PublicKeyword), + Token(SyntaxKind.OverrideKeyword)})) + .WithParameterList( + ParameterList( + SeparatedList( + new SyntaxNodeOrToken[]{ + Parameter( + Identifier("reader")) + .WithModifiers( + TokenList( + Token(SyntaxKind.RefKeyword))) + .WithType( + QualifiedName( + QualifiedName( + QualifiedName( + IdentifierName("System"), + IdentifierName("Text")), + IdentifierName("Json")), + IdentifierName("Utf8JsonReader"))), + Token(SyntaxKind.CommaToken), + Parameter( + Identifier("typeToConvert")) + .WithType( + QualifiedName( + IdentifierName("System"), + IdentifierName("Type"))), + Token(SyntaxKind.CommaToken), + Parameter( + Identifier("options")) + .WithType( + QualifiedName( + QualifiedName( + QualifiedName( + IdentifierName("System"), + IdentifierName("Text")), + IdentifierName("Json")), + IdentifierName("JsonSerializerOptions")))}))) + .WithBody( + Block( + SingletonList( + ReturnStatement( + ObjectCreationExpression( + IdentifierName(idName)) + .WithArgumentList( + ArgumentList( + SingletonSeparatedList( + Argument( + InvocationExpression( + MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + IdentifierName("reader"), + IdentifierName("GetInt32"))))))))))), + MethodDeclaration( + PredefinedType( + Token(SyntaxKind.VoidKeyword)), + Identifier("Write")) + .WithModifiers( + TokenList( + new []{ + Token(SyntaxKind.PublicKeyword), + Token(SyntaxKind.OverrideKeyword)})) + .WithParameterList( + ParameterList( + SeparatedList( + new SyntaxNodeOrToken[]{ + Parameter( + Identifier("writer")) + .WithType( + QualifiedName( + QualifiedName( + QualifiedName( + IdentifierName("System"), + IdentifierName("Text")), + IdentifierName("Json")), + IdentifierName("Utf8JsonWriter"))), + Token(SyntaxKind.CommaToken), + Parameter( + Identifier("value")) + .WithType( + IdentifierName(idName)), + Token(SyntaxKind.CommaToken), + Parameter( + Identifier("options")) + .WithType( + QualifiedName( + QualifiedName( + QualifiedName( + IdentifierName("System"), + IdentifierName("Text")), + IdentifierName("Json")), + IdentifierName("JsonSerializerOptions")))}))) + .WithBody( + Block( + SingletonList( + ExpressionStatement( + InvocationExpression( + MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + IdentifierName("writer"), + IdentifierName("WriteNumberValue"))) + .WithArgumentList( + ArgumentList( + SingletonSeparatedList( + Argument( + MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + IdentifierName("value"), + IdentifierName("Value"))))))))))})); + } + + protected override ClassDeclarationSyntax GetTypeConverter(string typeConverterName, string idName) { return ClassDeclaration(typeConverterName) .WithBaseList @@ -1064,41 +1134,5 @@ private static ClassDeclarationSyntax GetTypeConverter(string typeConverterName, ( IdentifierName("value"))}))))))})); } - - private static AttributeListSyntax GetJsonConverterAttribute(string jsonConverterName) - { - return AttributeList( - SingletonSeparatedList( - Attribute( - QualifiedName( - QualifiedName( - IdentifierName("Newtonsoft"), - IdentifierName("Json")), - IdentifierName("JsonConverter"))) - .WithArgumentList( - AttributeArgumentList( - SingletonSeparatedList( - AttributeArgument( - TypeOfExpression( - IdentifierName(jsonConverterName)))))))); - } - - private static AttributeListSyntax GetTypeConverterAttribute(string typeConverterName) - { - return AttributeList( - SingletonSeparatedList( - Attribute( - QualifiedName( - QualifiedName( - IdentifierName("System"), - IdentifierName("ComponentModel")), - IdentifierName("TypeConverter"))) - .WithArgumentList( - AttributeArgumentList( - SingletonSeparatedList( - AttributeArgument( - TypeOfExpression( - IdentifierName(typeConverterName)))))))); - } } } \ No newline at end of file diff --git a/src/StronglyTypedId.Generator/StringSyntaxTreeGenerator.cs b/src/StronglyTypedId.Generator/StringSyntaxTreeGenerator.cs index 30b4796cb..2b19589e5 100644 --- a/src/StronglyTypedId.Generator/StringSyntaxTreeGenerator.cs +++ b/src/StronglyTypedId.Generator/StringSyntaxTreeGenerator.cs @@ -1,87 +1,14 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; -using System; using System.Collections.Generic; -using System.Linq; -using System.Text; using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; namespace StronglyTypedId.Generator { - - public static class StringSyntaxTreeGenerator + public class StringSyntaxTreeGenerator : BaseSyntaxTreeGenerator { - // Generated using https://roslynquoter.azurewebsites.net/ - public static SyntaxList CreateStronglyTypedIdSyntax(StructDeclarationSyntax original, bool generateJsonConverter) - { - // Get the name of the decorated member - var idName = original.Identifier.ValueText; - var typeConverterName = idName + "TypeConverter"; - var jsonConverterName = idName + "JsonConverter"; - - var typeConverterAttribute = GetTypeConverterAttribute(typeConverterName); - - var attributes = generateJsonConverter - ? List( - new AttributeListSyntax[] - { - typeConverterAttribute, - GetJsonConverterAttribute(jsonConverterName) - }) - : SingletonList(typeConverterAttribute); - - var members = GetMembers(idName) - .Append(GetTypeConverter(typeConverterName, idName)); - if (generateJsonConverter) - { - members = members.Append(GetJsonConverter(jsonConverterName, idName)); - } - - return SingletonList( - StructDeclaration(idName) - .WithAttributeLists(attributes) - .WithModifiers( - TokenList( - new[] - { - Token(SyntaxKind.ReadOnlyKeyword), - Token(SyntaxKind.PartialKeyword) - })) - .WithBaseList( - BaseList( - SeparatedList( - new SyntaxNodeOrToken[] - { - SimpleBaseType( - QualifiedName( - IdentifierName("System"), - GenericName( - Identifier("IComparable")) - .WithTypeArgumentList( - TypeArgumentList( - SingletonSeparatedList( - IdentifierName(idName)))))), - Token(SyntaxKind.CommaToken), - SimpleBaseType( - QualifiedName( - IdentifierName("System"), - GenericName( - Identifier("IEquatable")) - .WithTypeArgumentList( - TypeArgumentList( - SingletonSeparatedList( - IdentifierName(idName)))))) - }))) - .WithMembers(List(members)) - .WithCloseBraceToken( - Token( - TriviaList(), - SyntaxKind.CloseBraceToken, - TriviaList()))); - } - - private static IEnumerable GetMembers(string idName) + protected override IEnumerable GetMembers(string idName) { yield return PropertyDeclaration( PredefinedType @@ -547,7 +474,7 @@ private static IEnumerable GetMembers(string idName) Token(SyntaxKind.SemicolonToken)); } - private static ClassDeclarationSyntax GetJsonConverter(string jsonConverterName, string idName) + protected override ClassDeclarationSyntax GetNewtonsoftJsonConverter(string jsonConverterName, string idName) { return ClassDeclaration(jsonConverterName) .WithBaseList @@ -835,9 +762,152 @@ private static ClassDeclarationSyntax GetJsonConverter(string jsonConverterName, Argument ( IdentifierName("reader")))))))))))))})); - } + } + + protected override ClassDeclarationSyntax GetSystemTextJsonConverter(string jsonConverterName, string idName) + { + return ClassDeclaration(jsonConverterName) + .WithModifiers( + TokenList( + Token(SyntaxKind.PublicKeyword))) + .WithBaseList( + BaseList( + SingletonSeparatedList( + SimpleBaseType( + QualifiedName( + QualifiedName( + QualifiedName( + QualifiedName( + IdentifierName("System"), + IdentifierName("Text")), + IdentifierName("Json")), + IdentifierName("Serialization")), + GenericName( + Identifier("JsonConverter")) + .WithTypeArgumentList( + TypeArgumentList( + SingletonSeparatedList( + IdentifierName(idName))))))))) + .WithMembers( + List( + new MemberDeclarationSyntax[]{ + MethodDeclaration( + IdentifierName(idName), + Identifier("Read")) + .WithModifiers( + TokenList( + new []{ + Token(SyntaxKind.PublicKeyword), + Token(SyntaxKind.OverrideKeyword)})) + .WithParameterList( + ParameterList( + SeparatedList( + new SyntaxNodeOrToken[]{ + Parameter( + Identifier("reader")) + .WithModifiers( + TokenList( + Token(SyntaxKind.RefKeyword))) + .WithType( + QualifiedName( + QualifiedName( + QualifiedName( + IdentifierName("System"), + IdentifierName("Text")), + IdentifierName("Json")), + IdentifierName("Utf8JsonReader"))), + Token(SyntaxKind.CommaToken), + Parameter( + Identifier("typeToConvert")) + .WithType( + QualifiedName( + IdentifierName("System"), + IdentifierName("Type"))), + Token(SyntaxKind.CommaToken), + Parameter( + Identifier("options")) + .WithType( + QualifiedName( + QualifiedName( + QualifiedName( + IdentifierName("System"), + IdentifierName("Text")), + IdentifierName("Json")), + IdentifierName("JsonSerializerOptions")))}))) + .WithBody( + Block( + SingletonList( + ReturnStatement( + ObjectCreationExpression( + IdentifierName(idName)) + .WithArgumentList( + ArgumentList( + SingletonSeparatedList( + Argument( + InvocationExpression( + MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + IdentifierName("reader"), + IdentifierName("GetString"))))))))))), + MethodDeclaration( + PredefinedType( + Token(SyntaxKind.VoidKeyword)), + Identifier("Write")) + .WithModifiers( + TokenList( + new []{ + Token(SyntaxKind.PublicKeyword), + Token(SyntaxKind.OverrideKeyword)})) + .WithParameterList( + ParameterList( + SeparatedList( + new SyntaxNodeOrToken[]{ + Parameter( + Identifier("writer")) + .WithType( + QualifiedName( + QualifiedName( + QualifiedName( + IdentifierName("System"), + IdentifierName("Text")), + IdentifierName("Json")), + IdentifierName("Utf8JsonWriter"))), + Token(SyntaxKind.CommaToken), + Parameter( + Identifier("value")) + .WithType( + IdentifierName(idName)), + Token(SyntaxKind.CommaToken), + Parameter( + Identifier("options")) + .WithType( + QualifiedName( + QualifiedName( + QualifiedName( + IdentifierName("System"), + IdentifierName("Text")), + IdentifierName("Json")), + IdentifierName("JsonSerializerOptions")))}))) + .WithBody( + Block( + SingletonList( + ExpressionStatement( + InvocationExpression( + MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + IdentifierName("writer"), + IdentifierName("WriteStringValue"))) + .WithArgumentList( + ArgumentList( + SingletonSeparatedList( + Argument( + MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + IdentifierName("value"), + IdentifierName("Value"))))))))))})); + } - private static ClassDeclarationSyntax GetTypeConverter(string typeConverterName, string idName) + protected override ClassDeclarationSyntax GetTypeConverter(string typeConverterName, string idName) { return ClassDeclaration(typeConverterName) .WithBaseList( @@ -1001,41 +1071,5 @@ private static ClassDeclarationSyntax GetTypeConverter(string typeConverterName, Argument( IdentifierName("value"))}))))))})); } - - private static AttributeListSyntax GetJsonConverterAttribute(string jsonConverterName) - { - return AttributeList( - SingletonSeparatedList( - Attribute( - QualifiedName( - QualifiedName( - IdentifierName("Newtonsoft"), - IdentifierName("Json")), - IdentifierName("JsonConverter"))) - .WithArgumentList( - AttributeArgumentList( - SingletonSeparatedList( - AttributeArgument( - TypeOfExpression( - IdentifierName(jsonConverterName)))))))); - } - - private static AttributeListSyntax GetTypeConverterAttribute(string typeConverterName) - { - return AttributeList( - SingletonSeparatedList( - Attribute( - QualifiedName( - QualifiedName( - IdentifierName("System"), - IdentifierName("ComponentModel")), - IdentifierName("TypeConverter"))) - .WithArgumentList( - AttributeArgumentList( - SingletonSeparatedList( - AttributeArgument( - TypeOfExpression( - IdentifierName(typeConverterName)))))))); - } } } \ No newline at end of file diff --git a/src/StronglyTypedId.Generator/StronglyTypedIdGenerator.cs b/src/StronglyTypedId.Generator/StronglyTypedIdGenerator.cs index 55426bf5d..ecb1e28c6 100644 --- a/src/StronglyTypedId.Generator/StronglyTypedIdGenerator.cs +++ b/src/StronglyTypedId.Generator/StronglyTypedIdGenerator.cs @@ -1,12 +1,9 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using CodeGeneration.Roslyn; +using CodeGeneration.Roslyn; using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; +using System; +using System.Threading; +using System.Threading.Tasks; namespace StronglyTypedId.Generator { @@ -14,12 +11,14 @@ public class StronglyTypedIdGenerator : IRichCodeGenerator { private readonly bool _generateJsonConverter; private readonly StronglyTypedIdBackingType _backingType; + private readonly StronglyTypedIdJsonConverter _jsonProvider; public StronglyTypedIdGenerator(AttributeData attributeData) { if (attributeData == null) throw new ArgumentNullException(nameof(attributeData)); _generateJsonConverter = (bool)attributeData.ConstructorArguments[0].Value; _backingType = (StronglyTypedIdBackingType)attributeData.ConstructorArguments[1].Value; + _jsonProvider = (StronglyTypedIdJsonConverter)attributeData.ConstructorArguments[2].Value; } public Task> GenerateAsync(TransformationContext context, IProgress progress, CancellationToken cancellationToken) @@ -42,17 +41,22 @@ public Task GenerateRichAsync(TransformationContext contex } private SyntaxList GetSyntax(StructDeclarationSyntax applyToClass) + { + return GetGenerator().CreateStronglyTypedIdSyntax(applyToClass, _generateJsonConverter, _jsonProvider); + } + + private BaseSyntaxTreeGenerator GetGenerator() { switch (_backingType) { case StronglyTypedIdBackingType.Int: - return IntSyntaxTreeGenerator.CreateStronglyTypedIdSyntax(applyToClass, _generateJsonConverter); + return new IntSyntaxTreeGenerator(); case StronglyTypedIdBackingType.String: - return StringSyntaxTreeGenerator.CreateStronglyTypedIdSyntax(applyToClass, _generateJsonConverter); + return new StringSyntaxTreeGenerator(); case StronglyTypedIdBackingType.Guid: default: - return GuidSyntaxTreeGenerator.CreateStronglyTypedIdSyntax(applyToClass, _generateJsonConverter); + return new GuidSyntaxTreeGenerator(); } } } -} +} \ No newline at end of file diff --git a/src/StronglyTypedId.Generator/templates/GuidId.cs b/src/StronglyTypedId.Generator/templates/GuidId.cs index d5903a4fe..88db86cd6 100644 --- a/src/StronglyTypedId.Generator/templates/GuidId.cs +++ b/src/StronglyTypedId.Generator/templates/GuidId.cs @@ -1,5 +1,6 @@ [System.ComponentModel.TypeConverter(typeof(GuidIdTypeConverter))] -[Newtonsoft.Json.JsonConverter(typeof(GuidIdJsonConverter))] +[Newtonsoft.Json.JsonConverter(typeof(GuidIdNewtonsoftJsonConverter))] +[System.Text.Json.Serialization.JsonConverter(typeof(GuidIdSystemTextJsonConverter))] readonly partial struct GuidId : System.IComparable, System.IEquatable { public System.Guid Value { get; } @@ -47,7 +48,7 @@ public override object ConvertFrom(System.ComponentModel.ITypeDescriptorContext } } - class GuidIdJsonConverter : Newtonsoft.Json.JsonConverter + class GuidIdNewtonsoftJsonConverter : Newtonsoft.Json.JsonConverter { public override bool CanConvert(System.Type objectType) { @@ -66,4 +67,17 @@ public override object ReadJson(Newtonsoft.Json.JsonReader reader, System.Type o return new GuidId(guid); } } + + class GuidIdSystemTextJsonConverter : System.Text.Json.Serialization.JsonConverter + { + public override GuidId Read(ref System.Text.Json.Utf8JsonReader reader, System.Type typeToConvert, System.Text.Json.JsonSerializerOptions options) + { + return new GuidId(System.Guid.Parse(reader.GetString())); + } + + public override void Write(System.Text.Json.Utf8JsonWriter writer, GuidId value, System.Text.Json.JsonSerializerOptions options) + { + writer.WriteStringValue(value.Value); + } + } } \ No newline at end of file diff --git a/src/StronglyTypedId.Generator/templates/IntId.cs b/src/StronglyTypedId.Generator/templates/IntId.cs index 6bdc222aa..346e17fd7 100644 --- a/src/StronglyTypedId.Generator/templates/IntId.cs +++ b/src/StronglyTypedId.Generator/templates/IntId.cs @@ -1,5 +1,6 @@ [System.ComponentModel.TypeConverter(typeof(IntIdTypeConverter))] -[Newtonsoft.Json.JsonConverter(typeof(IntIdJsonConverter))] +[Newtonsoft.Json.JsonConverter(typeof(IntIdNewtonsoftJsonConverter))] +[System.Text.Json.Serialization.JsonConverter(typeof(IntIdSystemTextJsonConverter))] readonly partial struct IntId : System.IComparable, System.IEquatable { public int Value { get; } @@ -44,7 +45,7 @@ public override object ConvertFrom(System.ComponentModel.ITypeDescriptorContext } } - class IntIdJsonConverter : Newtonsoft.Json.JsonConverter + class IntIdNewtonsoftJsonConverter : Newtonsoft.Json.JsonConverter { public override bool CanConvert(System.Type objectType) { @@ -62,4 +63,17 @@ public override object ReadJson(Newtonsoft.Json.JsonReader reader, System.Type o return new IntId(serializer.Deserialize(reader)); } } + + class IntIdSystemTextJsonConverter : System.Text.Json.Serialization.JsonConverter + { + public override IntId Read(ref System.Text.Json.Utf8JsonReader reader, System.Type typeToConvert, System.Text.Json.JsonSerializerOptions options) + { + return new IntId(reader.GetInt32()); + } + + public override void Write(System.Text.Json.Utf8JsonWriter writer, IntId value, System.Text.Json.JsonSerializerOptions options) + { + writer.WriteNumberValue(value.Value); + } + } } \ No newline at end of file diff --git a/src/StronglyTypedId.Generator/templates/StringId.cs b/src/StronglyTypedId.Generator/templates/StringId.cs index d6b89fde4..24ab7a2d0 100644 --- a/src/StronglyTypedId.Generator/templates/StringId.cs +++ b/src/StronglyTypedId.Generator/templates/StringId.cs @@ -1,5 +1,6 @@ [System.ComponentModel.TypeConverter(typeof(StringIdTypeConverter))] -[Newtonsoft.Json.JsonConverter(typeof(StringIdJsonConverter))] +[Newtonsoft.Json.JsonConverter(typeof(StringIdNewtonsoftJsonConverter))] +[System.Text.Json.Serialization.JsonConverter(typeof(StringIdSystemTextJsonConverter))] readonly partial struct StringId : System.IComparable, System.IEquatable { public string Value { get; } @@ -45,7 +46,7 @@ public override object ConvertFrom(System.ComponentModel.ITypeDescriptorContext } } - class StringIdJsonConverter : Newtonsoft.Json.JsonConverter + class StringIdNewtonsoftJsonConverter : Newtonsoft.Json.JsonConverter { public override bool CanConvert(System.Type objectType) { @@ -63,4 +64,17 @@ public override object ReadJson(Newtonsoft.Json.JsonReader reader, System.Type o return new StringId(serializer.Deserialize(reader)); } } + + class StringIdSystemTextJsonConverter : System.Text.Json.Serialization.JsonConverter + { + public override StringId Read(ref System.Text.Json.Utf8JsonReader reader, System.Type typeToConvert, System.Text.Json.JsonSerializerOptions options) + { + return new StringId(reader.GetString()); + } + + public override void Write(System.Text.Json.Utf8JsonWriter writer, StringId value, System.Text.Json.JsonSerializerOptions options) + { + writer.WriteStringValue(value.Value); + } + } } \ No newline at end of file diff --git a/test/StronglyTypedId.Tests/GeneratedId1.cs b/test/StronglyTypedId.Tests/GeneratedId1.cs deleted file mode 100644 index 8a624beab..000000000 --- a/test/StronglyTypedId.Tests/GeneratedId1.cs +++ /dev/null @@ -1,7 +0,0 @@ -using StronglyTypedId.Generator; - -namespace StronglyTypedId -{ - [StronglyTypedId] - partial struct GeneratedId1 { } -} \ No newline at end of file diff --git a/test/StronglyTypedId.Tests/GeneratedId2.cs b/test/StronglyTypedId.Tests/GeneratedId2.cs deleted file mode 100644 index 63cb838c6..000000000 --- a/test/StronglyTypedId.Tests/GeneratedId2.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; -using StronglyTypedId.Generator; - -namespace StronglyTypedId -{ - [StronglyTypedId] - public partial struct GeneratedId2 { } -} diff --git a/test/StronglyTypedId.Tests/GeneratedStronglyTypedIdTests.cs b/test/StronglyTypedId.Tests/GeneratedStronglyTypedIdTests.cs deleted file mode 100644 index 6e4219051..000000000 --- a/test/StronglyTypedId.Tests/GeneratedStronglyTypedIdTests.cs +++ /dev/null @@ -1,105 +0,0 @@ -using System; -using Newtonsoft.Json; -using Xunit; - -namespace StronglyTypedId -{ - public class GeneratedStronglyTypedIdTests - { - [Fact] - public void SameValuesAreEqual() - { - var id = Guid.NewGuid(); - var foo1 = new GeneratedId1(id); - var foo2 = new GeneratedId1(id); - - Assert.Equal(foo1, foo2); - } - - [Fact] - public void EmptyValueIsEmpty() - { - Assert.Equal(GeneratedId1.Empty.Value, Guid.Empty); - } - - - [Fact] - public void DifferentValuesAreUnequal() - { - var foo1 = GeneratedId1.New(); - var foo2 = GeneratedId1.New(); - - Assert.NotEqual(foo1, foo2); - } - - [Fact] - public void OverloadsWorkCorrectly() - { - var id = Guid.NewGuid(); - var same1 = new GeneratedId1(id); - var same2 = new GeneratedId1(id); - var different = GeneratedId1.New(); - - Assert.True(same1 == same2); - Assert.False(same1 == different); - Assert.False(same1 != same2); - Assert.True(same1 != different); - } - - [Fact] - public void DifferentTypesAreUnequal() - { - var bar = GeneratedId2.New(); - var foo = GeneratedId1.New(); - - //Assert.NotEqual(bar, foo); // does not compile - Assert.NotEqual((object)bar, (object)foo); - } - - [Fact] - public void CantCreateEmptyGeneratedId1() - { - var foo = new GeneratedId1(); - var bar = new GeneratedId2(); - - //Assert.NotEqual(bar, foo); // does not compile - Assert.NotEqual((object)bar, (object)foo); - } - - - [Fact] - public void CanSerializeToGuid() - { - var foo = GeneratedId1.New(); - - var serializedFoo = JsonConvert.SerializeObject(foo); - var serializedGuid = JsonConvert.SerializeObject(foo.Value); - - Assert.Equal(serializedFoo, serializedGuid); - } - - [Fact] - public void CanSerializeFromGuid() - { - var value = Guid.NewGuid(); - var foo = new GeneratedId1(value); - var serializedGuid = JsonConvert.SerializeObject(value); - - var deserializedFoo = JsonConvert.DeserializeObject(serializedGuid); - - Assert.Equal(foo, deserializedFoo); - } - - - [Fact] - public void WhenNoJsonConverter_SerializesWithValueProperty() - { - var foo = NoJsonGeneratedId.New(); - - var serialized = JsonConvert.SerializeObject(foo); - var expected = "{\"Value\":\"" + foo.Value + "\"}"; - - Assert.Equal(expected, serialized); - } - } -} \ No newline at end of file diff --git a/test/StronglyTypedId.Tests/GuidIdTests.cs b/test/StronglyTypedId.Tests/GuidIdTests.cs new file mode 100644 index 000000000..f261a7e6f --- /dev/null +++ b/test/StronglyTypedId.Tests/GuidIdTests.cs @@ -0,0 +1,147 @@ +using StronglyTypedId.Tests.Types; +using System; +using Xunit; +using NewtonsoftJsonSerializer = Newtonsoft.Json.JsonConvert; +using SystemTextJsonSerializer = System.Text.Json.JsonSerializer; + +namespace StronglyTypedId.Tests +{ + public class GuidIdTests + { + [Fact] + public void SameValuesAreEqual() + { + var id = Guid.NewGuid(); + var foo1 = new GuidId1(id); + var foo2 = new GuidId1(id); + + Assert.Equal(foo1, foo2); + } + + [Fact] + public void EmptyValueIsEmpty() + { + Assert.Equal(GuidId1.Empty.Value, Guid.Empty); + } + + + [Fact] + public void DifferentValuesAreUnequal() + { + var foo1 = GuidId1.New(); + var foo2 = GuidId1.New(); + + Assert.NotEqual(foo1, foo2); + } + + [Fact] + public void OverloadsWorkCorrectly() + { + var id = Guid.NewGuid(); + var same1 = new GuidId1(id); + var same2 = new GuidId1(id); + var different = GuidId1.New(); + + Assert.True(same1 == same2); + Assert.False(same1 == different); + Assert.False(same1 != same2); + Assert.True(same1 != different); + } + + [Fact] + public void DifferentTypesAreUnequal() + { + var bar = GuidId2.New(); + var foo = GuidId1.New(); + + //Assert.NotEqual(bar, foo); // does not compile + Assert.NotEqual((object)bar, (object)foo); + } + + [Fact] + public void CantCreateEmptyGeneratedId1() + { + var foo = new GuidId1(); + var bar = new GuidId2(); + + //Assert.NotEqual(bar, foo); // does not compile + Assert.NotEqual((object)bar, (object)foo); + } + + + [Fact] + public void CanSerializeToGuid_WithNewtonsoftJsonProvider() + { + var foo = NewtonsoftJsonGuidId.New(); + + var serializedFoo = NewtonsoftJsonSerializer.SerializeObject(foo); + var serializedGuid = NewtonsoftJsonSerializer.SerializeObject(foo.Value); + + Assert.Equal(serializedFoo, serializedGuid); + } + + [Fact] + public void CanSerializeToGuid_WithSystemTextJsonProvider() + { + var foo = SystemTextJsonGuidId.New(); + + var serializedFoo = SystemTextJsonSerializer.Serialize(foo); + var serializedGuid = SystemTextJsonSerializer.Serialize(foo.Value); + + Assert.Equal(serializedFoo, serializedGuid); + } + + [Fact] + public void CanDeserializeFromGuid_WithNewtonsoftJsonProvider() + { + var value = Guid.NewGuid(); + var foo = new NewtonsoftJsonGuidId(value); + var serializedGuid = NewtonsoftJsonSerializer.SerializeObject(value); + + var deserializedFoo = NewtonsoftJsonSerializer.DeserializeObject(serializedGuid); + + Assert.Equal(foo, deserializedFoo); + } + + [Fact] + public void CanDeserializeFromGuid_WithSystemTextJsonProvider() + { + var value = Guid.NewGuid(); + var foo = new SystemTextJsonGuidId(value); + var serializedGuid = SystemTextJsonSerializer.Serialize(value); + + var deserializedFoo = SystemTextJsonSerializer.Deserialize(serializedGuid); + + Assert.Equal(foo, deserializedFoo); + } + + [Fact] + public void CanSerializeToGuid_WithBothJsonConverters() + { + var foo = BothJsonGuidId.New(); + + var serializedFoo1 = NewtonsoftJsonSerializer.SerializeObject(foo); + var serializedGuid1 = NewtonsoftJsonSerializer.SerializeObject(foo.Value); + + var serializedFoo2 = SystemTextJsonSerializer.Serialize(foo); + var serializedGuid2 = SystemTextJsonSerializer.Serialize(foo.Value); + + Assert.Equal(serializedFoo1, serializedGuid1); + Assert.Equal(serializedFoo2, serializedGuid2); + } + + [Fact] + public void WhenNoJsonConverter_SerializesWithValueProperty() + { + var foo = NoJsonGuidId.New(); + + var serialized1 = NewtonsoftJsonSerializer.SerializeObject(foo); + var serialized2 = SystemTextJsonSerializer.Serialize(foo); + + var expected = "{\"Value\":\"" + foo.Value + "\"}"; + + Assert.Equal(expected, serialized1); + Assert.Equal(expected, serialized2); + } + } +} \ No newline at end of file diff --git a/test/StronglyTypedId.Tests/IntId.cs b/test/StronglyTypedId.Tests/IntId.cs deleted file mode 100644 index 7d9d5a8cf..000000000 --- a/test/StronglyTypedId.Tests/IntId.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace StronglyTypedId -{ - [StronglyTypedId(backingType: StronglyTypedIdBackingType.Int)] - partial struct IntId { } - - [StronglyTypedId(generateJsonConverter: false, backingType: StronglyTypedIdBackingType.Int)] - partial struct NoJsonIntId { } -} \ No newline at end of file diff --git a/test/StronglyTypedId.Tests/IntIdTests.cs b/test/StronglyTypedId.Tests/IntIdTests.cs index c8404469c..32407f9d3 100644 --- a/test/StronglyTypedId.Tests/IntIdTests.cs +++ b/test/StronglyTypedId.Tests/IntIdTests.cs @@ -1,7 +1,9 @@ -using Newtonsoft.Json; +using StronglyTypedId.Tests.Types; using Xunit; +using NewtonsoftJsonSerializer = Newtonsoft.Json.JsonConvert; +using SystemTextJsonSerializer = System.Text.Json.JsonSerializer; -namespace StronglyTypedId +namespace StronglyTypedId.Tests { public class IntIdTests { @@ -48,7 +50,7 @@ public void OverloadsWorkCorrectly() [Fact] public void DifferentTypesAreUnequal() { - var bar = GeneratedId2.New(); + var bar = GuidId2.New(); var foo = new IntId(23); //Assert.NotEqual(bar, foo); // does not compile @@ -56,40 +58,78 @@ public void DifferentTypesAreUnequal() } [Fact] - public void CanSerializeToString() + public void CanSerializeToInt_WithNewtonsoftJsonProvider() + { + var foo = new NewtonsoftJsonIntId(123); + + var serializedFoo = NewtonsoftJsonSerializer.SerializeObject(foo); + var serializedInt = NewtonsoftJsonSerializer.SerializeObject(foo.Value); + + Assert.Equal(serializedFoo, serializedInt); + } + + [Fact] + public void CanSerializeToInt_WithSystemTextJsonProvider() + { + var foo = new SystemTextJsonIntId(123); + + var serializedFoo = SystemTextJsonSerializer.Serialize(foo); + var serializedInt = SystemTextJsonSerializer.Serialize(foo.Value); + + Assert.Equal(serializedFoo, serializedInt); + } + + [Fact] + public void CanDeserializeFromInt_WithNewtonsoftJsonProvider() { var value = 123; - var foo = new IntId(value); + var foo = new NewtonsoftJsonIntId(value); + var serializedInt = NewtonsoftJsonSerializer.SerializeObject(value); - var serializedFoo = JsonConvert.SerializeObject(foo); - var serializedString = JsonConvert.SerializeObject(value); + var deserializedFoo = NewtonsoftJsonSerializer.DeserializeObject(serializedInt); - Assert.Equal(serializedFoo, serializedString); + Assert.Equal(foo, deserializedFoo); } [Fact] - public void CanSerializeFromString() + public void CanDeserializeFromInt_WithSystemTextJsonProvider() { var value = 123; - var foo = new IntId(value); + var foo = new SystemTextJsonIntId(value); + var serializedInt = SystemTextJsonSerializer.Serialize(value); - var serializedValue = JsonConvert.SerializeObject(value); - var deserializedFoo = JsonConvert.DeserializeObject(serializedValue); + var deserializedFoo = SystemTextJsonSerializer.Deserialize(serializedInt); Assert.Equal(foo, deserializedFoo); } + [Fact] + public void CanSerializeToInt_WithBothJsonConverters() + { + var foo = new BothJsonIntId(123); + + var serializedFoo1 = NewtonsoftJsonSerializer.SerializeObject(foo); + var serializedInt1 = NewtonsoftJsonSerializer.SerializeObject(foo.Value); + + var serializedFoo2 = SystemTextJsonSerializer.Serialize(foo); + var serializedInt2 = SystemTextJsonSerializer.Serialize(foo.Value); + + Assert.Equal(serializedFoo1, serializedInt1); + Assert.Equal(serializedFoo2, serializedInt2); + } [Fact] public void WhenNoJsonConverter_SerializesWithValueProperty() { var foo = new NoJsonIntId(123); - var serialized = JsonConvert.SerializeObject(foo); - var expected = "{\"Value\":123}"; + var serialized1 = NewtonsoftJsonSerializer.SerializeObject(foo); + var serialized2 = SystemTextJsonSerializer.Serialize(foo); - Assert.Equal(expected, serialized); - } + var expected = "{\"Value\":" + foo.Value + "}"; + Assert.Equal(expected, serialized1); + Assert.Equal(expected, serialized2); + } } } \ No newline at end of file diff --git a/test/StronglyTypedId.Tests/NoJsonGeneratedId.cs b/test/StronglyTypedId.Tests/NoJsonGeneratedId.cs deleted file mode 100644 index cb80a3880..000000000 --- a/test/StronglyTypedId.Tests/NoJsonGeneratedId.cs +++ /dev/null @@ -1,5 +0,0 @@ -namespace StronglyTypedId -{ - [StronglyTypedId(generateJsonConverter: false)] - public partial struct NoJsonGeneratedId {} -} \ No newline at end of file diff --git a/test/StronglyTypedId.Tests/StringId.cs b/test/StronglyTypedId.Tests/StringId.cs deleted file mode 100644 index d40bd9ced..000000000 --- a/test/StronglyTypedId.Tests/StringId.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace StronglyTypedId -{ - [StronglyTypedId(backingType: StronglyTypedIdBackingType.String)] - partial struct StringId { } - - [StronglyTypedId(generateJsonConverter: false, backingType: StronglyTypedIdBackingType.String)] - partial struct NoJsonStringId { } -} \ No newline at end of file diff --git a/test/StronglyTypedId.Tests/StringIdTests.cs b/test/StronglyTypedId.Tests/StringIdTests.cs index f84964dd7..36a4280bc 100644 --- a/test/StronglyTypedId.Tests/StringIdTests.cs +++ b/test/StronglyTypedId.Tests/StringIdTests.cs @@ -1,8 +1,9 @@ -using System; -using Newtonsoft.Json; +using StronglyTypedId.Tests.Types; using Xunit; +using NewtonsoftJsonSerializer = Newtonsoft.Json.JsonConvert; +using SystemTextJsonSerializer = System.Text.Json.JsonSerializer; -namespace StronglyTypedId +namespace StronglyTypedId.Tests { public class StringIdTests { @@ -49,48 +50,86 @@ public void OverloadsWorkCorrectly() [Fact] public void DifferentTypesAreUnequal() { - var bar = GeneratedId2.New(); + var bar = GuidId2.New(); var foo = new StringId("Value"); //Assert.NotEqual(bar, foo); // does not compile Assert.NotEqual((object)bar, (object)foo); } - + + [Fact] + public void CanSerializeToString_WithNewtonsoftJsonProvider() + { + var foo = new NewtonsoftJsonStringId("123"); + + var serializedFoo = NewtonsoftJsonSerializer.SerializeObject(foo); + var serializedString = NewtonsoftJsonSerializer.SerializeObject(foo.Value); + + Assert.Equal(serializedFoo, serializedString); + } + [Fact] - public void CanSerializeToString() + public void CanSerializeToString_WithSystemTextJsonProvider() { - var value = "value1"; - var foo = new StringId(value); + var foo = new SystemTextJsonStringId("123"); - var serializedFoo = JsonConvert.SerializeObject(foo); - var serializedString = JsonConvert.SerializeObject(value); + var serializedFoo = SystemTextJsonSerializer.Serialize(foo); + var serializedString = SystemTextJsonSerializer.Serialize(foo.Value); Assert.Equal(serializedFoo, serializedString); } [Fact] - public void CanSerializeFromString() + public void CanDeserializeFromString_WithNewtonsoftJsonProvider() { - var value = "value1"; - var foo = new StringId(value); + var value = "123"; + var foo = new NewtonsoftJsonStringId(value); + var serializedString = NewtonsoftJsonSerializer.SerializeObject(value); - var serializedValue = JsonConvert.SerializeObject(value); - var deserializedFoo = JsonConvert.DeserializeObject(serializedValue); + var deserializedFoo = NewtonsoftJsonSerializer.DeserializeObject(serializedString); Assert.Equal(foo, deserializedFoo); } + [Fact] + public void CanDeserializeFromString_WithSystemTextJsonProvider() + { + var value = "123"; + var foo = new SystemTextJsonStringId(value); + var serializedString = SystemTextJsonSerializer.Serialize(value); + + var deserializedFoo = SystemTextJsonSerializer.Deserialize(serializedString); + + Assert.Equal(foo, deserializedFoo); + } [Fact] - public void WhenNoJsonConverter_SerializesWithValueProperty() + public void CanSerializeToString_WithBothJsonConverters() { - var foo = new NoJsonStringId("the value"); + var foo = new BothJsonStringId("123"); - var serialized = JsonConvert.SerializeObject(foo); - var expected = "{\"Value\":\"the value\"}"; + var serializedFoo1 = NewtonsoftJsonSerializer.SerializeObject(foo); + var serializedString1 = NewtonsoftJsonSerializer.SerializeObject(foo.Value); - Assert.Equal(expected, serialized); + var serializedFoo2 = SystemTextJsonSerializer.Serialize(foo); + var serializedString2 = SystemTextJsonSerializer.Serialize(foo.Value); + + Assert.Equal(serializedFoo1, serializedString1); + Assert.Equal(serializedFoo2, serializedString2); } + [Fact] + public void WhenNoJsonConverter_SerializesWithValueProperty() + { + var foo = new NoJsonStringId("123"); + + var serialized1 = NewtonsoftJsonSerializer.SerializeObject(foo); + var serialized2 = SystemTextJsonSerializer.Serialize(foo); + + var expected = "{\"Value\":\"" + foo.Value + "\"}"; + + Assert.Equal(expected, serialized1); + Assert.Equal(expected, serialized2); + } } } \ No newline at end of file diff --git a/test/StronglyTypedId.Tests/StronglyTypedId.Tests.csproj b/test/StronglyTypedId.Tests/StronglyTypedId.Tests.csproj index 11f84d090..92f80f751 100644 --- a/test/StronglyTypedId.Tests/StronglyTypedId.Tests.csproj +++ b/test/StronglyTypedId.Tests/StronglyTypedId.Tests.csproj @@ -8,6 +8,7 @@ + diff --git a/test/StronglyTypedId.Tests/Types/GuidId.cs b/test/StronglyTypedId.Tests/Types/GuidId.cs new file mode 100644 index 000000000..22c216a33 --- /dev/null +++ b/test/StronglyTypedId.Tests/Types/GuidId.cs @@ -0,0 +1,20 @@ +namespace StronglyTypedId.Tests.Types +{ + [StronglyTypedId] + partial struct GuidId1 { } + + [StronglyTypedId] + public partial struct GuidId2 { } + + [StronglyTypedId(generateJsonConverter: false)] + public partial struct NoJsonGuidId { } + + [StronglyTypedId(jsonConverter: StronglyTypedIdJsonConverter.NewtonsoftJson)] + public partial struct NewtonsoftJsonGuidId { } + + [StronglyTypedId(jsonConverter: StronglyTypedIdJsonConverter.SystemTextJson)] + public partial struct SystemTextJsonGuidId { } + + [StronglyTypedId(jsonConverter: StronglyTypedIdJsonConverter.NewtonsoftJson | StronglyTypedIdJsonConverter.SystemTextJson)] + public partial struct BothJsonGuidId { } +} \ No newline at end of file diff --git a/test/StronglyTypedId.Tests/Types/IntId.cs b/test/StronglyTypedId.Tests/Types/IntId.cs new file mode 100644 index 000000000..83437a153 --- /dev/null +++ b/test/StronglyTypedId.Tests/Types/IntId.cs @@ -0,0 +1,17 @@ +namespace StronglyTypedId.Tests.Types +{ + [StronglyTypedId(backingType: StronglyTypedIdBackingType.Int)] + partial struct IntId { } + + [StronglyTypedId(generateJsonConverter: false, backingType: StronglyTypedIdBackingType.Int)] + public partial struct NoJsonIntId { } + + [StronglyTypedId(jsonConverter: StronglyTypedIdJsonConverter.NewtonsoftJson, backingType: StronglyTypedIdBackingType.Int)] + public partial struct NewtonsoftJsonIntId { } + + [StronglyTypedId(jsonConverter: StronglyTypedIdJsonConverter.SystemTextJson, backingType: StronglyTypedIdBackingType.Int)] + public partial struct SystemTextJsonIntId { } + + [StronglyTypedId(jsonConverter: StronglyTypedIdJsonConverter.NewtonsoftJson | StronglyTypedIdJsonConverter.SystemTextJson, backingType: StronglyTypedIdBackingType.Int)] + public partial struct BothJsonIntId { } +} \ No newline at end of file diff --git a/test/StronglyTypedId.Tests/Types/StringId.cs b/test/StronglyTypedId.Tests/Types/StringId.cs new file mode 100644 index 000000000..6a7cda7eb --- /dev/null +++ b/test/StronglyTypedId.Tests/Types/StringId.cs @@ -0,0 +1,17 @@ +namespace StronglyTypedId.Tests.Types +{ + [StronglyTypedId(backingType: StronglyTypedIdBackingType.String)] + partial struct StringId { } + + [StronglyTypedId(generateJsonConverter: false, backingType: StronglyTypedIdBackingType.String)] + public partial struct NoJsonStringId { } + + [StronglyTypedId(jsonConverter: StronglyTypedIdJsonConverter.NewtonsoftJson, backingType: StronglyTypedIdBackingType.String)] + public partial struct NewtonsoftJsonStringId { } + + [StronglyTypedId(jsonConverter: StronglyTypedIdJsonConverter.SystemTextJson, backingType: StronglyTypedIdBackingType.String)] + public partial struct SystemTextJsonStringId { } + + [StronglyTypedId(jsonConverter: StronglyTypedIdJsonConverter.NewtonsoftJson | StronglyTypedIdJsonConverter.SystemTextJson, backingType: StronglyTypedIdBackingType.String)] + public partial struct BothJsonStringId { } +} \ No newline at end of file diff --git a/version.props b/version.props index d084e7f28..837b10980 100644 --- a/version.props +++ b/version.props @@ -4,4 +4,4 @@ $(VersionPrefix) $(VersionPrefix)-$(VersionSuffix) - \ No newline at end of file +