26

私は次のクラスを持っています

[XmlRoot]
public class AList
{
   public List<B> ListOfBs {get; set;}
}

public class B
{
   public string BaseProperty {get; set;}
}

public class C : B
{
    public string SomeProperty {get; set;}
}

public class Main
{
    public static void Main(string[] args)
    {
        var aList = new AList();
        aList.ListOfBs = new List<B>();
        var c = new C { BaseProperty = "Base", SomeProperty = "Some" };
        aList.ListOfBs.Add(c);

        var type = typeof (AList);
        var serializer = new XmlSerializer(type);
        TextWriter w = new StringWriter();
        serializer.Serialize(w, aList);
    }    
}

コードを実行しようとすると、最後の行で InvalidOperationException が発生しました。

タイプ XmlTest.C は予期されていませんでした。XmlInclude または SoapInclude 属性を使用して、静的に認識されていない型を指定します。

[XmlInclude(typeof(C))] 属性を [XmlRoot] に追加すると問題が解決することはわかっています。しかし、私はそれを動的に達成したいと考えています。私のプロジェクトでは、ロード前にクラス C が不明であるためです。クラス C はプラグインとして読み込まれているため、そこに XmlInclude 属性を追加することはできません。

私も試してみました

TypeDescriptor.AddAttributes(typeof(AList), new[] { new XmlIncludeAttribute(c.GetType()) });

var type = typeof (AList);

しかし、役に立たない。それはまだ同じ例外を与えています。

誰もそれを達成する方法について何か考えがありますか?

4

4 に答える 4

35

2つのオプション。最も単純な(ただし、奇妙なxmlを与える)のは次のとおりです。

XmlSerializer ser = new XmlSerializer(typeof(AList),
    new Type[] {typeof(B), typeof(C)});

出力例:

<AList xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <ListOfBs>
    <B />
    <B xsi:type="C" />
  </ListOfBs>
</AList>

よりエレガントなのは:

XmlAttributeOverrides aor = new XmlAttributeOverrides();
XmlAttributes listAttribs = new XmlAttributes();
listAttribs.XmlElements.Add(new XmlElementAttribute("b", typeof(B)));
listAttribs.XmlElements.Add(new XmlElementAttribute("c", typeof(C)));
aor.Add(typeof(AList), "ListOfBs", listAttribs);

XmlSerializer ser = new XmlSerializer(typeof(AList), aor);

出力例:

<AList xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <b />
  <c />
</AList>

いずれの場合も、インスタンスをキャッシュして再利用する必要があります。serそうしないと、動的コンパイルからメモリが出血します。

于 2010-04-22T09:37:45.850 に答える
10

Marc の最初の回答 (読むだけなので、奇妙な出力を防ぐ必要はありません) に基づいて構築し、このcodeprojectに触発されて、より動的/汎用的な型配列を使用して未知の型を説明します。

    public static XmlSerializer GetSerializer()
    {
        var lListOfBs = (from lAssembly in AppDomain.CurrentDomain.GetAssemblies()
                           from lType in lAssembly.GetTypes()
                           where typeof(B).IsAssignableFrom(lType)
                           select lType).ToArray();
        return new XmlSerializer(typeof(AList), lListOfBs);
    }

(おそらく、ローカル変数の代わりに静的または読み取り専用の型配列を使用するなど、より効率的にすることができます。これにより、リフレクションを繰り返し使用することを避けることができます。しかし、アセンブリがいつロードされ、クラスとプロパティが取得されるかについて、私は十分に知りません初期化すると問題が発生するかどうかを知ることができます.私の使用法はそれほど多くなく、これをすべて調査するのに時間がかかるため、同じリフレクションを複数回使用するだけです.)

于 2013-01-18T16:44:48.663 に答える
2

XmlSerializerのドキュメントをご覧ください。2番目のパラメーターとして既知の型を期待するコンストラクターがあります。これは、ユースケースでは問題なく機能するはずです。

于 2010-04-22T09:33:14.547 に答える
0

属性はCILコードでメタデータを作成するために使用されるため、実行時に適用できるとは思いません。

于 2010-04-22T09:33:26.943 に答える