Sử dụng thuộc tính XmlInclude hoặc SoapInclude để chỉ định các loại tĩnh không được biết đến


98

Tôi gặp sự cố rất lạ khi làm việc với .NET's XmlSerializer.

Lấy các lớp ví dụ sau:

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, có ba phương pháp khác nhau để giải quyết vấn đề InvalidOperationExceptiondo bộ nối tiếp không biết về các loại dẫn xuất của Payment.

1. Thêm XmlIncludevào Paymentđịnh nghĩa lớp:

Điều này là không thể do tất cả các lớp được bao gồm dưới dạng tham chiếu bên ngoài mà tôi không có quyền kiểm soát.

2. Truyền các loại kiểu dẫn xuất trong khi tạo XmlSerializerphiên bản

Không hoạt động.

3. Định nghĩa XmlAttributeOverridescho thuộc tính đích để ghi đè tuần tự hóa mặc định của thuộc tính (như được giải thích trong bài đăng SO này )

Cũng không hoạt động ( XmlAttributeOverrideskhởi tạo sau).

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

Hàm tạo thích hợp XmlSerializersau đó sẽ được sử dụng.

LƯU Ý: không hoạt động Tôi có nghĩa là InvalidOperationException( BankPaymentkhông mong đợi ... ) được ném.

Bất cứ ai có thể làm sáng tỏ về chủ đề này? Làm thế nào để xử lý và gỡ lỗi thêm vấn đề?

Câu trả lời:


93

Điều này đã làm việc cho tôi:

[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
Vì vậy, loại cơ sở cần phải biết tất cả các triển khai của nó? Đây có vẻ không phải là một giải pháp tốt cho lắm. Có không có cách nào khác?
Alexander Stolz

2
@AlexanderStolz để triển khai chung chuyển Kiểu mới trong khi tạo Đối tượng XmlSerializable là giải pháp tốt nhất. Như đã đề cập stackoverflow.com/a/2689660/698127
Aamol

39

Chỉ cần giải quyết vấn đề. Sau khi tìm hiểu thêm một thời gian nữa, tôi đã tìm thấy bài đăng SO này có cùng tình huống. Nó đã đưa tôi đi đúng hướng.

Về cơ bản, XmlSerializercần biết không gian tên mặc định nếu các lớp dẫn xuất được bao gồm dưới dạng các kiểu bổ sung. Lý do chính xác tại sao điều này phải xảy ra vẫn chưa được biết nhưng, vẫn còn, quá trình tuần tự hóa đang hoạt động.


2

Tôi đồng ý với bizl

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

ngoài ra nếu bạn cần áp dụng lớp bao gồm này cho một mục đối tượng, bạn có thể làm như vậy

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

1

Chỉ cần làm điều đó trong Cơ sở, theo cách đó bất kỳ đứa trẻ nào cũng có thể được Serialized, ít mã sạch hơn.

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

Bằng cách này, bạn có thể gọi Serialize trên lớp con bất kể trường hợp nào và vẫn có thể làm những gì bạn cần trước khi Serializes đối tượng.


0

Dựa trên điều này, tôi đã có thể giải quyết điều này bằng cách thay đổi hàm tạo của XmlSerializertôi đang sử dụng thay vì thay đổi các lớp.

Thay vì sử dụng một cái gì đó như thế này (được đề xuất trong các câu trả lời khác):

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

Tôi đã làm điều này:

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();
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.