Skip to content

Json serialization #31

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Oct 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions N.SourceGenerators.UnionTypes.sln.DotSettings
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:String x:Key="/Default/Environment/Hierarchy/Build/SolBuilderDuo/UseMsbuildSolutionBuilder/@EntryValue">No</s:String></wpf:ResourceDictionary>
20 changes: 20 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,26 @@ public async Task SwitchAsyncMethod(FooResult result, CancellationToken cancella
}
```

### JSON serialization (EXPERIMENTAL)

To add JSON support
- add `JsonPolymorphicUnion` attribute to union type
- add `TypeDiscriminator` to each type variant

#### Limitations:
- .NET 7 or newer
- only complex type variants

#### Example
```csharp
[UnionType(typeof(JsonTestsFooJ), TypeDiscriminator = "Foo")]
[UnionType(typeof(JsonTestsBarJ), TypeDiscriminator = "Bar")]
[JsonPolymorphicUnion]
public partial class JsonTestsUnion
{
}
```

### Union to union converter

When one union type's variants is subset of another union type's variants use one of the following attributes to convert one type to another: `UnionConverterTo`, `UnionConverterFrom`, or `UnionConverter`.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
using BenchmarkDotNet.Attributes;

using N.SourceGenerators.UnionTypes.Benchmark.Models;

namespace N.SourceGenerators.UnionTypes.Benchmark.Benchmarks;

[MemoryDiagnoser]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ public static AwaitExpressionSyntax AwaitWithConfigureAwait(this ExpressionSynta
IdentifierName("ConfigureAwait")
)
).AddArgumentListArguments(
Argument(LiteralExpression(SyntaxKind.FalseLiteralExpression))
Argument(FalseLiteralExpression())
)
);
}
Expand Down
250 changes: 230 additions & 20 deletions src/N.SourceGenerators.UnionTypes/Helpers/RoslynUtils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,88 @@ internal static class RoslynUtils
{
public const string FuncType = "global::System.Func";
public const string ActionType = "global::System.Action";
public const string TaskType = "global::System.Threading.Tasks.Task";
public const string CancellationTokenType = "global::System.Threading.CancellationToken";
private const string TaskTypeName = "global::System.Threading.Tasks.Task";

private static ObjectCreationExpressionSyntax NewInvalidOperationException(ExpressionSyntax expression)
public static PredefinedTypeSyntax VoidType()
{
return ObjectCreationExpression(
IdentifierName("System.InvalidOperationException")
)
.AddArgumentListArguments(
Argument(expression)
);
return PredefinedType(Token(SyntaxKind.VoidKeyword));
}

public static PredefinedTypeSyntax ObjectType()
{
return PredefinedType(Token(SyntaxKind.ObjectKeyword));
}

public static PredefinedTypeSyntax StringType()
{
return PredefinedType(Token(SyntaxKind.StringKeyword));
}

public static TypeSyntax TaskType()
{
return IdentifierName(TaskTypeName);
}

public static TypeSyntax CancellationTokenType()
{
return IdentifierName("global::System.Threading.CancellationToken");
}

public static TypeSyntax DefaultJsonTypeInfoResolverType()
{
return IdentifierName("System.Text.Json.Serialization.Metadata.DefaultJsonTypeInfoResolver");
}

public static TypeSyntax JsonPropertyInfoType()
{
return IdentifierName("System.Text.Json.Serialization.Metadata.JsonPropertyInfo");
}

public static ParameterSyntax Parameter(string type, string name)
{
return Parameter(IdentifierName(type), name);
}

public static ParameterSyntax Parameter(TypeSyntax type, string name)
{
return SyntaxFactory.Parameter(Identifier(name))
.WithType(type);
}

public static ArgumentSyntax Argument(string name)
{
return SyntaxFactory.Argument(IdentifierName(name));
}

public static ObjectCreationExpressionSyntax NewInvalidOperationException(string message)
public static ThrowStatementSyntax ThrowInvalidOperationException(string message)
{
var expression = StringLiteral(message);
return NewInvalidOperationException(expression);
return ThrowInvalidOperationException(expression);
}

public static ThrowStatementSyntax ThrowInvalidOperationException(ExpressionSyntax expression)
{
return ThrowException(
"System.InvalidOperationException",
SyntaxFactory.Argument(expression)
);
}

public static ThrowStatementSyntax ThrowException(string type, params ArgumentSyntax[] arguments)
{
return ThrowException(IdentifierName(type), arguments);
}

private static ThrowStatementSyntax ThrowException(TypeSyntax type, params ArgumentSyntax[] arguments)
{
return ThrowStatement(
ObjectCreationExpression(
type
)
.AddArgumentListArguments(
arguments
)
);
}

public static GenericNameSyntax GenericType(string type, string t1)
Expand All @@ -33,35 +98,72 @@ public static GenericNameSyntax GenericType(string type, string t1)

public static GenericNameSyntax TaskIdentifier(string type)
{
return GenericType(TaskType, type);
return GenericType(TaskTypeName, type);
}

public static MemberAccessExpressionSyntax MemberAccess(string expression, string name)
public static MemberAccessExpressionSyntax MemberAccess(
string expression,
string name,
params string[] pathItems)
{
return MemberAccess(IdentifierName(expression), name);
var result = MemberAccess(IdentifierName(expression), name);

foreach (var path in pathItems)
{
result = MemberAccess(result, path);
}

return result;
}

public static MemberAccessExpressionSyntax MemberAccess(ExpressionSyntax expression, string name)

public static MemberAccessExpressionSyntax MemberAccess(
ExpressionSyntax expression,
string name,
params string[] pathItems)
{
return MemberAccessExpression(
var result = MemberAccessExpression(
SyntaxKind.SimpleMemberAccessExpression,
expression,
IdentifierName(name)
);

foreach (var path in pathItems)
{
result = MemberAccess(result, path);
}

return result;
}

public static ReturnStatementSyntax ReturnTrue()
{
return ReturnStatement(LiteralExpression(SyntaxKind.TrueLiteralExpression));
return ReturnStatement(TrueLiteralExpression());
}

public static LiteralExpressionSyntax TrueLiteralExpression()
{
return LiteralExpression(SyntaxKind.TrueLiteralExpression);
}

public static ReturnStatementSyntax ReturnFalse()
{
return ReturnStatement(LiteralExpression(SyntaxKind.FalseLiteralExpression));
return ReturnStatement(FalseLiteralExpression());
}

public static LiteralExpressionSyntax FalseLiteralExpression()
{
return LiteralExpression(SyntaxKind.FalseLiteralExpression);
}

private static LiteralExpressionSyntax StringLiteral(string message)
public static LiteralExpressionSyntax Utf8StringLiteral(string value)
{
return LiteralExpression(
SyntaxKind.Utf8StringLiteralExpression,
ParseToken($"\"{value}\"u8")
);
}

public static LiteralExpressionSyntax StringLiteral(string message)
{
return LiteralExpression(
SyntaxKind.StringLiteralExpression,
Expand Down Expand Up @@ -96,4 +198,112 @@ public static InterpolatedStringTextSyntax InterpolatedText(string value)
)
);
}

public static BinaryExpressionSyntax IsExpression(string varName, ExpressionSyntax right)
{
return BinaryExpression(
SyntaxKind.IsExpression,
IdentifierName(varName),
right
);
}

public static BinaryExpressionSyntax IsExpression(ExpressionSyntax left, ExpressionSyntax right)
{
return BinaryExpression(
SyntaxKind.IsExpression,
left,
right
);
}

public static BinaryExpressionSyntax EqualsExpression(ExpressionSyntax left, ExpressionSyntax right)
{
return BinaryExpression(
SyntaxKind.EqualsExpression,
left,
right
);
}

public static BinaryExpressionSyntax NotEqualsExpression(ExpressionSyntax left, ExpressionSyntax right)
{
return BinaryExpression(
SyntaxKind.NotEqualsExpression,
left,
right
);
}

public static InvocationExpressionSyntax NameOfExpressions(string varName)
{
return InvocationExpression(IdentifierName("nameof"))
.AddArgumentListArguments(
Argument(varName)
);
}

public static InitializerExpressionSyntax ObjectInitializerExpression(params ExpressionSyntax[] items)
{
return InitializerExpression(
SyntaxKind.ObjectInitializerExpression
).AddExpressions(
items
);
}

public static InitializerExpressionSyntax CollectionInitializerExpression(params ExpressionSyntax[] items)
{
return InitializerExpression(
SyntaxKind.CollectionInitializerExpression
).AddExpressions(
items
);
}

public static AssignmentExpressionSyntax SimpleAssignmentExpression(string left, string right)
{
return AssignmentExpression(
SyntaxKind.SimpleAssignmentExpression,
IdentifierName(left),
IdentifierName(right)
);
}

public static AssignmentExpressionSyntax SimpleAssignmentExpression(string left, ExpressionSyntax right)
{
return AssignmentExpression(
SyntaxKind.SimpleAssignmentExpression,
IdentifierName(left),
right
);
}

public static AssignmentExpressionSyntax SimpleAssignmentExpression(ExpressionSyntax left, ExpressionSyntax right)
{
return AssignmentExpression(
SyntaxKind.SimpleAssignmentExpression,
left,
right
);
}

public static LocalDeclarationStatementSyntax LocalVariableDeclaration(
TypeSyntax type,
string name,
ExpressionSyntax initExpression)
{
return LocalDeclarationStatement(
VariableDeclaration(
type
).AddVariables(
VariableDeclarator(name)
.WithInitializer(
EqualsValueClause(
initExpression
)
)
)
);
}
}
15 changes: 12 additions & 3 deletions src/N.SourceGenerators.UnionTypes/Models/UnionType.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using System.Collections.Immutable;
using System.Text;
using System.Text;

using N.SourceGenerators.UnionTypes.Extensions;

Expand Down Expand Up @@ -30,9 +29,13 @@ public bool UseStructLayout
}
}

public UnionTypeJsonOptions? JsonOptions { get; }
public bool GenerateJsonConverter { get; }

public UnionType(INamedTypeSymbol containerType,
TypeDeclarationSyntax? syntax,
IReadOnlyList<UnionTypeVariant> variants)
IReadOnlyList<UnionTypeVariant> variants,
UnionTypeJsonOptions? jsonOptions)
{
if (syntax != null)
{
Expand Down Expand Up @@ -82,6 +85,10 @@ public UnionType(INamedTypeSymbol containerType,
UnionTypeVariant variant = variants[variantIndex];
variant.IdConstValue = variantIndex + 1;
}

JsonOptions = jsonOptions;
GenerateJsonConverter = JsonOptions?.TypeDiscriminatorPropertyName != null
&& Variants.Any(v => v.HasValidTypeDiscriminator);
}

private static bool IsToStringMethod(ISymbol symbol)
Expand All @@ -104,6 +111,8 @@ public Diagnostic CreateDiagnostic(DiagnosticDescriptor descriptor, params objec
}
}

internal record UnionTypeJsonOptions(string TypeDiscriminatorPropertyName);

internal class UnionTypeComparer : IEqualityComparer<UnionType>
{
public static UnionTypeComparer Instance { get; } = new();
Expand Down
Loading