.net - .NETの拡張可能なオブジェクト階層でコレクションを適切にXMLシリアル化する方法

.net oop xml-serialization

シナリオ:


ConfigurationOptionOption1Option2の4つのクラスを取得しました
Option1Option2Optionから継承します
ConfigurationにはタイプList<Option>のプロパティがあります。このプロパティには、Optionから派生したオブジェクトを格納します
Optionは抽象的であり、実際のインスタンスを構築するために使用されることはありません。


問題:


これらのオブジェクトとメンバーに単純な[XmlType(...)]属性と[XmlElement(...)]属性で注釈を付けると、コレクション内のオブジェクトのシリアル化が、希望するものとは異なる結果になります
必要な[XmlArray(...)]属性と[XmlArrayItem(...)]属性を追加すると、必要なように見えますが、既知の項目タイプのリストをコンパイル時に指定する必要があるため、拡張性が不可能になります。


次に、実行できるLINQPadスクリプトの例を示します。

void Main()
{

    var Configuration = new Configuration();
    Configuration.Options.Add(new Option1());
    Configuration.Options.Add(new Option2());

    var serializer = new XmlSerializer(typeof(Configuration), new Type[]
    {
        typeof(Option1),
        typeof(Option2),
    });
    var ns = new XmlSerializerNamespaces();
    ns.Add(string.Empty, string.Empty);

    using (var writer = new StringWriter())
    {
        serializer.Serialize(writer, Configuration, ns);
        writer.ToString().Dump();
    }
}

[XmlType("configuration")]
public class Configuration
{
    private readonly List<Option> _Options = new List<Option>();

    [XmlElement("options")]
    public List<Option> Options { get { return _Options; } }

    [XmlArray("options2")]
    [XmlArrayItem(typeof(Option1))]
    [XmlArrayItem(typeof(Option2))]
    public List<Option> Options2 { get { return _Options; } }
}

public class Option { }

[XmlType("option1")]
public class Option1 : Option { }

[XmlType("option2")]
public class Option2 : Option { }


これを実行すると、次の出力が得られます。

<?xml version="1.0" encoding="utf-16"?>
<configuration>
  <options d2p1:type="option1" xmlns:d2p1="http://www.w3.org/2001/XMLSchema-instance" />
  <options d2p1:type="option2" xmlns:d2p1="http://www.w3.org/2001/XMLSchema-instance" />
  <options2>
    <option1 />
    <option2 />
  </options2>
</configuration>


最初のプロパティoptionsoptions2とはまったく異なる方法でシリアル化されていることに注意してください。ただし、内容は同じです。

基本的に、シリアル化されたxmlをoptions2のようにしたいのですが、属性によってクラスの許可されたタイプを指定する必要がありません。これにより、拡張性が不可能になるためです。

実行時に変更できるものを介してそれらを提供する必要がある場合は問題ありませんが、コンパイル時にハードコードすることはできません。
答え
実行時に属性を指定できるXmlAttributeOverridesを確認する必要があると思います。これは簡単なことではありませんが、心が溶けることもありません。最大の難点:XmlAttributeOverridesを受け入れるコンストラクターを使用すると、アセンブリが毎回生成されるため、必ずXmlSerializerインスタンスをキャッシュして再利用してください。そうしないと、アセンブリがリークします(収集できません)。

実装するには、XmlAttributesXmlArrayを調整するXmlArrayItemsインスタンスがあり、XmlAttributeOverrides.Add(Type,Member,XmlAttributes)を使用してそれを"Options2"に関連付けます。
関連記事

c# - Winformsはビットマップ画像をクリアしますか?

c# - 待っているラムダ

.net - monodevelopで実行可能ファイルの名前を変更する

c# - C#クラスの設計-「プロジェクト」クラスの配置を検討すべき場所

.net - NHIbernate:ICompositeUserTypeでバッグをマップする方法

c# - WPFウィンドウコンストラクターを使用してカルチャを変更できますか?

c# - msiインストール後にexeを実行すると、起動チェックボックスが表示されますが、アプリが実行されません

c# - 0MQ Windows GUIのベストプラクティス

asp.net - ASP.NETおよびYouTubeプライバシー

.net - Powershell-XML構成データを検索してレジストリ値で置き換える