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

除了上述之外,还有一个yPaymentSummaryzPaymentSummary实现完全相同。这些被添加到一个集合中,然后可以在每个上调用序列化方法:

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 的基类在地图中查找

c ++使基类在子类中使用覆盖的方法

c++ virtual总结

WebService生成XML文档时出错。不应是类型XXXX。使用XmlInclude或SoapInclude属性静态指定非已知的类型。

重写(override)与重载(overload)的区别

Gson反序列化泛型类型适配器的基类