0

I've generated a client for an external web service using svcutil. This is the code that deals with the service call

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

And the svcutil-generated code:

[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;
        }
    }

    ...
}

The payload is generated by a third party library:

<payload>
...
</payload>

But when invoking the client, the serializer modifies it to the empty namespace:

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

Causing the test to fail, since the service expects it to be qualified in the "urn:org:ns" namespace, so I've tried to set it up manually by using:

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

But this raises the following exception:

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"}

Adding [System.Xml.Serialization.XmlAnyElementAttribute(Namespace="urn:org:ns")] to the Items property doesn't have any effect on the serialization.

Just in case, this is the relevant part of the 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>

Is what I'm trying to do even possible ?

msg
  • 7,863
  • 3
  • 14
  • 33
  • Is it correct that you're unable to modify the functionality of `payload.generate()`? – Dan Field Apr 13 '15 at 13:27
  • Actually, rereading your question there's a bit missing here. Waht `items` property are you referring to? Can you post the code for it and what you've attempted? – Dan Field Apr 13 '15 at 13:30
  • Yes, I can't modify the generate function because it is from an external library. The Items property is in the `partial class TestRequest`, generated by svcutil. – msg Apr 13 '15 at 22:01

1 Answers1

0

I solved it, and turns out it doesn't have much to do with WCF itself but with XML handling. As stated in this answer, a node will always be assigned to the default namespace if not explicitly assigned one, and this can only be done upon creation, so I've written a recursive function to accomplish this. It's a quick and dirty solution, but does the trick for now. Beware if you are going to use this, it may not fit your use case.

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;
}
Community
  • 1
  • 1
msg
  • 7,863
  • 3
  • 14
  • 33