使用 XmlInclude 或 SoapInclude 属性指定静态未知的类型

Posted

技术标签:

【中文标题】使用 XmlInclude 或 SoapInclude 属性指定静态未知的类型【英文标题】:Use the XmlInclude or SoapInclude attribute to specify types that are not known statically 【发布时间】:2012-08-06 20:21:27 【问题描述】:

在使用 .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,有三种不同的方法可以解决由于序列化程序不知道Payment 的派生类型而导致的InvalidOperationException

1.将XmlInclude 添加到Payment 类定义中:

这是不可能的,因为所有类都作为我无法控制的外部引用包含在内。

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

没用。

3。为目标属性定义XmlAttributeOverrides 以覆盖属性的默认序列化(如this 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 不是预期的......)被抛出。

任何人都可以对这个主题有所了解吗?如何进一步解决问题?

【问题讨论】:

【参考方案1】:

这对我有用:

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

【讨论】:

所以基类型需要知道它的所有实现?这似乎不是一个很好的解决方案。没有别的办法吗? @AlexanderStolz 用于在创建 XmlSerializable 对象时传递新类型的通用实现是最佳解决方案。如前所述***.com/a/2689660/698127 [Serializable] 属性不是必需的。您只需要通过抽象类的名称指定期望序列化的类型 - 使用 XmlInclude 属性。【参考方案2】:

刚刚解决了这个问题。在挖掘了一段时间后,我发现this SO post 涵盖了完全相同的情况。它让我走上了正轨。

基本上,XmlSerializer 需要知道默认命名空间 if 派生类作为额外类型包含在内。必须发生这种情况的确切原因仍然未知,但序列化现在仍在工作。

【讨论】:

【参考方案3】:

基于this,我能够通过更改我正在使用的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();

【讨论】:

【参考方案4】:

只需在 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,并且仍然能够在对象序列化之前执行您需要做的事情。

【讨论】:

我想突出显示代码行,这对于可视化我们可以避免 XmlInclude 或其他属性的方式很重要:XmlSerializer Serializer = new XmlSerializer(this.GetType());【参考方案5】:

我同意 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;
    

【讨论】:

以上是关于使用 XmlInclude 或 SoapInclude 属性指定静态未知的类型的主要内容,如果未能解决你的问题,请参考以下文章

用 XmlInclude 修饰的基类在序列化时仍然会抛出类型未知的异常是不是有原因?

用 XmlInclude 修饰的基类在序列化时仍然会抛出类型未知的异常是不是有原因?

反序列化 xml 时忽略未知类型

DOM解析xml实现读写增删改

什么时候需要或方便地使用 Spring 或 EJB3 或同时使用它们?

如何使用 javascript 或 Jquery 使用文件系统或操作?