XmlSerializer не учитывает местный часовой пояс при десериализации свойства, аннотированного XmlElement с временем DataType - PullRequest
0 голосов
/ 02 мая 2018

Когда я десериализую строку времени, используя 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 голосов
/ 04 мая 2018

К сожалению, нет встроенного типа, в который можно десериализовать значение xs:time, когда оно включает смещение (которое необязательно в спецификации XSD).

Вместо этого вам необходимо определить пользовательский тип и реализовать соответствующие интерфейсы для настраиваемой сериализации и десериализации. Ниже приведена минимальная 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.

...