代码之家  ›  专栏  ›  技术社区  ›  Tarta

将Soap调用中的XML反序列化到对象数组中

  •  1
  • Tarta  · 技术社区  · 8 年前

    我正在尝试进行一个soap调用,并将我收到的回复反序列化。soap调用是正确的,但到目前为止,我还无法将答案反序列化为对象。具体来说,答案中包含的XML是一组重复数据。你可以猜到,我想用它创建一个对象数组。我已经检查了其他类似的问题,但到目前为止没有一个有效。让我们从定义整个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个类型为 c: ArrayOfKeyValueOfstringstring> 。每个对象包含2个类型的属性 c: 字符串的键值 。最后,这些属性中的每一个都包含 钥匙 和a 价值 。我需要的是 c: ArrayOfKeyValue字符串 ,包含 c: 字符串的键值 以及相关信息。我试图用以下类在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);
    }
    

    提前感谢您的帮助!

    2 回复  |  直到 8 年前
        1
  •  1
  •   dbc    8 年前

    碰巧的是, 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() 返回 XmlReader 其中 XNode 可以直接反序列化。这避免了将所选节点转换回字符串表示然后重新解析的要求。

    • 的命名空间 <b:Results> 节点为 "http://schemas.datacontract.org/2004/07/ThisIsATry" 。此命名空间感觉。。。临时的所以我避免在回答中使用它。

    尽管如此,因为XML看起来可能是由 wcf service ,该服务是否有可能提供 WSDL Metadata ?如果是这样,您可以自动生成客户端。看见

    有关如何执行此操作的文档。

        2
  •  0
  •   jdweng    8 年前

    尝试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());
            }
        }
    
    }