将 Web 服务调用的结果反序列化到子元素类型

Posted

技术标签:

【中文标题】将 Web 服务调用的结果反序列化到子元素类型【英文标题】:Deserializing result from Web Service call to a child element type 【发布时间】:2021-04-10 03:21:00 【问题描述】:

我已连接到 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;

现在看到错误:

第 1 行位置 73 出错。需要来自命名空间“namespace1”的元素“MyData”。 遇到名为“ServiceResponse”、命名空间“namespace1”的“元素”。

【问题讨论】:

目前还不清楚(至少对我而言)您到底在寻找什么。是否只想反序列化 ServiceResult 节点? 也请与我们分享MyData类是如何声明和xml注解的。 @PeterCsala 是的,我只需要 ServiceResult 部分。 MyData 类由服务引用提供,不是我声明的。 注解在这里非常重要。 ServiceResponse 上面有 XmlRootAttribute 吗? ServiceResponseServiceResult 属性是否有 XmlElementAttribute?如果没有映射,我们将无法为您提供帮助。 @PeterCsala感谢您的意见,但我不太明白您在问什么。我已将服务参考中的 MyData 定义信息添加到上面的原始问题中。 ServiceResult 和 ServiceResponse 是 XML 标签。我对它们的了解都在问题中发布的 XML 中。我可以从成员那里得知 ServiceResult 与 MyData 的数据类型相同,这就是我尝试将其反序列化为该类型的原因。希望对您有所帮助。 【参考方案1】:

假设您的 xml 文件如下所示:

<?xml version="1.0" encoding="utf-8" ?>
<ServiceResponse xmlns="namespace1">
  <ServiceResult xmlns:i="namespace2">
    <desc>1</desc>
  </ServiceResult>
</ServiceResponse>

我刚刚在您的示例中添加了 xmldesc

然后为了跳过***实体,我们需要对 xml 进行半解析。一种方法:

    LoadFileStream 转换为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 类上定义的。

secondLevelEntity 是一个XElement 实例。 XElement 继承自 XContainerXContainer 继承自 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; 

【讨论】:

以上是关于将 Web 服务调用的结果反序列化到子元素类型的主要内容,如果未能解决你的问题,请参考以下文章

Json.net 从 HTTPClient 结果反序列化 DateTime

通过 Web 传输实体框架对象并通过 JSON 返回的最佳方式

Web Service 或 WCF调用时读取 XML 数据时,超出最大字符串内容长度配额(8192)解决方法

在 C# 中反序列化 XML 元素的存在以布尔

RPC与消息队列

将 Web 服务 API 中的 XML 字符串反序列化为 C# 对象