将 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;
}

代码执行无一例外,但所有底层元素均为空。我知道这是因为 ServiceResponseServiceResult 不同,但我不知道如何指定我希望反序列化发生在子元素上,而不是整个对象(没有其他元素,只是 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。一种方法:

  1. LoadFileStream改成了XDocument
  2. 找到顶级实体
  3. 找到二级实体
//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 实例,希望 DataContractSerializerReadObject 有一个重载接受 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; }
}