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
下面是我模型中的属性:
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