2016-03-08 25 views
5

Di seguito è la proprietà nel mio modello:Web Api 2 datetimeoffset serializzazione XML Problema

public DateTimeOffset AcquireDate { get; set; } 

Di seguito si riporta la configurazione in WebApiConfig:

config.Formatters.Clear(); 
config.Formatters.Add(new JsonMediaTypeFormatter()); 
config.Formatters.Add(new XmlMediaTypeFormatter()); 

Di seguito è la risposta JSON (data è in IS 8601 formato):

enter image description here

Di seguito è riportato il respons XML e:

<AcquireDate xmlns:d3p1="http://schemas.datacontract.org/2004/07/System"> 
     <d3p1:DateTime>2008-01-10T16:40:12.1523923Z</d3p1:DateTime> 
     <d3p1:OffsetMinutes>330</d3p1:OffsetMinutes> 
</AcquireDate> 

da Fiddler:

enter image description here

in XML risposta, datetime e offset sono disponibili in due diversi elementi. Voglio DateTimeOffset come valore singolo, proprio come la risposta JSON (nel formato ISO 8601).

Potrei usare un'altra proprietà che sarebbe di tipo stringa e in questo modo il mio problema potrebbe essere risolto (il setter vuoto richiede che questa proprietà sia serializzata).

[DataMember(Name="AcquireDate")] 
    public string AcquireDateString 
    { 
     get 
     { 
      return AcquireDate.ToString("yyyy-MM-ddTHH:mm:ss.fffffffzzz"); 
     } 
     set { 
      AcquireDate = DateTimeOffset.Parse(value); 
     } 
    } 

Qualsiasi altra soluzione oltre a questo?

La soluzione non deve influire sulla serializzazione Json esistente che funziona correttamente.

risposta

2

Comprendiamo che DateTimeOffset ti offre l'ora UTC oltre a quanto tale valore differisce da UTC. Pertanto, il valore identifica sempre univocamente un singolo punto nel tempo. Questa è un'informazione molto preziosa che potresti non voler perdere. Ma se a causa dei requisiti è necessario memorizzare solo l'offset, quindi continua a leggere la soluzione di seguito.

Poiché è possibile modificare il tipo da DateTimeOffset a una stringa, è possibile modificare il tipo un po 'e utilizzare ancora DateTimeOffset.

Per esempio,

using System; 
using System.Xml; 
using System.Xml.Schema; 
using System.Xml.Serialization; 
using Newtonsoft.Json; 

namespace ConsoleApplication8 
{ 
    public struct Iso8601SerializableDateTimeOffset : IXmlSerializable 
    { 
     public DateTimeOffset value; 

     public Iso8601SerializableDateTimeOffset(DateTimeOffset value) 
     { 
      this.value = value; 
     } 

     public static implicit operator Iso8601SerializableDateTimeOffset(DateTimeOffset value) 
     { 
      return new Iso8601SerializableDateTimeOffset(value); 
     } 

     public static implicit operator DateTimeOffset(Iso8601SerializableDateTimeOffset instance) 
     { 
      return instance.value; 
     } 

     public static bool operator ==(Iso8601SerializableDateTimeOffset a, Iso8601SerializableDateTimeOffset b) 
     { 
      return a.value == b.value; 
     } 

     public static bool operator !=(Iso8601SerializableDateTimeOffset a, Iso8601SerializableDateTimeOffset b) 
     { 
      return a.value != b.value; 
     } 

     public static bool operator <(Iso8601SerializableDateTimeOffset a, Iso8601SerializableDateTimeOffset b) 
     { 
      return a.value < b.value; 
     } 

     public static bool operator >(Iso8601SerializableDateTimeOffset a, Iso8601SerializableDateTimeOffset b) 
     { 
      return a.value > b.value; 
     } 

     public override bool Equals(object o) 
     { 
      if (o is Iso8601SerializableDateTimeOffset) 
       return value.Equals(((Iso8601SerializableDateTimeOffset)o).value); 
      else if (o is DateTimeOffset) 
       return value.Equals((DateTimeOffset)o); 
      else 
       return false; 
     } 

     public override int GetHashCode() 
     { 
      return value.GetHashCode(); 
     } 

