Ejemplo n.º 1
0
        private static JensonPropertyInfo CreatePropertyInfo(JensonContext context, IPropertySymbol prop)
        {
            var    name     = prop.Name;
            string?jsonName = null;
            int?   order    = null;
            string?shouldSerializeFunction = null;

            var canBeNull = !prop.Type.IsValueType;

            var isReadOnly      = prop.IsReadOnly;
            var shouldSerialize = !prop.IsWriteOnly &&
                                  prop.GetMethod !.DeclaredAccessibility != Accessibility.Private &&
                                  prop.GetMethod.DeclaredAccessibility != Accessibility.Protected;
            var shouldDeserialize = !prop.IsReadOnly &&
                                    prop.SetMethod !.DeclaredAccessibility != Accessibility.Private &&
                                    prop.SetMethod.DeclaredAccessibility != Accessibility.Protected;

            var typeFormat = new SymbolDisplayFormat(typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces);
            var propType   = prop.Type.ToDisplayString(typeFormat);

            foreach (var attr in prop.GetAttributes())
            {
                if (SymbolEqualityComparer.Default.Equals(attr.AttributeClass, context.JsonIgnoreAttribute))
                {
                    shouldSerialize   = false;
                    shouldDeserialize = false;
                }
                else if (SymbolEqualityComparer.Default.Equals(attr.AttributeClass, context.JsonPropertyNameAttribute))
                {
                    jsonName = (string)attr.ConstructorArguments[0].Value !;
                }
                else if (SymbolEqualityComparer.Default.Equals(attr.AttributeClass, context.JensonPropertyAttribute))
                {
                    attr.TryGetProperty("Order", out order);
                    attr.TryGetProperty("ShouldSerializeFunction", out shouldSerializeFunction);
                }
            }

            return(new JensonPropertyInfo(
                       name,
                       jsonName,
                       propType,
                       isReadOnly,
                       shouldSerialize,
                       shouldDeserialize,
                       canBeNull,
                       order,
                       shouldSerializeFunction));
        }
Ejemplo n.º 2
0
        private static List <JensonPropertyInfo> ExtractPropertyInfo(JensonContext context, INamedTypeSymbol t, string typeName)
        {
            // TODO need better filtering to avoid duplicate properties (e.g. virtual)
            // Actually we need to fold property overrides into parent props so
            // users can override attributes in inherited classes.
            // E.g. JsonIgnore on A.Foo, but JsonInclude on B.Foo where B : A
            var properties = t.GetThisAndBaseTypes()
                             .Reverse() // Reverse so we handle base types first (for merging)
                             .SelectMany(t =>
                                         t.GetMembers()
                                         .Where(m => m.Kind == SymbolKind.Property && !m.IsImplicitlyDeclared)
                                         .Select(p => (IPropertySymbol)p))
                             .OrderBy(p => p.Name);

            var propertiesInfo = GetPropertyInfoFromSymbols(context, properties);

            // Use OrderBy for stable sort
            return(propertiesInfo.OrderBy(p => p.Order ?? 0).ToList());
        }
Ejemplo n.º 3
0
        private static IEnumerable <JensonPropertyInfo> GetPropertyInfoFromSymbols(JensonContext context, IEnumerable <IPropertySymbol> properties)
        {
            using var en = properties.GetEnumerator();

            var propsLeft = en.MoveNext();

            while (propsLeft)
            {
                var propName = en.Current.Name;
                var propInfo = CreatePropertyInfo(context, en.Current);

                propsLeft = en.MoveNext();

                while (propsLeft && propName.Equals(en.Current.Name) && en.Current.IsOverride)
                {
                    var additionalInfo = CreatePropertyInfo(context, en.Current);
                    propInfo  = MergePropertyInfo(propInfo, additionalInfo);
                    propsLeft = en.MoveNext();
                }

                yield return(propInfo);
            }
        }
