This is possible using the default infrastructure by making use of properties that return an object of type XmlComment
and marking those properties with [XmlAnyElement("SomeUniquePropertyName")]
.
I.e. if you add a property to Foo
like this:
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; }
}
The following XML will be generated:
<Foo>
<!--The application version, NOT the file version!-->
<Version>1.0</Version>
<Name>Bar</Name>
</Foo>
However, the question is asking for more than this, namely some way to look up the comment in a documentation system. The following accomplishes this by using extension methods to look up the documentation based on the reflected comment property name:
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);
}
}
For which the following XML is generated:
<Foo>
<!--The application version, NOT the file version!-->
<Version>1.0</Version>
<!--The application name, NOT the file name!-->
<Name>Bar</Name>
</Foo>
Notes:
The extension method XmlCommentExtensions.GetXmlCommentAttribute(this Type type, string memberName)
assumes that the comment property will be named xxxXmlComment
where xxx
is the "real" property. If so, it can automatically determine the real property name by marking the incoming memberName
attribute with CallerMemberNameAttribute
. This can be overridden manually by passing in the real name.
Once the type and member name are known, the extension method looks up the relevant comment by searching for an [XmlComment]
attribute applied to the property. This could be replaced with a cached lookup into a separate documentation file.
While it is still necessary to add the xxxXmlComment
properties for each property that might be commented, this is likely to be less burdensome than implementing IXmlSerializable
directly which is quite tricky, can lead to bugs in deserialization, and can require nested serialization of complex child properties.
To ensure that each comment precedes its associated element, see Controlling order of serialization in C#.
For XmlSerializer
to serialize a property it must have both a getter and setter. Thus I gave the comment properties setters that do nothing.
Working .Net fiddle.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…