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

AFAIK,有三种不同的方法来解决InvalidOperationException由序列化器不了解的派生类型引起的Payment

1.添加XmlIncludePayment类定义中:

由于所有类都被包含为我无法控制的外部引用,因此这是不可能的。

2.在XmlSerializer实例创建期间传递派生类型的类型

不起作用

3.定义XmlAttributeOverrides目标属性,以覆盖属性的默认序列化(如本SO post中所述

也不起作用(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然后将使用适当的构造函数。

注意:由于不起作用,我的意思是InvalidOperationExceptionBankPayment没想到...)被抛出。

谁能阐明这个问题?人们将如何进行进一步的调试?

Answers:


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
因此,基本类型需要了解其所有实现吗?这似乎不是一个很好的解决方案。有没有其他办法?
亚历山大·斯托尔兹

2
通用实现的@AlexanderStolz在创建XmlSerializable Object时传递新的Type是最佳解决方案。如前所述stackoverflow.com/a/2689660/698127
Aamol 2015年

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

这样,无论何种情况,您都可以在子类上调用序列化,并且仍然可以执行对象序列化之前需要做的事情。


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();
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.