Rise_against
Rise_against

Reputation: 1060

WCF Message Inspector - modify invalid XML SOAP response

I'm calling a SOAP service using a WCF client in .NET. It sometimes happens that the SOAP service is sending back a response that can't be serialized to the .NET objects. The main culprit here is a datetime field in which they are sending back "24:00:00" instead of "00:00:00".

In this case, we get back the following error: "The DateTime represented by the string is not supported in calendar System.Globalization.GregorianCalendar."

In order to solve this, I implemented a WCF Message Inspector, where I want to modify the message in the "AfterReceiveReply" method. Here I would like to find the incorrect date and correct it, before sending it back to the client.

I would do the replacement through string manipulation (find + replace).

However, I am not able to retrieve the content of the reply message as a string.. I tried doing this through "reply.GetReaderAtBodyContents()" and then get the "OuterXml", but this ends up crashing with the same error as above, since it's reading it as an XML object (and it contains an invalid date).

exception details

I also tried following, but there I have the same issue as well (since we can't write invalid XML to the XmlWrite object), it throws the same error as above.

MemoryStream ms = new MemoryStream();
XmlWriter xw = XmlWriter.Create(ms);
reply.WriteMessage(xw);
xw.Flush();
string body = Encoding.UTF8.GetString(ms.ToArray());
xw.Close();

The Message object only accepts an "XmlWriter" or "XmlDictionaryWriter" to write to a different stream: code snippet

Any ideas how I can get the raw body content of the reply message as a string object, without parsing it as an XML Document?

Thanks in advance!

Best regards

Upvotes: 0

Views: 945

Answers (1)

rene
rene

Reputation: 42414

If we assume the XmlDictionary that you get from GetReaderAtBodyContents isn't validating by itself, maybe this careful XmlReader that ignores FormatExceptions might be enough to get your dateTime rewritten.

Here is the implementation:

void AfterReceiveReply(ref System.ServiceModel.Channels.Message reply, object state) 
{
    // settings to ignore errors ...
    XmlReaderSettings settings = new XmlReaderSettings();
    settings.ValidationType = ValidationType.Schema;
    settings.ValidationEventHandler += (o,e) => { 
        if (e.Exception != null && !(e.Exception.InnerException is FormatException))
        {
          throw e.Exception;
        }
    }; // ignore formatexception errors

    var xmlrdr = XmlReader.Create(reply.GetReaderAtBodyContents(),settings);
    var ms = new MemoryStream();
    using(var wr = XmlWriter.Create(ms))
    while(xmlrdr.Read())
    {
        // very simple and naive xml copy
         switch(xmlrdr.NodeType) 
         {
             case XmlNodeType.Element:
                 wr.WriteStartElement(xmlrdr.Name);
             break;
             case XmlNodeType.Text:
                 var text = xmlrdr.ReadContentAsString();
                 // names of nodes that are a date
                 if (xmlrdr.Name == "date") 
                 {
                     DateTime dtm;
                     if (!DateTime.TryParse(text, out dtm)) 
                     {
                         // let's assume this is the only thing that can go wrong
                         text = text.Replace("24:00:00", "23:59:59");
                     }
                     wr.WriteString(text);
                     wr.WriteEndElement(); // yeah, well, let's gamble
                 } 
                 else
                 {
                     wr.WriteString(text);
                 }
             break;
             case XmlNodeType.EndElement:
                 wr.WriteEndElement();
             break;
             default:
               // Debug.Assert
             break;
         }
    }
    ms.Position=0;
    // create a new message for the next handler
    reply = System.ServiceModel.Channels.Message.CreateMessage(
       reply.Version, 
       reply.Headers.Action, 
       XmlReader.Create(ms));

}

Upvotes: 1

Related Questions