-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathJsonSerializerGenerator.cs
127 lines (105 loc) · 5.17 KB
/
JsonSerializerGenerator.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
using System.Text;
using Microsoft.CodeAnalysis;
using SyntaxReceiver = NetJsonAOT.Generators.SyntaxReceiverByAttribute
<System.SerializableAttribute, Microsoft.CodeAnalysis.CSharp.Syntax.TypeDeclarationSyntax>;
namespace NetJsonAOT.Generators;
[Generator]
internal partial class JsonSerializerGenerator : ISourceGenerator
{
public void Initialize(GeneratorInitializationContext context)
{
context.RegisterForSyntaxNotifications(receiverCreator: () =>
new SyntaxReceiver(
acceptSubTypes: true,
acceptableDeclarationTypes: Utilities.NonInterfaceDeclarationTypes
)
);
}
public void Execute(GeneratorExecutionContext context)
{
Console.WriteLine("Execute JsonSerializerGenerator");
var syntaxReceiver = context.SyntaxReceiver;
if (!syntaxReceiver.Is(out SyntaxReceiver? receiver))
return;
List<INamespaceSymbol> namespaces = [];
StringBuilder sb = new(80);
foreach (var syntax in receiver.Collected)
{
var semanticModel = context.Compilation.GetSemanticModel(syntax.SyntaxTree);
var symbol = semanticModel.GetDeclaredSymbol(syntax);
if (symbol == null)
{
Console.Error.WriteLine($"Symbol not found for {syntax.Kind()} at {syntax.GetLocation()}");
continue;
}
if (!Utilities.TryGetNamespace(symbol, namespaces, sb, out var namespaceString))
continue;
var accessModifier = Utilities.GetScope(syntax);
var tree = ClassGeneration.GenerateJsonSerializable(namespaceString, symbol.Name, accessModifier);
if (!tree.TryGetText(out var text))
{
text = tree.GetText();
}
Console.WriteLine("--------------------");
Console.WriteLine($"Generated code:\n{text}\n");
Console.WriteLine("--------------------\n\n");
var fullyQualifiedClassName = namespaceString + "." + symbol.Name;
var code = tree.GetText();
context.AddSource(fullyQualifiedClassName + ".Generated.cs", code);
Evil.RunJsonNetSourceGenerator(context.AddSource, context.Compilation, code);
}
// generate dictionary of jsonContextTypes
var codeBuilder = new StringBuilder();
codeBuilder.AppendLine("""
using System;
using System.Collections.Generic;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace NetJsonAOT
{
internal static class RuntimeJson
{
public static IReadOnlyDictionary<Type, Type> JsonContextTypes = new Dictionary<Type, Type>()
{
""");
foreach (var item in GeneratedTypes.GeneratedTypeInfos)
{
codeBuilder.AppendLine(
$"\t\t\t{{typeof({item.OriginalFullyQualifiedTypeName}), typeof({item.GeneratedTypeName})}},");
}
codeBuilder.AppendLine("}; // end dictionary");
codeBuilder.AppendLine("""
public static IReadOnlyDictionary<Type, JsonSerializerContext> JsonContexts = new Dictionary<Type, JsonSerializerContext>()
{
""");
foreach (var item in GeneratedTypes.GeneratedTypeInfos)
{
codeBuilder.AppendLine(
$"\t\t\t{{typeof({item.OriginalFullyQualifiedTypeName}), new {item.GeneratedTypeName}()}},");
}
codeBuilder.AppendLine("}; // end dictionary");
codeBuilder.AppendLine("""
public static IReadOnlyDictionary<Type, JsonSerializerOptions> JsonSerializerOptions;
static RuntimeJson()
{
var optionsDict = new Dictionary<Type, JsonSerializerOptions>();
foreach (var item in JsonContexts)
{
var options = new JsonSerializerOptions{
TypeInfoResolver = item.Value,
IncludeFields = true
};
optionsDict.Add(item.Key, options);
}
JsonSerializerOptions = optionsDict;
}
""");
codeBuilder.AppendLine("""
} // end class
} // end namespace
""");
var dictCode = codeBuilder.ToString();
context.AddSource("JsonContexts.Generated.cs", dictCode);
GeneratedTypes.GeneratedTypeInfos.Clear();
}
}