XmlSerializer を使用するときに XML ファイルにコメントを書き込む方法は?

これは、タイプ 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>
  <!--The application version, NOT the file version!-->
  <Version>1.0</Version>
  <!--The application name, NOT the file name!-->
  <Name>Bar</Name>
</Foo>

注:

    <リ>

    拡張メソッド XmlCommentExtensions.GetXmlCommentAttribute(this Type type, string memberName) コメント プロパティの名前が xxxXmlComment であると仮定します どこで xxx 「本当の」財産です。その場合、受信した memberName をマークすることで、実際のプロパティ名を自動的に決定できます。 CallerMemberNameAttribute の属性 .これは、実名を渡すことで手動でオーバーライドできます。

    <リ>

    型とメンバー名がわかると、拡張メソッドは [XmlComment] を検索して関連するコメントを検索します。 プロパティに適用される属性。これは、別のドキュメント ファイルへのキャッシュされたルックアップに置き換えることができます。

    <リ>

    xxxXmlComment を追加する必要がありますが、 コメントされる可能性のある各プロパティのプロパティ。これは、IXmlSerializable を実装するよりも負担が少ない可能性があります。 これは非常にトリッキーであり、逆シリアル化でバグが発生する可能性があり、複雑な子プロパティのネストされたシリアル化が必要になる可能性があります。

    <リ>

    各コメントが関連する要素の前に表示されるようにするには、C# でのシリアル化の順序の制御を参照してください。

    <リ>

    XmlSerializer の場合 プロパティをシリアル化するには、ゲッターとセッターの両方が必要です。したがって、何もしないコメント プロパティ セッターを指定しました。

動作中の .Net フィドル。


デフォルトのインフラストラクチャを使用することはできません。 IXmlSerializable を実装する必要があります

非常に簡単な実装:

public class Foo : IXmlSerializable
{
    [XmlComment(Value = "The application version, NOT the file version!")]
    public string Version { get; set; }
    public string Name { get; set; }


    public void WriteXml(XmlWriter writer)
    {
        var properties = GetType().GetProperties();

        foreach (var propertyInfo in properties)
        {
            if (propertyInfo.IsDefined(typeof(XmlCommentAttribute), false))
            {
                writer.WriteComment(
                    propertyInfo.GetCustomAttributes(typeof(XmlCommentAttribute), false)
                        .Cast<XmlCommentAttribute>().Single().Value);
            }

            writer.WriteElementString(propertyInfo.Name, propertyInfo.GetValue(this, null).ToString());
        }
    }
    public XmlSchema GetSchema()
    {
        throw new NotImplementedException();
    }

    public void ReadXml(XmlReader reader)
    {
        throw new NotImplementedException();
    }
}

[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public class XmlCommentAttribute : Attribute
{
    public string Value { get; set; }
}

出力:

<?xml version="1.0" encoding="utf-16"?>
<Foo>
  <!--The application version, NOT the file version!-->
  <Version>1.2</Version>
  <Name>A</Name>
</Foo>

別の方法としては、おそらく望ましい方法です。デフォルトのシリアライザーでシリアライズしてから、後処理を実行します。つまり、XML を更新します。 XDocument を使用 または XmlDocument .