¿Cómo anular la serialización C # DateTime con la clase generada automáticamente desde wsdl?

Tengo un WSDL que el consumidor de mi servicio web espera que se cumpla estrictamente. Lo convertí en una interfaz con wsdl.exe e hice que mi servicio web lo implementara. Excepto por este problema, en general he estado satisfecho con los resultados.

Un método GetCurrentTime simple tendrá la siguiente clase de respuesta generada desde el WSDL en la definición de la interfaz:

[System.CodeDobmCompiler.GeneratedCodeAttribute("wsdl", "2.0.50727.3038")] [System.SerializableAttribute()] [System.Diagnostics.DebuggerStepThroughAttribute()] [System.ComponentModel.DesignerCategoryAttribute("code")] [System.Xml.Serialization.XmlTypeAttribute(Namespace="[Client Namespace]")] public partial class GetCurrentTimeResponse { private System.DateTime timeStampField; [System.Xml.Serialization.XmlElementAttribute(Form=System.Xml.Schema.XmlSchemaForm.Unqualified] public System.DateTime TimeStamp{ // [accesses timeStampField] } } 

Cuando coloco los datos de respuesta en la clase de respuesta generada automáticamente, se serializa en una respuesta XML apropiada. (La mayoría de los métodos web tienen tipos de retorno mucho más complicados con múltiples niveles de arreglos).

El problema es que la serialización predeterminada de los objetos DateTime viola uno de los requisitos en el WSDL:

 ...      ... 

Tenga en cuenta la última parte del patrón donde los subsegundos deben tener 1 o 7 caracteres, si están incluidos. El cliente parece estar rechazando la respuesta porque no cumple con ese requisito.

El problema principal es que cuando .NET serializa un objeto DateTime, omite todos los ceros finales, lo que significa que el valor del subsegundo resultante varía en longitud. (por ejemplo, “12: 34: 56.700″ se serializa como ” 12: 34: 56: 7 ” de forma predeterminada). Utilizamos una precisión de milisegundos, por lo que necesito todas las marcas de tiempo para formatear con 7 dígitos de subsegundos para cumplir con el WSDL.

Sería fácil si pudiera especificar una cadena de formato, pero no estoy seguro de cómo controlar la cadena que utiliza el objeto DateTime para serializar a XML, o para anular el comportamiento de serialización. ¿Cómo hago esto? Teniendo en cuenta lo siguiente …

  • Me gustaría modificar el código generado lo menos posible … preferiblemente de ninguna manera si el cambio se puede realizar a través de una clase parcial o una clase heredada.
  • El uso de una clase heredada para el tipo de retorno del método web hará que el servicio web ya no implemente la interfaz generada automáticamente.
  • El tipo TimeStamp se produce en otros tipos de respuesta más complejos. Por lo tanto, anular manualmente todo el proceso de serialización puede llevar mucho tiempo de forma prohibitiva.

Actualización: Intenté implementar IXmlSerializable desde el principio, como sugirió John, y obtuve el siguiente error: System.InvalidOperationException: There was an error reflecting type '[...].GetCurrentTimeResponse'. ---> System.InvalidOperationException: Only XmlRoot attribute may be specified for the type [...].GetCurrentTimeResponse. Please use XmlSchemaProviderAttribute to specify schema type. System.InvalidOperationException: There was an error reflecting type '[...].GetCurrentTimeResponse'. ---> System.InvalidOperationException: Only XmlRoot attribute may be specified for the type [...].GetCurrentTimeResponse. Please use XmlSchemaProviderAttribute to specify schema type. Después de eso, pensé que tendría que piratear demasiado el código generado para mi gusto y seguir buscando otras soluciones.

Nunca he visto un esquema XML dentro de un WSDL colocar una restricción en un xsd:dateTime .

Creo que tendrás que implementar IXmlSerializable en tu clase GetCurrentTimeResponse . Puede hacer esto debido a la característica de “clase parcial” de C #. Cree otro archivo en su proyecto y agregue el siguiente código:

 public partial class GetCurrentTimeResponse : System.Xml.Serialization.IXmlSerializable { public XmlSchema GetSchema() { return null; } public void ReadXml(XmlReader reader) { // Fill the TimeStamp property here } public void WriteXml(XmlWriter writer) { // Write out TimeStamp.ToString( // System.Globalization.CultureInfo.InvariantCulture, // "proper format"); } } 

Posiblemente no sea la respuesta más relevante a su pregunta original, pero desde que fui redirigido a esta página mientras buscaba una manera de controlar el formato de las propiedades de DateTime en la serialización, alguien más podría encontrarlo útil.

Descubrí que al agregar lo siguiente a su app.config / web.config se le indica a la serialización .NET que formatee las propiedades de DateTime de manera consistente:

     

Ref: http://msdn.microsoft.com/en-us/library/ms229751(v=VS.85).aspx

Soy un imbécil

Primero, la expresión regular significa “entre 1 y 7 dígitos”, no “1 o 7 dígitos”, aunque en mi defensa, internet estaba equivocado. La razón por la que la respuesta no estaba validando era que faltaba la “Z” al final que indicaba UTC.

La solución fue tan simple como esta:

 GetCurrentTimeResponse response = new GetCurrentTimeResponse() { TimeStamp = timeStampWeWant.ToUniversalTime() } 

¡Y funcionó!

 2010-04-20T20:12:16.674Z