public void Generate(GeneratorExecutionContext context) { // retreive the populated receiver if (context.SyntaxReceiver is not JsonSyntaxReceiver receiver) { return; } Compilation compilation = context.Compilation; compilation = GenerateFromResource("GenerationOutputFolderAttribute.cs", context, compilation, null); GenerationFolder = GetGenerationOutputFolder(receiver.CandidateAttributes, compilation); if (!Directory.Exists(GenerationFolder)) { GenerationFolder = null; } if (!string.IsNullOrEmpty(GenerationFolder)) { if (File.Exists(Path.Combine(GenerationFolder, "output.log"))) { File.Delete(Path.Combine(GenerationFolder, "output.log")); } } compilation = GenerateFromResource("InvalidJsonException.cs", context, compilation, GenerationFolder); compilation = GenerateFromResource("JsonArrayAttribute.cs", context, compilation, GenerationFolder); compilation = GenerateFromResource("JsonValueAttribute.cs", context, compilation, GenerationFolder); compilation = GenerateFromResource("JsonAttribute.cs", context, compilation, GenerationFolder); compilation = GenerateFromResource("JsonIgnoreNullAttribute.cs", context, compilation, GenerationFolder); compilation = GenerateFromResource("JsonDictionaryAttribute.cs", context, compilation, GenerationFolder); compilation = GenerateFromResource("JsonIgnoreAttribute.cs", context, compilation, GenerationFolder); compilation = GenerateFromResource("JsonOptionalAttribute.cs", context, compilation, GenerationFolder); compilation = GenerateFromResource("JsonListAttribute.cs", context, compilation, GenerationFolder); compilation = GenerateFromResource("JsonNameAttribute.cs", context, compilation, GenerationFolder); compilation = GenerateFromResource("JsonSpanExtensions.cs", context, compilation, GenerationFolder); compilation = GenerateFromResource("JsonUtf8SpanExtensions.cs", context, compilation, GenerationFolder); compilation = GenerateFromResource("ICustomConverter.cs", context, compilation, GenerationFolder); compilation = GenerateFromResource("CustomConverterAttribute.cs", context, compilation, GenerationFolder); compilation = GenerateFromResource("JsonUtf8Builder.cs", context, compilation, GenerationFolder); compilation = GenerateFromResource("IJsonBuilder.cs", context, compilation, GenerationFolder); compilation = GenerateFromResource("JsonStringBuilder.cs", context, compilation, GenerationFolder); var utf8Literals = new Utf8Literals(); var classBuilder = new CodeBuilder(utf8Literals); classBuilder.Append(@" using System; using System.Text; using System.Collections.Generic; using System.Runtime.CompilerServices; namespace JsonSrcGen { #nullable enable public class JsonConverter { [ThreadStatic] JsonStringBuilder? Builder; [ThreadStatic] JsonUtf8Builder? Utf8Builder; "); var classes = GetJsonClassInfo(receiver.Targets, compilation); var generators = new IJsonGenerator[] { new DateTimeGenerator(), new DateTimeOffsetGenerator(), new NullableDateTimeGenerator(), new NullalbeDateTimeOffsetGenerator(), new GuidGenerator(), new NullableGuidGenerator(), new AppendReadGenerator("Int32"), new AppendReadGenerator("UInt32"), new AppendReadGenerator("UInt64"), new AppendReadGenerator("Int64"), new AppendReadGenerator("Int16"), new AppendReadGenerator("UInt16"), new AppendReadGenerator("Byte"), new AppendReadGenerator("Double"), new AppendReadGenerator("Decimal"), new AppendReadGenerator("Single") { ReadType = "Double" }, new NullableAppendReadGenerator("UInt64?"), new NullableAppendReadGenerator("UInt32?"), new NullableAppendReadGenerator("UInt16?") { ReadType = "UInt32?" }, new NullableAppendReadGenerator("Byte?") { ReadType = "UInt32?" }, new NullableAppendReadGenerator("Int32?"), new NullableAppendReadGenerator("Int16?") { ReadType = "Int32?" }, new NullableAppendReadGenerator("Int64?"), new NullableAppendReadGenerator("Double?"), new NullableAppendReadGenerator("Decimal?"), new NullableAppendReadGenerator("Single?") { ReadType = "Double?" }, new BoolGenerator(), new NullableBoolGenerator(), new StringGenerator(), new ListGenerator(type => GetGeneratorForType(type)), new ArrayGenerator(type => GetGeneratorForType(type), new CodeBuilder(utf8Literals)), new DictionaryGenerator(type => GetGeneratorForType(type)), new CharGenerator() }; _generators = new Dictionary <string, IJsonGenerator>(); foreach (var generator in generators) { _generators.Add(generator.GeneratorId, generator); } foreach (var customClass in classes) { _generators.Add(customClass.FullName, new CustomTypeGenerator(customClass.FullName)); } var customTypeConverters = GetCustomTypeConverters(receiver.Targets, compilation, utf8Literals); foreach (var customTypeConverter in customTypeConverters) { LogLine($"Adding customTypeConverter GeneratorId: {customTypeConverter.GeneratorId}"); if (_generators.ContainsKey(customTypeConverter.GeneratorId)) { LogLine($"overriding existing"); _generators[customTypeConverter.GeneratorId] = customTypeConverter; } else { LogLine($"new generator"); _generators.Add(customTypeConverter.GeneratorId, customTypeConverter); } } var toJsonGenerator = new ToJsonGenerator(GetGeneratorForType, utf8Literals); var fromJsonGenerator = new FromJsonGenerator(GetGeneratorForType, utf8Literals); var listTypes = GetListAttributesInfo(receiver.CandidateAttributes, compilation); foreach (var listType in listTypes) { toJsonGenerator.GenerateList(listType, classBuilder); toJsonGenerator.GenerateListUtf8(listType, classBuilder); fromJsonGenerator.GenerateList(listType, classBuilder); fromJsonGenerator.GenerateListUtf8(listType, classBuilder); } var arrayTypes = GetArrayAttributesInfo(receiver.CandidateAttributes, compilation); foreach (var arrayType in arrayTypes) { toJsonGenerator.GenerateArray(arrayType, classBuilder); toJsonGenerator.GenerateArrayUtf8(arrayType, classBuilder); fromJsonGenerator.GenerateArray(arrayType, classBuilder); fromJsonGenerator.GenerateArrayUtf8(arrayType, classBuilder); } var dictionaryTypes = GetDictionaryAttributesInfo(receiver.CandidateAttributes, compilation); foreach (var dictionaryType in dictionaryTypes) { toJsonGenerator.GenerateDictionary(dictionaryType.Item1, dictionaryType.Item2, classBuilder); toJsonGenerator.GenerateDictionaryUtf8(dictionaryType.Item1, dictionaryType.Item2, classBuilder); fromJsonGenerator.GenerateDictionary(dictionaryType.Item1, dictionaryType.Item2, classBuilder); fromJsonGenerator.GenerateDictionaryUtf8(dictionaryType.Item1, dictionaryType.Item2, classBuilder); } foreach (var jsonClass in classes) { toJsonGenerator.Generate(jsonClass, classBuilder); toJsonGenerator.GenerateUtf8(jsonClass, classBuilder); fromJsonGenerator.Generate(jsonClass, classBuilder); fromJsonGenerator.GenerateUtf8(jsonClass, classBuilder); } var valueTypes = GetValueAttributesInfo(receiver.CandidateAttributes, compilation); foreach (var valueType in valueTypes) { toJsonGenerator.GenerateValue(valueType, classBuilder); toJsonGenerator.GenerateValueUtf8(valueType, classBuilder); fromJsonGenerator.GenerateValue(valueType, classBuilder); fromJsonGenerator.GenerateValueUtf8(valueType, classBuilder); } foreach (var generator in _generators) { var codeBuilder = generator.Value.ClassLevelBuilder; if (codeBuilder != null) { classBuilder.Append(codeBuilder.ToString()); } } classBuilder.AppendLine(1, "}"); utf8Literals.GenerateMatches(classBuilder); classBuilder.AppendLine(1, "public partial class JsonUtf8Builder"); classBuilder.AppendLine(1, "{"); utf8Literals.GenerateCopy(classBuilder); classBuilder.AppendLine(1, "}"); classBuilder.AppendLine(0, "}"); classBuilder.AppendLine(0, "#nullable restore"); if (GenerationFolder != null) { try { File.WriteAllText(Path.Combine(GenerationFolder, "Generated.cs"), classBuilder.ToString()); } catch (DirectoryNotFoundException) { //Don't fail the generation as this makes the CI Unit Tests fail } } context.AddSource("JsonConverter", SourceText.From(classBuilder.ToString(), Encoding.UTF8)); }
IReadOnlyCollection <IJsonGenerator> GetCustomTypeConverters(List <TypeDeclarationSyntax> classDeclarations, Compilation compilation, Utf8Literals utf8Literals) { var customTypeConverters = new List <IJsonGenerator>(); foreach (var candidateClass in classDeclarations) { SemanticModel model = compilation.GetSemanticModel(candidateClass.SyntaxTree); var classSymbol = model.GetDeclaredSymbol(candidateClass); if (HasCustomConverterAttribute(classSymbol)) { string converterClassName = classSymbol.Name; string converterNamespace = classSymbol.ContainingNamespace.ToString(); var targetType = GetCustomConverterTargetType(classSymbol, model); if (ImplementsInterface(classSymbol, "JsonSrcGen.ICustomConverter")) { customTypeConverters.Add(new CustomConverterGenerator( targetType.GeneratorId, targetType.FullName, $"{converterNamespace}.{converterClassName}", new CodeBuilder(utf8Literals))); } } } return(customTypeConverters); }