XmlIncludeまたはSoapInclude属性を使用して、静的に認識されていないタイプを指定します


96

.NETで作業しているときに非常に奇妙な問題が発生しましたXmlSerializer

次のクラスの例を見てください。

public class Order 
{
    public PaymentCollection Payments { get; set; }

    //everything else is serializable (including other collections of non-abstract types)
}

public class PaymentCollection : Collection<Payment>
{
}

public abstract class Payment 
{
    //abstract methods
}

public class BankPayment : Payment
{
    //method implementations
}

私の知る限り、シリアライザがInvalidOperationExceptionの派生型を認識していないことが原因で発生するを解決するには、3つの異なる方法がありPaymentます。

1.追加するXmlIncludeにはPayment、クラスの定義:

すべてのクラスが、私が制御できない外部参照として含まれているため、これは不可能です。

2. XmlSerializerインスタンスの作成中に派生型の型を渡す

動作しません。

3. XmlAttributeOverridesプロパティのデフォルトのシリアル化をオーバーライドするためのターゲットプロパティの定義(このSO投稿で説明)

また、機能しません(XmlAttributeOverrides初期化が続きます)。

Type bankPayment = typeof(BankPayment);

XmlAttributes attributes = new XmlAttributes();
attributes.XmlElements.Add(new XmlElementAttribute(bankPayment.Name, bankPayment));

XmlAttributeOverrides overrides = new XmlAttributeOverrides();
overrides.Add(typeof(Order), "Payments", attributes);

適切な XmlSerializerコンストラクターが使用されます。

注:によって機能しませんInvalidOperationException(つまり、BankPayment予期されていなかった...)がスローます。

誰もが主題にいくつかの光を当てることができますか?どのようにして問題を解決し、さらにデバッグしますか?

回答:


91

これは私のために働きました:

[XmlInclude(typeof(BankPayment))]
[Serializable]
public abstract class Payment { }    

[Serializable]
public class BankPayment : Payment {} 

[Serializable]
public class Payments : List<Payment>{}

XmlSerializer serializer = new XmlSerializer(typeof(Payments), new Type[]{typeof(Payment)});

15
それで、基本型はその実装のすべてを知る必要がありますか?これは非常に良い解決策のようには見えません。他に方法はありませんか?
Alexander Stolz、2014

2
@AlexanderStolzは、XmlSerializableオブジェクトの作成中に新しいTypeを渡す汎用的な実装が最適なソリューションです。前述のとおり、stackoverflow.com
a / 2689660/698127

39

問題を解決しました。しばらく掘り下げた後、このSOの投稿を見つけました、まったく同じ状況をカバーする。それは正しい軌道に乗った。

基本的に、派生クラスが追加の型として含まれている場合XmlSerializer、はデフォルトの名前空間を知る必要があります。これが発生しなければならない正確な理由はまだ不明ですが、それでもシリアル化が機能しています。


2

私はbizlに同意します

[XmlInclude(typeof(ParentOfTheItem))]
[Serializable]
public abstract class WarningsType{ }

また、このインクルードクラスをオブジェクトアイテムに適用する必要がある場合は、そのようにすることができます

[System.Xml.Serialization.XmlElementAttribute("Warnings", typeof(WarningsType))]
public object[] Items
{
    get
    {
        return this.itemsField;
    }
    set
    {
        this.itemsField = value;
    }
}

1

Baseでそれを行うだけで、そのようにしてすべての子をシリアル化でき、コードクリーナーコードを削減できます。

public abstract class XmlBaseClass  
{
  public virtual string Serialize()
  {
    this.SerializeValidation();

    XmlSerializerNamespaces XmlNamespaces = new XmlSerializerNamespaces(new[] { XmlQualifiedName.Empty });
    XmlWriterSettings XmlSettings = new XmlWriterSettings
    {
      Indent = true,
      OmitXmlDeclaration = true
    };

    StringWriter StringWriter = new StringWriter();

    XmlSerializer Serializer = new XmlSerializer(this.GetType());
    XmlWriter XmlWriter = XmlWriter.Create(StringWriter, XmlSettings);
    Serializer.Serialize(XmlWriter, this, XmlNamespaces);
    StringWriter.Flush();
    StringWriter.Close();

    return StringWriter.ToString();

  }

  protected virtual void SerializeValidation() {}
}

[XmlRoot(ElementName = "MyRoot", Namespace = "MyNamespace")]
public class XmlChildClass : XmlBaseClass
{
  protected override void SerializeValidation()
  {
    //Add custom validation logic here or anything else you need to do
  }
}

このようにして、状況に関係なく子クラスでSerializeを呼び出し、オブジェクトSerializeの前に必要なことを実行できます。


0

これに基づいXmlSerializer、クラスを変更する代わりに、使用していたコンストラクタを変更することでこれを解決することができました。

このようなものを使用する代わりに(他の回答で提案されています):

[XmlInclude(typeof(Derived))]
public class Base {}

public class Derived : Base {}

public void Serialize()
{
    TextWriter writer = new StreamWriter(SchedulePath);
    XmlSerializer xmlSerializer = new XmlSerializer(typeof(List<Derived>));
    xmlSerializer.Serialize(writer, data);
    writer.Close();
}

これは私がしました:

public class Base {}

public class Derived : Base {}

public void Serialize()
{
    TextWriter writer = new StreamWriter(SchedulePath);
    XmlSerializer xmlSerializer = new XmlSerializer(typeof(List<Derived>), new[] { typeof(Derived) });
    xmlSerializer.Serialize(writer, data);
    writer.Close();
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.