Как создать SOAP-совместимый XML-ответ с правильными префиксами пространства имен? - PullRequest
0 голосов
/ 05 июля 2019

Я пишу небольшой веб-сервер (HttpListener), который в конечном итоге будет работать как часть службы Windows и отвечать на запросы SOAP из другого приложения.

Я написал код для декодирования XML-запроса SOAP и извлечения действия, его обработки и получения результата, но не могу получить корректный XML-ответ.

Мне бы хотелось избегать генерации каждого элемента по отдельности, поскольку типы ответов могут различаться, и я не хочу кодировать каждый вариант на веб-сервере и предпочел бы не углубляться в Reflection и ходить по Введите структуру для вывода значений. Я бы предпочел использовать что-то простое, например метод XmlSerializer Serialize (который, по-видимому, использует структуру Type), но неясно, имеет ли он достаточный контроль.

Вывод, который я пытаюсь создать в моей тестовой программе:

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
  <s:Body>
    <GetUsernamesResponse xmlns="http://tempuri.org/">
      <GetUsernamesResult xmlns:a="http://schemas.datacontract.org/2004/07/ConsoleApp2"
            xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
        <a:Results xmlns:b="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
          <b:string>Kermit.The.Frog</b:string>
          <b:string>Miss.Piggy</b:string>
        </a:Results>
      </GetUsernamesResult>
    </GetUsernamesResponse>
  </s:Body>
</s:Envelope>

Вывод, который я получаю:

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
  <s:Body>
    <GetUsernamesResponse xmlns:i="http://www.w3.org/2001/XMLSchema-instance" 
            xmlns="http://tempuri.org/">
      <GetUsernamesResult xmlns:a="http://schemas.datacontract.org/2004/07/ConsoleApp2" 
                xmlns:b="http://schemas.microsoft.com/2003/10/Serialization/Arrays" 
                xmlns="">
        <Results>
          <string>Kermit.The.Frog</string>
          <string>Miss.Piggy</string>
        </Results>
      </GetUsernamesResult>
    </GetUsernamesResponse>
  </s:Body>
</s:Envelope>

Это текущая тестовая программа:

using System;
using System.Collections.Generic;
using System.IO;
using System.Xml;
using System.Xml.Serialization;

namespace ConsoleApp2
{
    public class GetUsernamesResponse
    {
        public List<string> Results { get; set; }
    }

    public class GetUsernamesResult : GetUsernamesResponse {}

    public class Program
    {
        private const string ns_t = "http://tempuri.org/";
        private const string ns_s = "http://schemas.xmlsoap.org/soap/envelope/";
        private const string ns_i = "http://www.w3.org/2001/XMLSchema-instance";
        private const string ns_a = "http://schemas.datacontract.org/2004/07/ConsoleApp2";
        private const string ns_b = "http://schemas.microsoft.com/2003/10/Serialization/Arrays";

        private static void Main(string[] args)
        {
            var r = new GetUsernamesResult()
            {
                Results = new List<string>
                {
                    "Kermit.The.Frog",
                    "Miss.Piggy"
                }
            };

            var ns = new XmlSerializerNamespaces();
            ns.Add("i", ns_i);
            ns.Add("a", ns_a);
            ns.Add("b", ns_b);

            var oSerializer = new XmlSerializer(typeof(GetUsernamesResult));
            using (var sw = new StringWriter())
            {
                var xw = XmlWriter.Create(
                    sw,
                    new XmlWriterSettings()
                    {
                        OmitXmlDeclaration = true,
                        Indent = true,
                        ConformanceLevel = ConformanceLevel.Fragment,
                        NamespaceHandling = NamespaceHandling.OmitDuplicates,
                    });
                xw.WriteStartElement("s", "Envelope", ns_s);
                xw.WriteStartElement("s", "Body", ns_s);
                xw.WriteStartElement($"GetUsernamesResponse", ns_t);
                xw.WriteAttributeString("xmlns", "i", null, ns_i);
                oSerializer.Serialize(xw, r, ns);
                xw.WriteEndElement();
                xw.WriteEndElement();
                xw.WriteEndElement();
                xw.Close();
                Console.WriteLine(sw);
            }
            Console.ReadKey();
        }
    }
}

Может ли это быть сделано с помощью Serialize или действительно должно быть сделано с помощью Reflection и, фактически, воспроизвести то, что уже делает респондент SOAP в IIS?

К вашему сведению, я также пытался настроить отображение типов ...

var mapping = new SoapReflectionImporter().ImportTypeMapping(typeof(BarcodeProductionGetUsernamesResult));
var oSerializer = new XmlSerializer(mapping);

... но результирующий XML был совершенно другим и, хотя он не выдавал ошибку, также не декодировал в вызывающем приложении; было возвращено нулевое значение

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
  <s:Body>
    <GetUsernamesResponse xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://tempuri.org/">
      <GetUsernamesResult xmlns:a="http://schemas.datacontract.org/2004/07/ConsoleApp2" xmlns:b="http://schemas.microsoft.com/2003/10/Serialization/Arrays" id="id1" xmlns="">
      <Results href="#id2" />
    </GetUsernamesResult>
    <q1:Array id="id2" xmlns:q2="http://www.w3.org/2001/XMLSchema" q1:arrayType="q2:string[2]" xmlns:q1="http://schemas.xmlsoap.org/soap/encoding/">
        <Item xmlns="">Kermit.The.Frog</Item>
        <Item xmlns="">Miss.Piggy</Item>
      </q1:Array>
    </GetUsernamesResponse>
  </s:Body>
</s:Envelope>

1 Ответ

0 голосов
/ 07 июля 2019

Мне нравится использовать xml linq.Для сложных заголовков пространства имен я просто анализирую строку, чтобы получить желаемые результаты.Смотрите код ниже:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
using System.Xml.Serialization;
using System.IO;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            MyClass myClass = new MyClass();

            XDocument doc = MySerializer<MyClass>.GetXElement(myClass);
        }
    }

    public class MySerializer<T> where T : new()
    {
        static string xml =
           "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\">" +
           "    <s:Body>" +
           "       <GetUsernamesResponse xmlns=\"http://tempuri.org/\">" +
           "          <GetUsernamesResult xmlns:a=\"http://schemas.datacontract.org/2004/07/ConsoleApp2\" xmlns:i=\"http://www.w3.org/2001/XMLSchema-instance\">" +
           "             <a:Results xmlns:b=\"http://schemas.microsoft.com/2003/10/Serialization/Arrays\">" +
           "             </a:Results>" +
           "          </GetUsernamesResult>" +
           "      </GetUsernamesResponse>" +
           "   </s:Body>" +
           "</s:Envelope>";
        public static XDocument GetXElement(T myClass)
        {


            XDocument doc = XDocument.Parse(xml);

            XElement results = doc.Descendants().Where(x => x.Name.LocalName == "Results").FirstOrDefault();
            XNamespace ns_b = results.GetNamespaceOfPrefix("b");

            StringWriter sWriter = new StringWriter();
            XmlWriter xWriter = XmlWriter.Create(sWriter);

            XmlSerializerNamespaces ns1 = new XmlSerializerNamespaces();
            ns1.Add("b", ns_b.NamespaceName);

            XmlSerializer serializer = new XmlSerializer(typeof(T), ns_b.NamespaceName);
            serializer.Serialize(xWriter, myClass, ns1);
            results.Add(XElement.Parse(sWriter.ToString()));

            return doc;
        }

    }
    public class MyClass
    {
        public string test { get; set; }
    }

}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...