【发布时间】:2019-07-05 17:04:35
【问题描述】:
我正在编写一个小型 Web 服务器 (HttpListener),它最终将作为 Windows 服务的一部分运行并响应来自另一个应用程序的 SOAP 请求。
我已经编写了代码来解码 SOAP 请求 XML 并提取操作,对其进行处理并获得结果,但不能完全正确生成响应 XML。
我想避免单独生成每个元素,因为响应的类型可能会有所不同,并且我不想将每个变体都编码到 Web 服务器中,并且不想深入研究反射和遍历输入结构以输出值。我更喜欢使用像 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 来完成,并且有效地重现 IIS 中的 SOAP 响应程序已经在做的事情?
仅供参考,我也尝试设置类型映射...
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>
【问题讨论】:
-
您没有使用 ns_a 或 ns_b 编写任何元素,这就是为什么输出都不包含 a: 或 b:。
-
我意识到这一点并且得出的结论是,除非对象/类已经用 XmlElement 属性进行了修饰,否则唯一的解决方案是使用反射实际遍历结构并显式生成元素。似乎没有自动机制来识别数组/列表/集合等。