     public XmlSchema GetSchema() 
     { 
      return null; 
     } 

     public void ReadXml(XmlReader reader) 
     { 
      var text = reader.ReadElementString(); 
      value = DateTimeOffset.ParseExact(text, format: "o", formatProvider: null); 
     } 

     public override string ToString() 
     { 
      return value.ToString(format: "o"); 
     } 

     public string ToString(string format) 
     { 
      return value.ToString(format); 
     } 

     public void WriteXml(XmlWriter writer) 
     { 
      writer.WriteString(value.ToString(format: "o")); 
     } 
    } 

    public class Foo 
    { 
     public Guid Id { get; set; } 

     [JsonConverter(typeof(UtcDateTimeOffsetConverter))] 
     public Iso8601SerializableDateTimeOffset AcquireDate { get; set; } 
    } 


    class Program 
    { 
     static void Main(string[] args) 
     { 
      var foo = new Foo { 
       Id = Guid.NewGuid(), 
       AcquireDate = DateTimeOffset.Now 
      };   

      var xmlSerializer = new System.Xml.Serialization.XmlSerializer(foo.GetType()); 
      xmlSerializer.Serialize(Console.Out, foo); 
      Console.WriteLine(); 
      Console.ReadLine(); 
     } 
    } 
} 

uscita

<?xml version="1.0" encoding="IBM437"?> 
<Foo xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> 
    <Id>830cabe2-340b-42c6-bad4-12b5b8b1c43f</Id> 
    <AcquireDate>2016-03-14T10:47:51.8162249-04:00</AcquireDate> 
</Foo> 

Per JSON, abbiamo bisogno di un convertitore, ma siamo in grado di riutilizzare Newtonsoft.Json.Converters.IsoDateTimeConverter

public class UtcDateTimeOffsetConverter : Newtonsoft.Json.Converters.IsoDateTimeConverter 
    { 
     public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) 
     { 
      if (value is Iso8601SerializableDateTimeOffset) 
      { 
       var date = (Iso8601SerializableDateTimeOffset)value; 
       value = date.value; 
      } 
      base.WriteJson(writer, value, serializer); 
     } 

     public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) 
     { 
      object value = base.ReadJson(reader, objectType, existingValue, serializer); 
      if (value is Iso8601SerializableDateTimeOffset) 
      { 
       var date = (Iso8601SerializableDateTimeOffset)value; 
       value = date.value; 
      } 
      return value; 
     } 
    } 

Il controller

public class ValuesController : ApiController 
{ 
    public class Foo 
    { 
     public Guid Id { get; set; } 

     [JsonConverter(typeof(UtcDateTimeOffsetConverter))] 
     public Iso8601SerializableDateTimeOffset AcquireDate { get; set; } 
    } 

    // GET api/values 
    public IEnumerable<Foo> Get() 
    { 
     return new Foo[] { 
      new Foo() { 
       Id = Guid.NewGuid(), 
       AcquireDate = DateTimeOffset.Now 
      } 
     }; 
    } 
} 

uscita

<ArrayOfFoo xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/WebApplication1.Models"> 
    <Foo> 
     <AcquireDate>2016-03-14T12:04:30.2791167-04:00</AcquireDate> 
     <Id>b3188528-f854-454a-bf9f-9822ff27dc6f</Id> 
    </Foo> 
</ArrayOfFoo> 

JSON

[{"Id":"e24bc769-3463-4320-b39a-9ff97e709142","AcquireDate":"2016-03-15T10:47:29.3061449-04:00"}] 

esempio completi sono disponibili all'indirizzo github: https://github.com/alexnolasco/DatetimeOffsetXMLSerializationExample

Consulta anche: How can I XML Serialize a DateTimeOffset Property?

Choosing Between DateTime, DateTimeOffset, TimeSpan, and TimeZoneInfo

+0

Come dovrei usarlo con API Web? Qualsiasi esempio sarebbe utile. –

+0

@NileshThakkar Aggiunto un metodo APIController di esempio alla risposta, vedere se questo aiuta. –

+0

Dobbiamo apportare modifiche alla configurazione per farlo funzionare per le API Web? Poiché l'API Web per impostazione predefinita utilizza DataContractSerializer per XMLMediaTypeFormatter. –