Search code examples
c#xmlserializersvcutil.exeformatexceptionservice-model

How to change xsd:date format in svcutil.exe generated classes


I generated C# classes from .wsdl file and it works. But I have following issue. Service formats xsd:date types in response incorrect. Example:

<date xsi:type="xsd:date">2016-01-27 14:20:30</date>

But it should be one of these:

<date xsi:type="xsd:date">2016-01-27</date>  
<date xsi:type="xsd:dateTime">2016-01-27T14:20:30</date>

And because of that I get exception

Unhandled Exception: System.ServiceModel.CommunicationException: Error in deserializing body of reply message for operation 'createVacature'. ---> System.InvalidOperationException: There is an error in XML document (2, 646). ---> System.FormatException: String was not recognized as a valid DateTime.

How can I override date parsing? Or any other way to fix it? Implementing all that manually without svcutil.exe would be overkill.


Solution

  • Here is my solution. I intercept service response before parsing and manually edit it.

    Here is date fixing functionality:

    public class MessageDateFixer : IClientMessageInspector
    {
        public object BeforeSendRequest(ref Message request, IClientChannel channel)
        {
            return null;
        }
    
        public void AfterReceiveReply(ref Message reply, object correlationState)
        {
            XmlDocument document = new XmlDocument();
            MemoryStream memoryStream = new MemoryStream();
            XmlWriter xmlWriter = XmlWriter.Create(memoryStream);
            reply.WriteMessage(xmlWriter);
            xmlWriter.Flush();
            memoryStream.Position = 0;
            document.Load(memoryStream);
    
            FixMessage(document);
    
            memoryStream.SetLength(0);
            xmlWriter = XmlWriter.Create(memoryStream);
            document.WriteTo(xmlWriter);
            xmlWriter.Flush();
            memoryStream.Position = 0;
            XmlReader xmlReader = XmlReader.Create(memoryStream);
            reply = Message.CreateMessage(xmlReader, int.MaxValue, reply.Version);
        }
    
        private static void FixMessage(XmlDocument document)
        {
            FixAllNodes(document.ChildNodes);
        }
    
        private static void FixAllNodes(XmlNodeList list)
        {
            foreach (XmlNode node in list)
            {
                FixNode(node);
            }
        }
    
        private static void FixNode(XmlNode node)
        {
            if (node.Attributes != null &&
                node.Attributes["xsi:type"] != null)
            {
                if (node.Attributes["xsi:type"].Value == "xsd:date")
                {
                    node.Attributes["xsi:type"].Value = "xsd:dateTime";
                    node.InnerXml = node.InnerXml.Replace(" ", "T");
                }
            }
    
            FixAllNodes(node.ChildNodes);
        }
    }
    

    Here is auxiliary class:

    public class DateFixerBehavior : IEndpointBehavior
    {
        public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
        {
            clientRuntime.MessageInspectors.Add(new MessageDateFixer());
        }
    
        public void Validate(ServiceEndpoint endpoint)
        {
    
        }
    
        public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
        {
    
        }
    
        public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
        {
    
        }
    }
    

    Here is usage:

    PosterToolClient poster = new PosterToolClient();
    poster.Endpoint.Behaviors.Add(new DateFixerBehavior());