Question

I used the Add Service Reference feature to create a proxy to an external web service.

By default, the WCF client is producing SOAP messages where the message body has namespace decorations that look like this:

  <s:Body>
    <BankingTransaction xmlns="http://tempuri.org/">
      <amount>0</amount>
    </BankingTransaction>
  </s:Body>

I need the message body to look like this instead

  <s:Body>
    <bb:BankingTransaction xmlns:bb="http://tempuri.org/">
      <amount>0</amount>
    </bb:BankingTransaction>
  </s:Body>

The distinction is the "bb" xml namespace alias. The web service that I'm trying to consume requires that the xml namespace for the message payload be aliased. And the default behavior the WCF client is to define the namespace as the DEFAULT namespace. I've searched high and low for a configuration / decoration solution to this problem and haven't found it. Barring a configuration solution, I'll have to inspect and alter each SOAP message after it is serialized. #lame.

Is there a simple solution here?

Was it helpful?

Solution

The solution to this problem is to create a custom MessageInspector (via IClientMessageInspector) to inspect and alter the SOAP messages that the WCF client proxy produces, prior to sending them over the wire. The basis for this solution is articulated in Steven Cheng's post, "[WCF] How to modify WCF message via custom MessageInspector", with further background from Kirk Evan's post, "Modify Message Content with WCF".

I used the code from Steven's post to wire up the custom MessageInspector infrastructure. Then I modified his Transformf2() method, which operates only on the <Body> portion of the SOAP message, to suit my particular needs. In my case, as described in the original question, I needed to define and use an alias for my target web service XML namespace, xmlns="http://tempuri.org", above.

To do this I must

  1. obtain a reference to the operation node, <BankingTransaction>, which will always be the first (and only) child of <Body>
  2. remove the attribute that sets the default namespace to the target namespace
  3. set the prefix (namespace alias) for the node

The modified Transform2() code that does this is below:

   private static Message Transform(Message oldMessage)
    {
        //load the old message into XML
        var msgbuf = oldMessage.CreateBufferedCopy(int.MaxValue);

        var tmpMessage = msgbuf.CreateMessage();
        var xdr = tmpMessage.GetReaderAtBodyContents();

        var xdoc = new XmlDocument();
        xdoc.Load(xdr);
        xdr.Close();

        // We are making an assumption that the Operation element is the
        // first child element of the Body element
        var targetNodes = xdoc.SelectNodes("./*");

        // There should be only one Operation element
        var node = (null != targetNodes) ? targetNodes[0] : null;

        if (null != node)
        {
            if(null != node.Attributes) node.Attributes.RemoveNamedItem("xmlns");
            node.Prefix = "bb";
        }

        var ms = new MemoryStream();
        var xw = XmlWriter.Create(ms);
        xdoc.Save(xw);
        xw.Flush();
        xw.Close();

        ms.Position = 0;
        var xr = XmlReader.Create(ms);

        //create new message from modified XML document
        var newMessage = Message.CreateMessage(oldMessage.Version, null, xr);
        newMessage.Headers.CopyHeadersFrom(oldMessage);
        newMessage.Properties.CopyProperties(oldMessage.Properties);

        return newMessage;
    }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top