SCENARIO
我正在我的公司平台和一组合作伙伴Web服务之间实现一个接口层。这些服务采用XML格式,但SOAP不通过https公开。
每个服务都有一个XSD文件,我创建了相关的类。
合作伙伴拥有一个生产环境和一个sanbox环境。 XSD文件中的命名空间独占于生产环境。 所以所有使用xsd工具创建的类和XSD文件都有引用生产环境的属性:
[System.Xml.Serialization.XmlTypeAttribute(Namespace = "http://url/to/production/environment/getnewsalesresult")]
问题
现在我正在测试沙箱环境中的业务逻辑。 Web服务XML响应引用沙箱命名空间:
xmlns="http://url/to/sandbox/environment/getnewsalesresult"
最终,当将响应反序列化到相应的类时,我收到此错误:
There is an error in XML document (5, 2). ---> System.InvalidOperationException: <getnewsalesresult xmlns='http://url/to/sandbox/environment/getnewsalesresult'> was not expected.
因为目标类不知道沙箱命名空间!!
PROOF
我已经基于沙箱响应创建了一个假的XML响应,并用生产命名空间引用替换了沙箱命名空间引用。我成功地将此响应反序列化到相应的类中。
问题
是否可以在XmlTypeAttribute属性中以编程方式设置命名空间的值:
[System.Xml.Serialization.XmlTypeAttribute(Namespace = myProgrammaticallySetNamespaceValue)]
答案 0 :(得分:0)
解决问题的一种方法&#34;这个问题是制作XSD文件的副本,并为您需要与之交谈的每个环境编辑它们。然后从每个XSD生成一组类,并将它们放在单独的程序集中(或者,至少在单个程序集中的单独名称空间内)。
然后,在运行时,您可以根据当前环境选择要实际使用的类集。您可能会受益于使用partial
类来引入每个实现派生的单个接口(或基类),以便其余代码可以一致地使用它们。
当然,更安全的解决方法是让您的合作伙伴修复他们的XML生成。从逻辑上讲,如果两个XML代表相同的&#34;类型&#34;对象,然后它们应该由具有单个命名空间的单个模式描述。应用于XML的名称空间应该不取决于检索XML的当前URL(实际上,通常,名称空间根本不需要是URL,而只需要URI)。但这是一个政治的&#34;此时可能无法修复的问题。
答案 1 :(得分:0)
最终,我决定以编程方式将对http响应中的沙盒的引用替换为对生产环境的引用。
我不太喜欢这个解决方案,但实际上我必须部署项目:
/*
* This method is generic so I can perform request to different webservices
* The HttpRequestConfiguration and the HttpRequestManager are two classes we use to wrap low level http request/response process.
*/
internal T PerformHttpRequest<T>(HttpRequestConfiguration httpConf, bool overrideNS = false, string oldNS = null, string newNS = null)
{
T result = default(T);
try
{
string xmlContent = HttpRequestManager.PerformHttpRequest<string>(httpConf);
/*
* override the namespace, replacing the oldNS with the newNS
* not the best solution
*/
if (overrideNS && !string.IsNullOrEmpty(oldNS) && !string.IsNullOrEmpty(newNS))
xmlContent = xmlContent.Replace(oldNS, newNS);
/*
* deserialize the content into a T object
*/
var serializer = new XmlSerializer(typeof(T));
using (TextReader reader = new StringReader(xmlContent))
{
result = (T)serializer.Deserialize(reader);
}
}
catch (Exception ex)
{
/* manage the exception */
}
return result;
}