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

在使用.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. Passing the derived types' types during creation of the XmlSerializer instance

Doesn't work.

3. 为目标属性定义 XmlAttributeOverrides,以覆盖该属性的默认序列化(如 这个职位中所解释的)

也不起作用(随后进行 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没有预料到..。)。

有人能就这个问题提供一些解释吗? 人们如何进一步调试这个问题呢?

128621 次浏览

问题解决了。在深入调查了一段时间后,我发现了 这个职位,它涵盖了完全相同的情况。这让我找到了正确的方向。

基本上,XmlSerializer需要知道默认名称空间 如果派生类作为额外类型包含在内。为什么会发生这种情况的确切原因仍然不清楚,但是序列化现在仍然在工作。

这对我很有效:

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

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

只需在 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 之前执行所需的操作。

I agree with 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;
}
}

我添加了这个 [System.Xml.Serialization.XmlAttributeAttribute()]的财产,它为我解决。