wcfhttp-headershttprequest

Remove root element from MessageHeader


My problem is that root element is written twice. I would like to delete root element generated by MessageHeader itself:

My code:

var el = XElement.Parse("<wsse:Security xmlns:wsse='http://schemas.xmlsoap.org/ws/2002/07/secext'>" +
        "<wsse:UsernameToken xmlns:wsu='http://schemas.xmlsoap.org/ws/2002/07/utility'>" +
        "<wsse:Username>TEST</wsse:Username>" +
        "<wsse:Password Type='wsse:PasswordText'>TEST1</wsse:Password>" +
        "</wsse:UsernameToken>" +
        "</wsse:Security>");

request.Headers.Add(MessageHeader.CreateHeader("Security", "wsse", el));

Here, code generated in request header - I would like to remove outer 'Security' node, but I dont' know how:

<Security xmlns="wsse">
      <wsse:Security xmlns:wsse="http://schemas.xmlsoap.org/ws/2002/07/secext">
        <wsse:UsernameToken xmlns:wsu="http://schemas.xmlsoap.org/ws/2002/07/utility">
          <wsse:Username>TEST</wsse:Username>
          <wsse:Password Type="wsse:PasswordText">TEST1</wsse:Password>
        </wsse:UsernameToken>
      </wsse:Security>
    </Security>

Solution

  • Since there is no overload for MessageHeader.CreateHeader, you can just extract it from the parsed structure itself and use its data to create MessageHeader with it:

    var el = XElement.Parse("<wsse:Security xmlns:wsse='http://schemas.xmlsoap.org/ws/2002/07/secext'>" +
            "<wsse:UsernameToken xmlns:wsu='http://schemas.xmlsoap.org/ws/2002/07/utility'>" +
            "<wsse:Username>TEST</wsse:Username>" +
            "<wsse:Password Type='wsse:PasswordText'>TEST1</wsse:Password>" +
            "</wsse:UsernameToken>" +
            "</wsse:Security>");
    
    string name = el.Name.LocalName;
    string xmlns = el.Attributes().Single().Value;
    XNode singleChild = el.Nodes().Single();
    
    var h = MessageHeader.CreateHeader(name, xmlns, singleChild);
    

    That should give you the desired result.

    Note that MessageHeader.CreateHeader method forbids using names with ":" sign in it (so no NamespaceName are allowed).

    Update based on comment

    Alright, here is a way to replace what the MessageHeader outputs with a custom implementation of it:

    public sealed class PrefixedMessageHeader(XElement header) : MessageHeader
    {
        public override string Name => header.Name.LocalName;
    
        public override string Namespace => header.Name.Namespace.NamespaceName;
    
        protected override void OnWriteStartHeader(XmlDictionaryWriter writer, MessageVersion messageVersion)
        {
            writer.WriteStartElement(header.GetPrefixOfNamespace(header.Name.Namespace), Name, Namespace);
        }
    
        protected override void OnWriteHeaderContents(XmlDictionaryWriter writer, MessageVersion messageVersion)
        {
            foreach (XNode node in header.Nodes())
            {
                using var reader = node.CreateReader();
                writer.WriteNode(reader, false);
            }
        }
    }
    

    So, instead of calling MessageHeader.CreateHeader you can just create initialize new PrefixedMessageHeader(el) and pass it on to the request.Headers.Add method.