如何使用 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 属性?的主要内容,如果未能解决你的问题,请参考以下文章