Web Api 2 DatetimeOffset XML 序列化问题

Web Api 2 DatetimeOffset XML Serialization Issue

下面是我模型中的属性:

 public DateTimeOffset AcquireDate { get; set; }

下面是WebApiConfig中的配置:

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

下面是 json 响应(日期采用 IS 8601 格式):

下面是 XML 响应:

<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>

来自提琴手:

在Xml 响应中,日期时间和偏移量在两个不同的元素中可用。我希望 DateTimeOffset 作为单个值,就像 json 响应(ISO 8601 格式)一样。

我可以再使用一个 属性 类型的字符串,这样我的问题就可以解决了(空的 setter 需要对这个 属性 进行序列化)。

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

除此之外还有其他解决方案吗?

解决方案不应影响现有的 Json 序列化,它工作正常。

让我们了解 DateTimeOffset 为您提供 UTC 时间以及该值与 UTC 的差异。因此,该值始终明确标识单个时间点。这是您可能不想泄露的非常有价值的信息。但如果由于要求您必须只存储偏移量,请继续阅读下面的解决方案。

既然您可以灵活地将类型从 DateTimeOffset 更改为字符串,那么也许您可以稍微更改类型并仍然使用 DateTimeOffset。

例如,

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();
        }
    }
}

输出

<?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>

对于JSON,我们需要一个转换器,但我们可以重复使用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;
        }
    }

控制器

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
            }
        };
    }
 }

输出

<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"}]

完整示例可在 github 上找到:https://github.com/alexnolasco/DatetimeOffsetXMLSerializationExample

另请参阅:How can I XML Serialize a DateTimeOffset Property?

Choosing Between DateTime, DateTimeOffset, TimeSpan, and TimeZoneInfo