由于名称空间,无法从XML反序列化

时间:2014-07-22 07:41:56

标签: xml web-services c#-4.0 namespaces xsd

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)]

2 个答案:

答案 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;

    }