Ejemplo n.º 4
0
        public void Execute(SourceGeneratorContext generatorContext)
        {
            var ct          = generatorContext.CancellationToken;
            var compilation = generatorContext.Compilation;

            var jensonSerializeAttribute         = compilation.GetTypeByMetadataName("Jenson.Attributes.JensonSerializeAttribute");
            var jensonPropertyAttribute          = compilation.GetTypeByMetadataName("Jenson.Attributes.JensonPropertyAttribute");
            var jensonTypeDiscriminatorAttribute = compilation.GetTypeByMetadataName("Jenson.Attributes.JensonTypeDiscriminatorAttribute");
            var jsonPropertyNameAttribute        = compilation.GetTypeByMetadataName("System.Text.Json.Serialization.JsonPropertyNameAttribute");
            var jsonIgnoreAttribute = compilation.GetTypeByMetadataName("System.Text.Json.Serialization.JsonIgnoreAttribute");

            if (jensonSerializeAttribute is null)
            {
                throw new Exception("Missing JensonSerializeAttribute in compilation.");
            }
            if (jensonPropertyAttribute is null)
            {
                throw new Exception("Missing JensonPropertyAttribute in compilation.");
            }
            if (jensonTypeDiscriminatorAttribute is null)
            {
                throw new Exception("Missing JensonTypeDiscriminatorAttribute in compilation.");
            }
            if (jsonPropertyNameAttribute is null)
            {
                throw new Exception("Missing JsonPropertyName in compilation.");
            }
            if (jsonIgnoreAttribute is null)
            {
                throw new Exception("Missing JsonIgnore in compilation.");
            }

            var context = new JensonContext(
                generatorContext,
                jensonSerializeAttribute,
                jensonPropertyAttribute,
                jensonTypeDiscriminatorAttribute,
                jsonPropertyNameAttribute,
                jsonIgnoreAttribute);

            var rootTypes = compilation.GlobalNamespace.GetAllTypes(ct)
                            .Where(t => t.GetAttributes().Any(attr => SymbolEqualityComparer.Default.Equals(attr.AttributeClass, jensonSerializeAttribute)));

            ct.ThrowIfCancellationRequested();

            var sb = new StringBuilder();

            foreach (var t in rootTypes)
            {
                var typeDeclarations = t.DeclaringSyntaxReferences.Select(sr =>
                {
                    return((TypeDeclarationSyntax)sr.GetSyntax());
                });

                var isPartial = typeDeclarations.Any(td => td.Modifiers.Any(SyntaxKind.PartialKeyword));

                if (!isPartial)
                {
                    foreach (var td in typeDeclarations)
                    {
                        var diagnostic = Diagnostic.Create(MissingPartialModifier, td.GetLocation(), new object[] { t.Name });
                        generatorContext.ReportDiagnostic(diagnostic);
                    }
                }

                var w = new SourceWriter();
                GenerateTypeSerializer(context, w, t);

                generatorContext.AddSource($"{t.Name}.jenson", SourceText.From(w.ToString(), Encoding.UTF8));

                // for debugging
                sb.Append(w.ToString()); sb.AppendLine(); sb.AppendLine();
            }

            // for debugging
            File.WriteAllText("C:\\Users\\jesse\\Desktop\\converter.cs", sb.ToString());
        }
Ejemplo n.º 5
0
        private void GenerateTypeSerializer(JensonContext context, SourceWriter w, INamedTypeSymbol t)
        {
            AppendImports(w);

            w.Line();

            var ns       = t.ContainingNamespace;
            var nsString = ns.Name;

            ns = ns.ContainingNamespace;
            while (!ns.IsGlobalNamespace)
            {
                nsString = ns.Name + "." + nsString;
                ns       = ns.ContainingNamespace;
            }

            w.Line($"namespace {nsString}");
            w.Line("{");
            w.Indent();

            var typeName    = t.Name;
            var declKeyword = t.TypeKind == TypeKind.Class ? (t.IsRecord() ? "record" : "class") : "struct";

            w.Line($"[JsonConverter(typeof({typeName}Converter))]");
            w.Line($"public partial {declKeyword} {typeName} {{ }}");

            var propertiesInfo = ExtractPropertyInfo(context, t, typeName);

            w.Line($"public class {typeName}Converter : JsonConverter<{typeName}>");
            w.Line("{");
            w.Indent();

            var typeDiscriminatorAttr = t.GetAttributes()
                                        .FirstOrDefault(attr =>
                                                        SymbolEqualityComparer.Default.Equals(
                                                            attr.AttributeClass,
                                                            context.JensonTypeDiscriminatorAttribute));

            if (typeDiscriminatorAttr is not null)
            {
                var discrPropName = (string)typeDiscriminatorAttr.ConstructorArguments[0].Value;
                var discrFun      = (string)typeDiscriminatorAttr.ConstructorArguments[1].Value;

                var discrProp = propertiesInfo.First(p => p.Name.Equals(discrPropName));

                WriteTypeDiscriminatorConverterBody(w, typeName, discrProp, discrFun);
            }
            else
            {
                foreach (var p in propertiesInfo)
                {
                    // TODO When property naming policy is set this won't necessarily match
                    // UTF-8 json variable names. We should take JsonSerializerOptions into account.
                    // Relevant props:
                    // - PropertyNameCaseInsensitive
                    // - PropertyNamingPolicy

                    // TODO use utf8 string literals when available
                    w.Line($"private static readonly byte[] _{p.Name}Name = Encoding.UTF8.GetBytes(\"{p.JsonName ?? p.Name}\");");
                }

                w.Line();
                AppendRead(w, typeName, propertiesInfo);

                w.Line();
                AppendWrite(w, typeName, propertiesInfo);
            }

            w.Dedent();
            w.Line("}"); // class
            w.Dedent();
            w.Line("}"); // namespace

            Debug.Assert(w.Indentation == 0);
        }