我正在尝试使用DataContractSerializer对一小段XML进行反序列化:
<iopbase:SoapFaultException xmlns:iopbase=""http://example.com/foo/"">
<faultcode></faultcode>
<faultstring>Hello World</faultstring>
</iopbase:SoapFaultException>
问题是DataContractSerializer没有“看到”<faultcode>
和<faultstring>
元素,我得到以下例外:
SerializationException: 'EndElement' 'SoapFaultException' from namespace
'http://example.com/foo/' is not expected. Expecting element 'faultcode'.
这是一个用于复制问题的精简代码:
[DataContract(Namespace = "http://example.com/foo/")]
public class SoapFaultException
{
[DataMember(IsRequired = true)]
public string faultcode { get; set; }
[DataMember(IsRequired = true)]
public string faultstring { get; set; }
}
var xml = System.Xml.Linq.XElement.Parse(@"
<iopbase:SoapFaultException xmlns:iopbase=""http://example.com/foo/"">
<faultcode></faultcode>
<faultstring>Hello World</faultstring>
</iopbase:SoapFaultException>");
var serializer = new DataContractSerializer(typeof(SoapFaultException));
var e = (SoapFaultException) serializer.ReadObject(xml.CreateReader()); <- Exception Here
最后,堆叠异常的痕迹。
at System.Runtime.Serialization.XmlObjectSerializerReadContext.ThrowRequiredMemberMissingException(XmlReaderDelegator xmlReader, Int32 memberIndex, Int32 requiredIndex, XmlDictionaryString[] memberNames)
at ReadSoapFaultExceptionFromXml(XmlReaderDelegator , XmlObjectSerializerReadContext , XmlDictionaryString[] , XmlDictionaryString[] )
at System.Runtime.Serialization.ClassDataContract.ReadXmlValue(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext context)
at System.Runtime.Serialization.XmlObjectSerializerReadContext.ReadDataContractValue(DataContract dataContract, XmlReaderDelegator reader)
at System.Runtime.Serialization.XmlObjectSerializerReadContext.InternalDeserialize(XmlReaderDelegator reader, String name, String ns, Type declaredType, DataContract& dataContract)
at System.Runtime.Serialization.XmlObjectSerializerReadContext.InternalDeserialize(XmlReaderDelegator xmlReader, Type declaredType, DataContract dataContract, String name, String ns)
at System.Runtime.Serialization.DataContractSerializer.InternalReadObject(XmlReaderDelegator xmlReader, Boolean verifyObjectName, DataContractResolver dataContractResolver)
at System.Runtime.Serialization.XmlObjectSerializer.ReadObjectHandleExceptions(XmlReaderDelegator reader, Boolean verifyObjectName, DataContractResolver dataContractResolver)
at System.Runtime.Serialization.DataContractSerializer.ReadObject(XmlReader reader)
at XXX.Program.Main() in C:\Git\XXX\Program.cs:line 161
at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args)
at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Threading.ThreadHelper.ThreadStart()
答案 0 :(得分:1)
您看到异常的原因是元素<faultcode>
和<faultstring>
的名称空间与数据协定不一致。
一方面,在您的XML中,根元素位于xmlns:iopbase="http://example.com/foo/"
命名空间中,但其子元素不在任何命名空间中,缺少xmlns:
前缀。另一方面,通过将[DataContract(Namespace = "http://example.com/foo/")]
添加到SoapFaultException
,您指定SoapFaultException
根元素及其子元素将出现在指定的命名空间中。由于XML元素不在此命名空间中,因此它们不会成功反序列化。
要确认这一点,如果我序列化您的类型实例,设置名称空间前缀xmlns:iopbase="http://example.com/foo/"
,我会得到:
<iopbase:SoapFaultException xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://example.com/foo/" xmlns:iopbase="http://example.com/foo/">
<iopbase:faultcode></iopbase:faultcode>
<iopbase:faultstring>Hello World</iopbase:faultstring>
</iopbase:SoapFaultException>
使用代码
var test = new SoapFaultException { faultcode = "", faultstring = "Hello World" };
var serializer = new DataContractSerializer(typeof(SoapFaultException));
var doc = new XDocument();
using (var writer = doc.CreateWriter())
{
serializer.WriteObject(writer, test);
}
// Add the iopbase prefix for clarity
doc.Root.Add(new XAttribute(XNamespace.Xmlns + "iopbase", "http://example.com/foo/"));
Debug.WriteLine(doc);
要解决此问题,理想情况下,您希望能够独立于整个数据协定命名空间指定数据成员命名空间,但遗憾的是数据协定序列化程序doesn't support that。相反,您有以下选择:
您可以将SoapFaultException
拆分为类层次结构,并为层次结构中的每个级别指定不同的命名空间,将最派生的类型放在根元素的命名空间中,如下所示:
[DataContract(Namespace = "")]
public abstract class SoapFaultExceptionBase
{
[DataMember(IsRequired = true)]
public string faultcode { get; set; }
[DataMember(IsRequired = true)]
public string faultstring { get; set; }
}
[DataContract(Namespace = "http://example.com/foo/")]
public class SoapFaultException : SoapFaultExceptionBase
{
}
您可以将SoapFaultException
保留在空名称空间中,将construct your DataContractSerializer
保留为提供的XML根元素名称和名称空间:
[DataContract(Namespace = "")]
public class SoapFaultException
{
[DataMember(IsRequired = true)]
public string faultcode { get; set; }
[DataMember(IsRequired = true)]
public string faultstring { get; set; }
}
然后做:
var serializer = new DataContractSerializer(typeof(SoapFaultException), "SoapFaultException", "http://example.com/foo/");
var e = (SoapFaultException)serializer.ReadObject(xml.CreateReader());