用 XmlInclude 修饰的基类在序列化时仍然会抛出类型未知的异常是不是有原因?
Posted
技术标签:
【中文标题】用 XmlInclude 修饰的基类在序列化时仍然会抛出类型未知的异常是不是有原因?【英文标题】:Is there a reason why a base class decorated with XmlInclude would still throw a type unknown exception when serialized?用 XmlInclude 修饰的基类在序列化时仍然会抛出类型未知的异常是否有原因? 【发布时间】:2021-12-25 10:05:12 【问题描述】:我将简化代码以节省空间,但所呈现的内容确实说明了核心问题。
我有一个类,它的属性是基类型。有 3 个派生类可以分配给该属性。
如果我将任何派生类分配给容器并尝试序列化容器,XmlSerializer 会抛出可怕的:
“类型 x 不是预期的。使用 XmlInclude 或 SoapInclude 属性来指定静态未知的类型。”
但是我的基类已经用那个属性装饰了,所以我认为必须有一个额外的“隐藏”要求。
真正奇怪的是,默认的 WCF 序列化程序在此类层次结构中没有问题。
容器类
[DataContract]
[XmlRoot(ElementName = "TRANSACTION", Namespace = Constants.Namespace)]
public class PaymentSummaryRequest : CommandRequest
[DataMember]
public PaymentSummary Summary get; set;
public PaymentSummaryRequest()
Mechanism = CommandMechanism.PaymentSummary;
基类
[DataContract]
[XmlInclude(typeof(xPaymentSummary))]
[XmlInclude(typeof(yPaymentSummary))]
[XmlInclude(typeof(zPaymentSummary))]
[KnownType(typeof(xPaymentSummary))]
[KnownType(typeof(yPaymentSummary))]
[KnownType(typeof(zPaymentSummary))]
public abstract class PaymentSummary
派生类之一
[DataContract]
public class xPaymentSummary : PaymentSummary
序列化代码
var serializer = new XmlSerializer(typeof(PaymentSummaryRequest));
serializer.Serialize(Console.Out,new PaymentSummaryRequestSummary = new xPaymentSummary);
例外
System.InvalidOperationException: 生成 XML 文档时出错。 ---> System.InvalidOperationException:类型 xPaymentSummary 不是预期的。 使用 XmlInclude 或 SoapInclude 属性指定静态未知的类型。在
Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializationWriterPaymentSummaryRequest.Write13_PaymentSummary(String n, 字符串 ns, PaymentSummary o, Boolean isNullable, Boolean needType) 在
Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializationWriterPaymentSummaryRequest.Write14_PaymentSummaryRequest(字符串 n, 字符串 ns, PaymentSummaryRequest o, Boolean isNullable, Boolean needType) 在
Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializationWriterPaymentSummaryRequest.Write15_TRANSACTION(对象 o) --- 内部异常堆栈结束 跟踪---在
System.Xml.Serialization.XmlSerializer.Serialize(XmlWriter xmlWriter, 对象 o, XmlSerializerNamespaces 命名空间, String encodingStyle, String id) at
System.Xml.Serialization.XmlSerializer.Serialize(TextWriter textWriter, 对象 o, XmlSerializerNamespaces 命名空间) 在 UserQuery.RunUserAuthoredQuery() 中 c:\Users\Tedford\AppData\Local\Temp\uqacncyo.0.cs:line 47
【问题讨论】:
为什么要将 DataContract 与 XML Serializer 混合使用? 另外,请显示您用于序列化的代码,并发布包含所有内部异常的完整异常。只需捕获异常并发布 ex.ToString()。 他们真的有两个不同的目的。该类本质上是用于与 WCF 服务对话的 DTO。该服务本身正在序列化该 DTO 并将其存储以用于报告和故障排除目的。我将 WCF 特定属性保留在适当的位置,以防两者之间发生一些令人毛骨悚然的动作。 【参考方案1】:您看到的问题是因为 PaymentSummaryRequest
正在设置命名空间。您可以删除命名空间或将命名空间添加到 PaymentSummary
类:
[XmlRoot(Namespace = Constants.Namespace)]
[XmlInclude(typeof(xxxPaymentSummary))]
public abstract class PaymentSummary
正如@Tedford 在他下面的评论中提到的,只有在使用派生类型时才需要命名空间。
看起来在生成 XML 序列化程序集时,由于根节点设置了命名空间而基类没有,因此在生成的序列化程序集中不包括 XML 包含逻辑。
解决问题的另一种方法是从类本身中删除命名空间声明,并在 XmlSerializer
构造函数上指定命名空间:
var serializer = new XmlSerializer(
typeof(PaymentSummaryRequest),
Constants.Namespace
);
【讨论】:
我都试过了,因为这是异常详细信息所建议的。这两个属性都不会导致行为发生变化。但是,我将适当地更新示例以减少混淆。 需要注意的是,问题似乎只存在于序列化后期绑定元素中。如果容器类设置为直接使用 xxxPaymentSummary,则不需要同步 XML 命名空间。无论如何,很好的发现!【参考方案2】:我遇到了同样的问题,一些谷歌搜索把我带到了这里。我不能接受基类中的每个实现都具有XMLInclude
属性。我让它与通用序列化方法一起工作。首先,一些 OP 的原始示例,但为了清晰起见稍作修改:
public abstract class PaymentSummary
public string _summary;
public string _type;
public class xPaymentSummary : PaymentSummary
public xPaymentSummary()
public xPaymentSummary(string summary)
_summary = summary;
_type = this.GetType().ToString();
除了上述之外,还有一个yPaymentSummary
和zPaymentSummary
实现完全相同。这些被添加到一个集合中,然后可以在每个上调用序列化方法:
List<PaymentSummary> summaries = new List<PaymentSummary>();
summaries.Add(new xPaymentSummary("My summary is X."));
summaries.Add(new yPaymentSummary("My summary is Y."));
summaries.Add(new zPaymentSummary("My summary is Z."));
foreach (PaymentSummary sum in summaries)
SerializeRecord(sum);
最后是序列化方法——一个简单的泛型方法,在序列化时使用记录的类型:
static void SerializeRecord<T>(T record) where T: PaymentSummary
var serializer = new XmlSerializer(record.GetType());
serializer.Serialize(Console.Out, record);
Console.WriteLine(" ");
Console.WriteLine(" ");
以上产生以下输出:
【讨论】:
变化自:var serializer = new XmlSerializer(typeof(record));到: var serializer = new XmlSerializer(record.GetType());是消除错误的更改。以上是关于用 XmlInclude 修饰的基类在序列化时仍然会抛出类型未知的异常是不是有原因?的主要内容,如果未能解决你的问题,请参考以下文章
使用带有 boost::shared_ptr 的基类在地图中查找
WebService生成XML文档时出错。不应是类型XXXX。使用XmlInclude或SoapInclude属性静态指定非已知的类型。