将 Soap 调用中的 XML 反序列化为对象数组
Deserializing XML from Soap call into Array of Objects
我正在尝试进行 soap 调用并反序列化我收到的答案。 soap 调用是正确的,但到目前为止我还没有设法将答案反序列化为一个对象。具体来说,答案中包含的XML是一个重复数据的数组。正如您所猜到的,我想从中创建一个对象数组。我检查了其他类似的问题,但到目前为止 none 有效。让我们从定义整个 XML.
开始
<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:a="http://www.w3.org/2005/08/addressing">
<s:Header>
<a:Action s:mustUnderstand="1">ThisIsATry/RetrieveResponse</a:Action>
</s:Header>
<s:Body>
<RetrieveResponse xmlns="ThisIsATry">
<RetrieveInsurersResult xmlns:b="http://schemas.datacontract.org/2004/07/ThisIsATry" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<b:Errors xmlns:c="http://schemas.microsoft.com/2003/10/Serialization/Arrays" />
<b:Message>Selected 2 records</b:Message>
<b:Results xmlns:c="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
<c:ArrayOfKeyValueOfstringstring>
<c:KeyValueOfstringstring>
<c:Key>PersonId</c:Key>
<c:Value>1</c:Value>
</c:KeyValueOfstringstring>
<c:KeyValueOfstringstring>
<c:Key>Name</c:Key>
<c:Value>Mike</c:Value>
</c:KeyValueOfstringstring>
</c:ArrayOfKeyValueOfstringstring>
<c:ArrayOfKeyValueOfstringstring>
<c:KeyValueOfstringstring>
<c:Key>PersonId</c:Key>
<c:Value>2</c:Value>
</c:KeyValueOfstringstring>
<c:KeyValueOfstringstring>
<c:Key>Name</c:Key>
<c:Value>Henry</c:Value>
</c:KeyValueOfstringstring>
</c:ArrayOfKeyValueOfstringstring>
</b:Results>
<b:Warnings xmlns:c="http://schemas.microsoft.com/2003/10/Serialization/Arrays" />
</RetrieveInsurersResult>
</RetrieveResponse>
</s:Body>
</s:Envelope>
我需要用到的部分是:
<b:Results xmlns:c="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
<c:ArrayOfKeyValueOfstringstring>
<c:KeyValueOfstringstring>
<c:Key>PersonId</c:Key>
<c:Value>1</c:Value>
</c:KeyValueOfstringstring>
<c:KeyValueOfstringstring>
<c:Key>Name</c:Key>
<c:Value>Mike</c:Value>
</c:KeyValueOfstringstring>
</c:ArrayOfKeyValueOfstringstring>
<c:ArrayOfKeyValueOfstringstring>
<c:KeyValueOfstringstring>
<c:Key>PersonId</c:Key>
<c:Value>2</c:Value>
</c:KeyValueOfstringstring>
<c:KeyValueOfstringstring>
<c:Key>Name</c:Key>
<c:Value>Henry</c:Value>
</c:KeyValueOfstringstring>
</c:ArrayOfKeyValueOfstringstring>
</b:Results>
如您所见,有 2 个 "objects" 类型 c:ArrayOfKeyValueOfstringstring>。每个对象包含 2 个 c:KeyValueOfstringstring 类型的属性。最后,这些属性中的每一个都包含一个 key 和一个 value 。我需要的是 c:ArrayOfKeyValueOfstringstring 数组,其中包含 c:KeyValueOfstringstring 数组和相关信息。我试图用以下 类:
在我的 c# 代码中表示此数据
public class ArrayOfKeyValueOfstringstring
{
[XmlElement("ArrayOfKeyValueOfstringstring")]
public KeyValueOfstringstring[] Value { get; set; }
}
public class KeyValueOfstringstring
{
[XmlElement("KeyValueOfstringstring")]
public KeyValue Pair { get; set; }
}
public class KeyValue
{
[XmlElement("Key")]
public string Key { get; set; }
[XmlElement("Value")]
public string Value { get; set; }
}
目前我处理回复的方式是:
var result = client.UploadString(dataBaseConnectionString, soapTemplate); //calls the serive and brings back the XML
var document = XDocument.Parse(result); //gives back the first XML I posted in this question
var results = document.Descendants().FirstOrDefault(x => x.Name.LocalName == "Results"); //gives back the 2nd XML I posted
xmlNamespaceManager.AddNamespace("c", "http://schemas.microsoft.com/2003/10/Serialization/Arrays");
var oXmlSerializer = new XmlSerializer(typeof(SoapResponse[]));
//this part is wrong..
using (var mem = new MemoryStream(Encoding.UTF8.GetBytes(results.ToString())))
{
var responseObj = (ArrayOfKeyValueOfstringstring)oXmlSerializer.Deserialize(mem);
}
在此先感谢您的帮助!
碰巧,ArrayOfKeyValueOfstringstring
是 DataContractSerializer
在序列化 Dictionary<string, string>
时选择的元素名称,因此您将更容易使用该序列化器来反序列化你的 XML.
首先介绍一下扩展方法:
public static partial class DataContractSerializerExtensions
{
public static T ToContractObject<T>(this XContainer doc, DataContractSerializer serializer = null)
{
if (doc == null)
throw new ArgumentNullException();
using (var reader = doc.CreateReader())
{
return (T)(serializer ?? new DataContractSerializer(typeof(T))).ReadObject(reader);
}
}
}
现在您可以将 XML 解析为 List<Dictionary<string, string>>
,如下所示:
var dictionaries = document.Descendants()
.Where(d => d.Name.LocalName == "ArrayOfKeyValueOfstringstring")
.Select(d => d.ToContractObject<Dictionary<string, string>>())
.ToList();
稍后您可以将词典列表映射到您的首选模型。
但是,如果出于某种原因必须使用XmlSerializer
,请改为引入以下扩展方法和数据模型:
public static partial class XmlSerializerExtensions
{
public static T ToObject<T>(this XContainer doc, XmlSerializer serializer = null)
{
if (doc == null)
throw new ArgumentNullException();
using (var reader = doc.CreateReader())
{
return (T)(serializer ?? new XmlSerializer(typeof(T))).Deserialize(reader);
}
}
}
[XmlRoot(ElementName = "KeyValueOfstringstring", Namespace = "http://schemas.microsoft.com/2003/10/Serialization/Arrays")]
public class KeyValueOfstringstring
{
[XmlElement(ElementName = "Key", Namespace = "http://schemas.microsoft.com/2003/10/Serialization/Arrays")]
public string Key { get; set; }
[XmlElement(ElementName = "Value", Namespace = "http://schemas.microsoft.com/2003/10/Serialization/Arrays")]
public string Value { get; set; }
}
[XmlRoot(ElementName = "ArrayOfKeyValueOfstringstring", Namespace = "http://schemas.microsoft.com/2003/10/Serialization/Arrays")]
public class ArrayOfKeyValueOfstringstring
{
[XmlElement(ElementName = "KeyValueOfstringstring", Namespace = "http://schemas.microsoft.com/2003/10/Serialization/Arrays")]
public List<KeyValueOfstringstring> KeyValueOfstringstring { get; set; }
}
并反序列化如下:
var results = document.Descendants()
.Where(d => d.Name.LocalName == "ArrayOfKeyValueOfstringstring")
.Select(d => d.ToObject<ArrayOfKeyValueOfstringstring>())
.ToList();
备注:
我使用 XNode.CreateReader()
到 return 一个 XmlReader
,从中可以直接反序列化一个 XNode
。这避免了将选定节点转换回字符串表示然后 re-parse.
的要求
<b:Results>
节点的名称空间是 "http://schemas.datacontract.org/2004/07/ThisIsATry"
。这个命名空间感觉......临时......所以我避免在我的回答中使用它。
综上所述,因为 XML 看起来可能是由 wcf service, is there any chance the service provides WSDL Metadata 生成的?如果是这样,您可以自动生成客户端。参见
- Generating a WCF Client from Service Metadata
- How to: Create a Windows Communication Foundation Client
- How to: Add, Update, or Remove a Service Reference.
有关如何完成此操作的文档。
尝试 xml linq
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication1
{
class Program
{
const string FILENAME = @"c:\temp\test.xml";
static void Main(string[] args)
{
XDocument doc = XDocument.Load(FILENAME);
XElement results = doc.Descendants().Where(x => x.Name.LocalName == "Results").FirstOrDefault();
XNamespace nsC = results.GetNamespaceOfPrefix("c");
Dictionary<string, List<string>> dict = results.Descendants(nsC + "KeyValueOfstringstring")
.GroupBy(x => (string)x.Element(nsC + "Key"), y => (string)y.Element(nsC + "Value"))
.ToDictionary(x => x.Key, y => y.ToList());
}
}
}
我正在尝试进行 soap 调用并反序列化我收到的答案。 soap 调用是正确的,但到目前为止我还没有设法将答案反序列化为一个对象。具体来说,答案中包含的XML是一个重复数据的数组。正如您所猜到的,我想从中创建一个对象数组。我检查了其他类似的问题,但到目前为止 none 有效。让我们从定义整个 XML.
开始<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:a="http://www.w3.org/2005/08/addressing">
<s:Header>
<a:Action s:mustUnderstand="1">ThisIsATry/RetrieveResponse</a:Action>
</s:Header>
<s:Body>
<RetrieveResponse xmlns="ThisIsATry">
<RetrieveInsurersResult xmlns:b="http://schemas.datacontract.org/2004/07/ThisIsATry" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<b:Errors xmlns:c="http://schemas.microsoft.com/2003/10/Serialization/Arrays" />
<b:Message>Selected 2 records</b:Message>
<b:Results xmlns:c="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
<c:ArrayOfKeyValueOfstringstring>
<c:KeyValueOfstringstring>
<c:Key>PersonId</c:Key>
<c:Value>1</c:Value>
</c:KeyValueOfstringstring>
<c:KeyValueOfstringstring>
<c:Key>Name</c:Key>
<c:Value>Mike</c:Value>
</c:KeyValueOfstringstring>
</c:ArrayOfKeyValueOfstringstring>
<c:ArrayOfKeyValueOfstringstring>
<c:KeyValueOfstringstring>
<c:Key>PersonId</c:Key>
<c:Value>2</c:Value>
</c:KeyValueOfstringstring>
<c:KeyValueOfstringstring>
<c:Key>Name</c:Key>
<c:Value>Henry</c:Value>
</c:KeyValueOfstringstring>
</c:ArrayOfKeyValueOfstringstring>
</b:Results>
<b:Warnings xmlns:c="http://schemas.microsoft.com/2003/10/Serialization/Arrays" />
</RetrieveInsurersResult>
</RetrieveResponse>
</s:Body>
</s:Envelope>
我需要用到的部分是:
<b:Results xmlns:c="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
<c:ArrayOfKeyValueOfstringstring>
<c:KeyValueOfstringstring>
<c:Key>PersonId</c:Key>
<c:Value>1</c:Value>
</c:KeyValueOfstringstring>
<c:KeyValueOfstringstring>
<c:Key>Name</c:Key>
<c:Value>Mike</c:Value>
</c:KeyValueOfstringstring>
</c:ArrayOfKeyValueOfstringstring>
<c:ArrayOfKeyValueOfstringstring>
<c:KeyValueOfstringstring>
<c:Key>PersonId</c:Key>
<c:Value>2</c:Value>
</c:KeyValueOfstringstring>
<c:KeyValueOfstringstring>
<c:Key>Name</c:Key>
<c:Value>Henry</c:Value>
</c:KeyValueOfstringstring>
</c:ArrayOfKeyValueOfstringstring>
</b:Results>
如您所见,有 2 个 "objects" 类型 c:ArrayOfKeyValueOfstringstring>。每个对象包含 2 个 c:KeyValueOfstringstring 类型的属性。最后,这些属性中的每一个都包含一个 key 和一个 value 。我需要的是 c:ArrayOfKeyValueOfstringstring 数组,其中包含 c:KeyValueOfstringstring 数组和相关信息。我试图用以下 类:
在我的 c# 代码中表示此数据public class ArrayOfKeyValueOfstringstring
{
[XmlElement("ArrayOfKeyValueOfstringstring")]
public KeyValueOfstringstring[] Value { get; set; }
}
public class KeyValueOfstringstring
{
[XmlElement("KeyValueOfstringstring")]
public KeyValue Pair { get; set; }
}
public class KeyValue
{
[XmlElement("Key")]
public string Key { get; set; }
[XmlElement("Value")]
public string Value { get; set; }
}
目前我处理回复的方式是:
var result = client.UploadString(dataBaseConnectionString, soapTemplate); //calls the serive and brings back the XML
var document = XDocument.Parse(result); //gives back the first XML I posted in this question
var results = document.Descendants().FirstOrDefault(x => x.Name.LocalName == "Results"); //gives back the 2nd XML I posted
xmlNamespaceManager.AddNamespace("c", "http://schemas.microsoft.com/2003/10/Serialization/Arrays");
var oXmlSerializer = new XmlSerializer(typeof(SoapResponse[]));
//this part is wrong..
using (var mem = new MemoryStream(Encoding.UTF8.GetBytes(results.ToString())))
{
var responseObj = (ArrayOfKeyValueOfstringstring)oXmlSerializer.Deserialize(mem);
}
在此先感谢您的帮助!
碰巧,ArrayOfKeyValueOfstringstring
是 DataContractSerializer
在序列化 Dictionary<string, string>
时选择的元素名称,因此您将更容易使用该序列化器来反序列化你的 XML.
首先介绍一下扩展方法:
public static partial class DataContractSerializerExtensions
{
public static T ToContractObject<T>(this XContainer doc, DataContractSerializer serializer = null)
{
if (doc == null)
throw new ArgumentNullException();
using (var reader = doc.CreateReader())
{
return (T)(serializer ?? new DataContractSerializer(typeof(T))).ReadObject(reader);
}
}
}
现在您可以将 XML 解析为 List<Dictionary<string, string>>
,如下所示:
var dictionaries = document.Descendants()
.Where(d => d.Name.LocalName == "ArrayOfKeyValueOfstringstring")
.Select(d => d.ToContractObject<Dictionary<string, string>>())
.ToList();
稍后您可以将词典列表映射到您的首选模型。
但是,如果出于某种原因必须使用XmlSerializer
,请改为引入以下扩展方法和数据模型:
public static partial class XmlSerializerExtensions
{
public static T ToObject<T>(this XContainer doc, XmlSerializer serializer = null)
{
if (doc == null)
throw new ArgumentNullException();
using (var reader = doc.CreateReader())
{
return (T)(serializer ?? new XmlSerializer(typeof(T))).Deserialize(reader);
}
}
}
[XmlRoot(ElementName = "KeyValueOfstringstring", Namespace = "http://schemas.microsoft.com/2003/10/Serialization/Arrays")]
public class KeyValueOfstringstring
{
[XmlElement(ElementName = "Key", Namespace = "http://schemas.microsoft.com/2003/10/Serialization/Arrays")]
public string Key { get; set; }
[XmlElement(ElementName = "Value", Namespace = "http://schemas.microsoft.com/2003/10/Serialization/Arrays")]
public string Value { get; set; }
}
[XmlRoot(ElementName = "ArrayOfKeyValueOfstringstring", Namespace = "http://schemas.microsoft.com/2003/10/Serialization/Arrays")]
public class ArrayOfKeyValueOfstringstring
{
[XmlElement(ElementName = "KeyValueOfstringstring", Namespace = "http://schemas.microsoft.com/2003/10/Serialization/Arrays")]
public List<KeyValueOfstringstring> KeyValueOfstringstring { get; set; }
}
并反序列化如下:
var results = document.Descendants()
.Where(d => d.Name.LocalName == "ArrayOfKeyValueOfstringstring")
.Select(d => d.ToObject<ArrayOfKeyValueOfstringstring>())
.ToList();
备注:
我使用
XNode.CreateReader()
到 return 一个XmlReader
,从中可以直接反序列化一个XNode
。这避免了将选定节点转换回字符串表示然后 re-parse. 的要求
<b:Results>
节点的名称空间是"http://schemas.datacontract.org/2004/07/ThisIsATry"
。这个命名空间感觉......临时......所以我避免在我的回答中使用它。
综上所述,因为 XML 看起来可能是由 wcf service, is there any chance the service provides WSDL Metadata 生成的?如果是这样,您可以自动生成客户端。参见
- Generating a WCF Client from Service Metadata
- How to: Create a Windows Communication Foundation Client
- How to: Add, Update, or Remove a Service Reference.
有关如何完成此操作的文档。
尝试 xml linq
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication1
{
class Program
{
const string FILENAME = @"c:\temp\test.xml";
static void Main(string[] args)
{
XDocument doc = XDocument.Load(FILENAME);
XElement results = doc.Descendants().Where(x => x.Name.LocalName == "Results").FirstOrDefault();
XNamespace nsC = results.GetNamespaceOfPrefix("c");
Dictionary<string, List<string>> dict = results.Descendants(nsC + "KeyValueOfstringstring")
.GroupBy(x => (string)x.Element(nsC + "Key"), y => (string)y.Element(nsC + "Value"))
.ToDictionary(x => x.Key, y => y.ToList());
}
}
}