将 Web 服务调用的结果反序列化为子元素类型
Deserializing result from Web Service call to a child element type
我已连接到 Web 服务并正在下载 XML,我正在尝试反序列化。但是,XML 具有与我尝试序列化到的子元素不同类型的顶级元素。
<ServiceResponse xmlns="namespace1">
<ServiceResult xmlns:i="namespace2">
...lots of elements
</ServiceResult>
</ServiceResponse>
我写了下面的代码:
public MyData ConvertXmlToMyData(string filepath)
{
XmlRootAttribute xRoot = new XmlRootAttribute();
xRoot.ElementName = "ServiceResponse";
xRoot.Namespace = @"namespace1";
xRoot.IsNullable = true;
XmlSerializer reader = new XmlSerializer(typeof(MyData), xRoot);
StreamReader file = new StreamReader(filepath);
MyData result = (MyData)reader.Deserialize(file);
file.Close();
return result;
}
代码执行无一例外,但所有底层元素均为空。我知道这是因为 ServiceResponse
与 ServiceResult
不同,但我不知道如何指定我希望反序列化发生在子元素上,而不是整个对象(没有其他元素,只是 ServiceResult
).
类型的单个子元素
我看到的唯一解决方案是我应该编辑类型的声明,但在我的情况下,我是从 Web 服务获取它们的,所以我不能这样做。
有谁知道我应该怎么做?
编辑:添加了服务参考中 MyData 注释的详细信息
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Runtime.Serialization", "4.0.0.0")]
[System.Runtime.Serialization.DataContractAttribute(Name="MyData", Namespace="namespace2")]
[System.SerializableAttribute()]
public partial class MyData: object, System.Runtime.Serialization.IExtensibleDataObject, System.ComponentModel.INotifyPropertyChanged {
编辑 2:
将代码更改为:
public MyData ConvertXmlToMyData(string filepath)
{
DataContractSerializer reader = new DataContractSerializer(typeof(MyData));
StreamReader file = new StreamReader(filepath);
file.BaseStream.Position = 0;
MyData result = (MyData)reader.ReadObject(file.BaseStream); <- error thrown here
file.Close();
return result;
}
现在看到错误:
Error in line 1 position 73. Expecting element 'MyData' from namespace 'namespace1'..
Encountered 'Element' with name 'ServiceResponse', namespace 'namespace1'.
假设您的 xml 文件如下所示:
<?xml version="1.0" encoding="utf-8" ?>
<ServiceResponse xmlns="namespace1">
<ServiceResult xmlns:i="namespace2">
<desc>1</desc>
</ServiceResult>
</ServiceResponse>
我刚刚在您的示例中添加了一个 xml
和一个 desc
。
然后为了跳过顶级实体,我们需要半解析 xml。一种方法:
Load
把FileStream
改成了XDocument
- 找到顶级实体
- 找到二级实体
//Step #1
var xml = File.OpenRead("sample.xml");
var semiParsedData = XDocument.Load(xml);
//Step #2
const string topNamespace = "namespace1", firstNode = "ServiceResponse";
const StringComparison comparison = StringComparison.InvariantCultureIgnoreCase;
var topLevelEntity = semiParsedData.Descendants().FirstOrDefault(element =>
string.Equals(element.Name.Namespace.NamespaceName, topNamespace, comparison)
&& string.Equals(element.Name.LocalName, firstNode, comparison));
//Step #3
const string secondNode = "ServiceResult";
var secondLevelEntity = topLevelEntity?.Descendants().FirstOrDefault(element =>
string.Equals(element.Name.Namespace.NamespaceName, topNamespace, comparison)
&& string.Equals(element.Name.LocalName, secondNode, comparison));
if (secondLevelEntity == null)
return;
希望有一个叫做CreateReader
的方法,它定义在XNode
class.
secondLevelEntity
是一个 XElement
实例。
XElement
继承自 XContainer
.
XContainer
继承自 XNode
.
因此,我们可以在 secondLevelEntity
:
上调用 CreateReader
using var resultReader = secondLevelEntity.CreateReader();
resultReader
是一个 XmlReader
实例,希望 DataContractSerializer
的 ReadObject
有一个重载接受 XmlReader
:
var deserializer = new DataContractSerializer(typeof(MyData));
var myData = (MyData)deserializer.ReadObject(resultReader);
Console.WriteLine(myData.Description);
最后数据模型应该是这样的:
[DataContract(Name = "ServiceResult", Namespace = "namespace1")]
public class MyData
{
[DataMember(Name = "desc")]
public string Description { get; set; }
}
我已连接到 Web 服务并正在下载 XML,我正在尝试反序列化。但是,XML 具有与我尝试序列化到的子元素不同类型的顶级元素。
<ServiceResponse xmlns="namespace1">
<ServiceResult xmlns:i="namespace2">
...lots of elements
</ServiceResult>
</ServiceResponse>
我写了下面的代码:
public MyData ConvertXmlToMyData(string filepath)
{
XmlRootAttribute xRoot = new XmlRootAttribute();
xRoot.ElementName = "ServiceResponse";
xRoot.Namespace = @"namespace1";
xRoot.IsNullable = true;
XmlSerializer reader = new XmlSerializer(typeof(MyData), xRoot);
StreamReader file = new StreamReader(filepath);
MyData result = (MyData)reader.Deserialize(file);
file.Close();
return result;
}
代码执行无一例外,但所有底层元素均为空。我知道这是因为 ServiceResponse
与 ServiceResult
不同,但我不知道如何指定我希望反序列化发生在子元素上,而不是整个对象(没有其他元素,只是 ServiceResult
).
我看到的唯一解决方案是我应该编辑类型的声明,但在我的情况下,我是从 Web 服务获取它们的,所以我不能这样做。
有谁知道我应该怎么做?
编辑:添加了服务参考中 MyData 注释的详细信息
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Runtime.Serialization", "4.0.0.0")]
[System.Runtime.Serialization.DataContractAttribute(Name="MyData", Namespace="namespace2")]
[System.SerializableAttribute()]
public partial class MyData: object, System.Runtime.Serialization.IExtensibleDataObject, System.ComponentModel.INotifyPropertyChanged {
编辑 2:
将代码更改为:
public MyData ConvertXmlToMyData(string filepath)
{
DataContractSerializer reader = new DataContractSerializer(typeof(MyData));
StreamReader file = new StreamReader(filepath);
file.BaseStream.Position = 0;
MyData result = (MyData)reader.ReadObject(file.BaseStream); <- error thrown here
file.Close();
return result;
}
现在看到错误:
Error in line 1 position 73. Expecting element 'MyData' from namespace 'namespace1'.. Encountered 'Element' with name 'ServiceResponse', namespace 'namespace1'.
假设您的 xml 文件如下所示:
<?xml version="1.0" encoding="utf-8" ?>
<ServiceResponse xmlns="namespace1">
<ServiceResult xmlns:i="namespace2">
<desc>1</desc>
</ServiceResult>
</ServiceResponse>
我刚刚在您的示例中添加了一个 xml
和一个 desc
。
然后为了跳过顶级实体,我们需要半解析 xml。一种方法:
Load
把FileStream
改成了XDocument
- 找到顶级实体
- 找到二级实体
//Step #1
var xml = File.OpenRead("sample.xml");
var semiParsedData = XDocument.Load(xml);
//Step #2
const string topNamespace = "namespace1", firstNode = "ServiceResponse";
const StringComparison comparison = StringComparison.InvariantCultureIgnoreCase;
var topLevelEntity = semiParsedData.Descendants().FirstOrDefault(element =>
string.Equals(element.Name.Namespace.NamespaceName, topNamespace, comparison)
&& string.Equals(element.Name.LocalName, firstNode, comparison));
//Step #3
const string secondNode = "ServiceResult";
var secondLevelEntity = topLevelEntity?.Descendants().FirstOrDefault(element =>
string.Equals(element.Name.Namespace.NamespaceName, topNamespace, comparison)
&& string.Equals(element.Name.LocalName, secondNode, comparison));
if (secondLevelEntity == null)
return;
希望有一个叫做CreateReader
的方法,它定义在XNode
class.
secondLevelEntity
是一个XElement
实例。XElement
继承自XContainer
.XContainer
继承自XNode
.
因此,我们可以在 secondLevelEntity
:
CreateReader
using var resultReader = secondLevelEntity.CreateReader();
resultReader
是一个 XmlReader
实例,希望 DataContractSerializer
的 ReadObject
有一个重载接受 XmlReader
:
var deserializer = new DataContractSerializer(typeof(MyData));
var myData = (MyData)deserializer.ReadObject(resultReader);
Console.WriteLine(myData.Description);
最后数据模型应该是这样的:
[DataContract(Name = "ServiceResult", Namespace = "namespace1")]
public class MyData
{
[DataMember(Name = "desc")]
public string Description { get; set; }
}