在WCF中为complextype中的元素设置名称空间

时间:2015-04-13 10:10:22

标签: c# xml wcf serialization xml-namespaces

我使用svcutil为外部Web服务生成了一个客户端。这是处理服务调用的代码

XmlDocument doc = new XmlDocument();
doc.LoadXml(payload.generate());

TestRequest testMessage = new TestRequest();
XmlElement[] elem = new XmlElement[1];
elem[0] = doc.DocumentElement;
testMessage.Items = elem;
TestResponse response = client.Test(Operation, Version, 0, testMessage);

svcutil生成的代码:

[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")]
[System.ServiceModel.ServiceContractAttribute(Namespace="http://provider/", ConfigurationName="Tests.Proxies.TestService")]
public interface TestService
{
    [System.ServiceModel.OperationContractAttribute(Action="http://provider/Test", ReplyAction="*")]
    [System.ServiceModel.XmlSerializerFormatAttribute()]
    [return: System.ServiceModel.MessageParameterAttribute(Name="Response")]
    Tests.Proxies.TestResponse Test(string Operation, string Version, long CheckSum, Tests.Proxies.TestRequest Request);
}

[System.CodeDom.Compiler.GeneratedCodeAttribute("svcutil", "4.0.30319.18020")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true, Namespace="http://provider/")]
public partial class TestRequest
{
    private System.Xml.XmlElement[] itemsField;

    private string[] textField;

    [System.Xml.Serialization.XmlAnyElementAttribute(Order=0)]
    public System.Xml.XmlElement[] Items
    {
        get
        {
            return this.itemsField;
        }
        set
        {
            this.itemsField = value;
        }
    }

    ...
}

有效负载由第三方库生成:

<payload>
...
</payload>

但是在调用客户端时,序列化程序会将其修改为空命名空间:

<payload xmlns="">
...
</payload>

导致测试失败,因为服务期望它在"urn:org:ns"命名空间中合格,所以我尝试使用以下方法手动设置:

doc.DocumentElement.SetAttribute("xmlns", "urn:org:ns");

但这会引发以下异常:

CommunicationException:
{"Error serializing message : 'Error generating XML document."}
{"Prefix '' is already bound to namespace '' and cannot be changed to 'urn:org:ns'.
Parameter name: prefix"}

[System.Xml.Serialization.XmlAnyElementAttribute(Namespace="urn:org:ns")]添加到Items属性不会对序列化产生任何影响。

以防万一,这是wsdl的相关部分:

<s:element name="Test">
    <s:complexType>
        <s:sequence>
            <s:element minOccurs="0" name="Operation" type="s:string"/>
            <s:element minOccurs="0" name="Version" type="s:string"/>
            <s:element minOccurs="0" name="CheckSum" type="s:long"/>
            <s:element name="TestRequest">
                <s:complexType mixed="true">
                    <s:choice maxOccurs="unbounded" minOccurs="0">
                        <s:any processContents="lax"/>
                    </s:choice>
                </s:complexType>
            </s:element>
        </s:sequence>
    </s:complexType>
</s:element>

我尝试做的甚至可能吗?

1 个答案:

答案 0 :(得分:0)

我解决了它,结果证明它与WCF本身没什么关系,但是处理XML。如this answer中所述,如果未明确分配一个节点,则该节点将始终分配给默认命名空间,并且这只能在创建时完成,因此我已编写了一个递归函数来完成此操作。这是一个快速而肮脏的解决方案,但现在可以解决问题。请注意,如果您打算使用它,它可能不适合您的用例。

XmlDocument doc = new XmlDocument();
doc.LoadXml(payload.generate());
XmlElement newRootNode = RecursiveCopy(doc.DocumentElement, doc, "urn:org:ns");

public XmlElement RecursiveCopy(XmlNode node, XmlDocument doc, string targetNamespace) 
{  
    XmlElement nodeCopy = doc.CreateElement(node.LocalName, targetNamespace);
    foreach (XmlNode child in node.ChildNodes) 
    {
        if (child.NodeType == XmlNodeType.Element)
        {
            nodeCopy.AppendChild(RecursiveCopy(child, doc, targetNamespace);
        }
        else if (child.NodeType == XmlNodeType.Text || child.NodeType == XmlNodeType.CDATA) 
        {
            XmlNode newNode = doc.CreateNode(child.NodeType, child.LocalName, targetNamespace);
            newNode.Value = child.Value;
            nodeCopy.AppendChild(newNode);
        }
    }
    return nodeCopy;
}
相关问题