Statik olarak bilinmeyen türleri belirtmek için XmlInclude veya SoapInclude özniteliğini kullanın


99

.NET'lerle çalışırken çok garip bir sorun yaşıyorum XmlSerializer.

Aşağıdaki örnek sınıfları alın:

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
}

AFAIK, InvalidOperationExceptionserileştiricinin türetilmiş Payment.

1. ekleme XmlIncludeiçin Paymentsınıf tanımı:

Bu, üzerinde hiçbir kontrole sahip olmadığım dış referanslar olarak dahil edilen tüm sınıflar nedeniyle mümkün değil.

2. Türetilmiş türlerin türlerini, XmlSerializerörneğin oluşturulması sırasında iletme

Çalışmıyor.

3. XmlAttributeOverridesÖzelliğin varsayılan serileştirmesini geçersiz kılmak için hedef özelliği tanımlama ( bu SO yazısında açıklandığı gibi )

Ayrıca çalışmaz ( XmlAttributeOverridesaşağıdaki başlatma).

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);

Daha XmlSerializersonra uygun kurucu kullanılacaktır.

NOT: ile çalışmıyor demek istediğim InvalidOperationException( BankPaymentbeklenmiyordu ... ) atıldı.

Biri konuya biraz ışık tutabilir mi? Sorun nasıl devam eder ve daha fazla hata ayıklanır?

Yanıtlar:


94

Bu benim için çalıştı:

[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)});

16
Öyleyse temel türün tüm uygulamalarını bilmesi gerekiyor mu? Bu pek iyi bir çözüm gibi görünmüyor. Başka yolu yok mu?
Alexander Stolz

3
@AlexanderStolz, XmlSerializable Object oluştururken yeni Type geçiren genel uygulama için en iyi çözümdür. Belirtildiği gibi stackoverflow.com/a/2689660/698127
Aamol

39

Sadece sorunu çözdüm. Bir süre daha kazı yaptıktan sonra, aynı durumu kapsayan bu SO yazısını buldum . Beni doğru yola soktu.

Temel olarak, türetilmiş sınıflar ekstra türler olarak dahil edilmişseXmlSerializer , varsayılan ad alanını bilmeye ihtiyaç duyar . Bunun neden olması gerektiğinin kesin nedeni hala bilinmemektedir, ancak yine de serileştirme şu anda çalışıyor.


3

Base bu I yapıcısı değiştirerek bunu çözmek başardı XmlSerializerkullanılarak yerine sınıfları değişiyordu I.

Bunun gibi bir şey kullanmak yerine (diğer cevaplarda önerilmektedir):

[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();
}

Bunu ben yaptım:

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();
}

2

Bunu sadece Base'de yapın, bu şekilde herhangi bir çocuk Seri hale getirilebilir, daha az kod temizleyici kod.

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
  }
}

Bu şekilde, durum ne olursa olsun alt sınıfta Serialize çağırabilir ve yine de nesne Serializes'den önce ihtiyacınız olanı yapabilirsiniz.


2

Bizl'e katılıyorum

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

ayrıca bu dahil edilen sınıfı bir nesne öğesine uygulamanız gerekiyorsa, bunu yapabilirsiniz

[System.Xml.Serialization.XmlElementAttribute("Warnings", typeof(WarningsType))]
public object[] Items
{
    get
    {
        return this.itemsField;
    }
    set
    {
        this.itemsField = value;
    }
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.