使用默认基础设施是可以实现的,通过使用返回类型为 XmlComment
的属性,并将这些属性标记为 [XmlAnyElement("SomeUniquePropertyName")]
。
例如,如果您像这样向 Foo
添加属性:
public class Foo
{
[XmlAnyElement("VersionComment")]
public XmlComment VersionComment { get { return new XmlDocument().CreateComment("The application version, NOT the file version!"); } set { } }
public string Version { get; set; }
public string Name { get; set; }
}
以下 XML 将被生成:
<Foo>
<!--The application version, NOT the file version!-->
<Version>1.0</Version>
<Name>Bar</Name>
</Foo>
然而,问题的要求不仅如此,还需要一种在文档系统中查找注释的方法。以下使用扩展方法根据反射注释属性名称查找文档来实现这一点:
public class Foo
{
[XmlAnyElement("VersionXmlComment")]
public XmlComment VersionXmlComment { get { return GetType().GetXmlComment(); } set { } }
[XmlComment("The application version, NOT the file version!")]
public string Version { get; set; }
[XmlAnyElement("NameXmlComment")]
public XmlComment NameXmlComment { get { return GetType().GetXmlComment(); } set { } }
[XmlComment("The application name, NOT the file name!")]
public string Name { get; set; }
}
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public class XmlCommentAttribute : Attribute
{
public XmlCommentAttribute(string value)
{
this.Value = value;
}
public string Value { get; set; }
}
public static class XmlCommentExtensions
{
const string XmlCommentPropertyPostfix = "XmlComment";
static XmlCommentAttribute GetXmlCommentAttribute(this Type type, string memberName)
{
var member = type.GetProperty(memberName);
if (member == null)
return null;
var attr = member.GetCustomAttribute<XmlCommentAttribute>();
return attr;
}
public static XmlComment GetXmlComment(this Type type, [CallerMemberName] string memberName = "")
{
var attr = GetXmlCommentAttribute(type, memberName);
if (attr == null)
{
if (memberName.EndsWith(XmlCommentPropertyPostfix))
attr = GetXmlCommentAttribute(type, memberName.Substring(0, memberName.Length - XmlCommentPropertyPostfix.Length));
}
if (attr == null || string.IsNullOrEmpty(attr.Value))
return null;
return new XmlDocument().CreateComment(attr.Value);
}
}
以下XML是为此生成的:
<Foo>
<Version>1.0</Version>
<Name>Bar</Name>
</Foo>
注释:
扩展方法XmlCommentExtensions.GetXmlCommentAttribute(this Type type, string memberName)
假定注释属性将被命名为xxxXmlComment
,其中xxx
是“真正”的属性。如果是这样的话,它可以通过使用CallerMemberNameAttribute
标记传入的memberName
属性自动确定真实属性名称。这可以通过手动传递真实名称来覆盖。
一旦知道类型和成员名称,扩展方法通过搜索应用于属性的[XmlComment]
属性查找相关注释。这可以替换为缓存查找到单独的文档文件中。
虽然仍然需要为可能被注释的每个属性添加xxxXmlComment
属性,但这很可能比直接实现IXmlSerializable
要少负担得多,后者非常棘手,可能导致反序列化错误,并且可能需要复杂子属性的嵌套序列化。
要确保每个注释在其关联元素之前,请参见控制C#中序列化的顺序。
对于XmlSerializer
序列化属性,它必须同时具有getter和setter。因此,我给了注释属性一个什么都不做的setter。
正在使用 .Net fiddle 工作。