在使用带有DataType时间的XmlElement注释的属性进行反序列化时,XmlSerializer不考虑本地时区

时间:2018-05-02 13:27:36

标签: c# timezone xmlserializer

当我使用XmlSerializer.Deserialize反序列化时间字符串时,我希望它考虑我的本地时区,以便格式化时间字符串

00:00:00.0000000+01:00

被解析为00:00,因为我在时区GMT + 1。

我弄错了吗?

以下是我运行的用于测试xml反序列化的代码:

using System;
using System.IO;
using System.Xml.Serialization;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Testing
{
    [TestClass]
    public class FooTest
    {
        [TestMethod]
        public void Test()
        {
            var serializer = new XmlSerializer(typeof(Foo),
                new XmlRootAttribute("Foo"));

            var xml = "<Foo><TheTime>00:00:00.0000000+01:00</TheTime></Foo>";

            var stream = new MemoryStream();
            var writer = new StreamWriter(stream);
            writer.Write(xml);
            writer.Flush();
            stream.Position = 0;

            var f = (Foo) serializer.Deserialize(stream);

            Assert.AreEqual("00:00", f.TheTime.ToShortTimeString()); // actual: 01:00
        }

        [Serializable]
        public class Foo
        {
            [XmlElement(DataType = "time")]
            public DateTime TheTime { get; set; }
        }
    }
}

1 个答案:

答案 0 :(得分:1)

不幸的是,没有内置类型,当它包含偏移量(在XSD规范中可选)时,您可以将xs:time值反序列化。

相反,您需要定义自定义类型并实现适当的接口以进行自定义序列化和反序列化。下面是一个最小的TimeOffset结构,可以做到这一点。

[XmlSchemaProvider("GetSchema")]
public struct TimeOffset : IXmlSerializable
{
    public DateTime Time { get; set; }
    public TimeSpan Offset { get; set; }

    public static XmlQualifiedName GetSchema(object xs)
    {
        return new XmlQualifiedName("time", "http://www.w3.org/2001/XMLSchema");
    }

    XmlSchema IXmlSerializable.GetSchema()
    {
        // this method isn't actually used, but is required to be implemented
        return null;
    }

    void IXmlSerializable.ReadXml(XmlReader reader)
    {
        var s = reader.NodeType == XmlNodeType.Element
            ? reader.ReadElementContentAsString()
            : reader.ReadContentAsString();

        if (!DateTimeOffset.TryParseExact(s, "HH:mm:ss.FFFFFFFzzz",
            CultureInfo.InvariantCulture, DateTimeStyles.None, out var dto))
        {
            throw new FormatException("Invalid time format.");
        }

        this.Time = dto.DateTime;
        this.Offset = dto.Offset;
    }

    void IXmlSerializable.WriteXml(XmlWriter writer)
    {
        var dto = new DateTimeOffset(this.Time, this.Offset);
        writer.WriteString(dto.ToString("HH:mm:ss.FFFFFFFzzz", CultureInfo.InvariantCulture));
    }

    public string ToShortTimeString()
    {
        return this.Time.ToString("HH:mm", CultureInfo.InvariantCulture);
    }
}

通过此定义,您现在可以将代码中Foo.TheTime的类型更改为TimeOffset,并且您的测试将通过。您还可以删除属性中的DataType="time",因为它是通过GetSchema方法在对象中声明的。