如何使用 Roslyn 为类中具有特定返回类型的所有属性添加 JsonIgnore 属性?

Posted

技术标签:

【中文标题】如何使用 Roslyn 为类中具有特定返回类型的所有属性添加 JsonIgnore 属性?【英文标题】:How do I add a JsonIgnore attribute to all properties with a specific return type in a class using Roslyn? 【发布时间】:2021-01-01 11:56:30 【问题描述】:

在下面的代码中,一次只更新一个属性,之前更新的属性将被忽略。我知道语法节点是不可变的,所以我遗漏了一些东西。我最终得到的是只有最后一个被替换的属性被更新。我错过了什么?

var newMemberDeclarations = new SyntaxList<MemberDeclarationSyntax>();
newMemberDeclarations.AddRange(@class.Members);

foreach (var prop in props)

    var oldProperty = (PropertyDeclarationSyntax)prop;
    // e.g. Customer or ICollection<Customer>
    if (oldProperty.Type.Kind() == SyntaxKind.IdentifierName ||
        oldProperty.Type.Kind() == SyntaxKind.GenericName)
    
        var memberAttributes = new SyntaxList<AttributeListSyntax>().AddRange(oldProperty.AttributeLists).Add(jsonIgnoreAttribute);
        var newProperty = SyntaxFactory.PropertyDeclaration(
                            memberAttributes,
                            oldProperty.Modifiers,
                            oldProperty.Type,
                            oldProperty.ExplicitInterfaceSpecifier,
                            oldProperty.Identifier,
                            oldProperty.AccessorList
                            );
        var replaced = @class.Members.Replace(oldProperty, newProperty);
        // This ignores previously updated property 
        // and only adds the attribute on the last replaced property
        newMemberDeclarations.AddRange(replaced);                        
     

var attributes = @class.AttributeLists.AddRange(attributeLists);
root = root.ReplaceNode(@class, @class.WithAttributeLists(attributes))
           .AddUsings(usingSerialization, usingCollections, usingJson)
           .WithMembers(newMemberDeclarations)
           .NormalizeWhitespace();

【问题讨论】:

【参考方案1】:
            foreach (var classDeclaration in classDeclarations)
            
                var @class = classDeclaration as ClassDeclarationSyntax;

                List<AttributeListSyntax> attributeLists = new List<AttributeListSyntax>();
                attributeLists.Add(serializableAttribute);

                var guidTypeSyntax = SyntaxFactory.ParseTypeName(typeof(Guid).FullName) as QualifiedNameSyntax;
                var dateTimeTypeSyntax = SyntaxFactory.ParseTypeName(typeof(DateTime).FullName) as QualifiedNameSyntax;
                
                var customTypes = @class.DescendantNodes().Where(x => x is PropertyDeclarationSyntax)
                    .Select(x => (PropertyDeclarationSyntax)x)
                    .Where(x => x.Type.Kind() == SyntaxKind.IdentifierName || x.Type.Kind() == SyntaxKind.GenericName)
                    ;
                
                customTypes.ToList().ForEach(t => // .Type.GetType().FullName
                
                    bool isGuid = ((IdentifierNameSyntax)t.Type).Identifier.Value == guidTypeSyntax.Right.Identifier.Value;
                    bool isDateTime = ((IdentifierNameSyntax)t.Type).Identifier.Value == dateTimeTypeSyntax.Right.Identifier.Value;
                    
                    if (t.Type is GenericNameSyntax)
                    
                        var args = ((GenericNameSyntax)t.Type).TypeArgumentList.Arguments;
                        foreach(var arg in args)
                        
                            if(arg is IdentifierNameSyntax)
                            
                                if (!(isGuid || isDateTime))
                                
                                    var type = ((IdentifierNameSyntax)arg).Identifier.Value.ToString();
                                    var attribute = CreateKnownTypeAttribute(type);
                                    var knownTypeEntityHashSetAttribute = CreateKnownTypeAttribute($"HashSet<type>");
                                    attributeLists.Add(knownTypeEntityHashSetAttribute);
                                    attributeLists.Add(attribute);
                                
                            
                        
                     
                    else if(t.Type is IdentifierNameSyntax)
                    
                        if (!(isGuid || isDateTime))
                        
                            var identifier = ((IdentifierNameSyntax)t.Type).Identifier.Value.ToString();
                            var attribute = CreateKnownTypeAttribute(identifier);
                            var knownTypeEntityHashSetAttribute = CreateKnownTypeAttribute($"HashSet<identifier>");
                            attributeLists.Add(knownTypeEntityHashSetAttribute);
                            attributeLists.Add(attribute);
                        
                    
                );

                var members = new SyntaxList<MemberDeclarationSyntax>(@class.Members);
                
                customTypes.ToList().ForEach(oldProperty =>
                
                    var memberAttributes = new SyntaxList<AttributeListSyntax>().AddRange(oldProperty.AttributeLists).Add(jsonIgnoreAttribute);
                    var newProperty = SyntaxFactory.PropertyDeclaration(
                        memberAttributes,
                        oldProperty.Modifiers,
                        oldProperty.Type,
                        oldProperty.ExplicitInterfaceSpecifier,
                        oldProperty.Identifier,
                        oldProperty.AccessorList
                        ) as MemberDeclarationSyntax;

                    if (oldProperty.Type is IdentifierNameSyntax)
                    
                        bool isGuid = ((IdentifierNameSyntax)oldProperty.Type).Identifier.Value == guidTypeSyntax.Right.Identifier.Value;
                        bool isDateTime = ((IdentifierNameSyntax)oldProperty.Type).Identifier.Value == dateTimeTypeSyntax.Right.Identifier.Value;
                        bool isInt = oldProperty.Type.Kind() == SyntaxKind.IntKeyword;
                        bool isFloat = oldProperty.Type.Kind() == SyntaxKind.FloatKeyword;

                        if (!(isGuid || isDateTime))
                        
                            var identifier = oldProperty.Identifier.Value;
                            var indexOf = members.IndexOf(m => m is PropertyDeclarationSyntax ? ((PropertyDeclarationSyntax)m).Identifier.Value == identifier : false);
                            members = members.RemoveAt(indexOf).Add(newProperty);
                        
                    
                    else if(oldProperty.Type is GenericNameSyntax)
                    
                        var identifier = oldProperty.Identifier.Value;
                        var indexOf = members.IndexOf(m => m is PropertyDeclarationSyntax 
                           ? ((PropertyDeclarationSyntax)m).Identifier.Value == identifier : false);
                        members = members.RemoveAt(indexOf).Add(newProperty);
                    
                );

                var attributes = @class.AttributeLists.AddRange(attributeLists);
                var c = SyntaxFactory.ClassDeclaration(@class.Identifier)
                    .WithBaseList(@class.BaseList)
                    .AddModifiers(SyntaxFactory.Token(SyntaxKind.PublicKeyword), 
                                  SyntaxFactory.Token(SyntaxKind.PartialKeyword))
                    .WithAttributeLists(attributes)
                    .AddMembers(members.ToArray());

                root = root.ReplaceNode(@class, c)
                    .AddUsings(usingSerialization, usingCollections, usingSerialisation)
                    .NormalizeWhitespace();
            

【讨论】:

以上是关于如何使用 Roslyn 为类中具有特定返回类型的所有属性添加 JsonIgnore 属性?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 .h 和 .cpp 中为类中的私有 char 数组编写 get 函数?

如何在 Roslyn 代码生成器中生成数组类型?

如何将类属性设置为类方法返回类型?

part01.03 委托与 Lambda 表达式:委托

两张图示轻松看懂 UML 类图

Roslyn代码分析从无错误的解决方案返回错误的构建错误