c# - 以特定格式 .net 将 DateTime 序列化为 XML

标签 c# .net xml serialization format

我有大量自动生成的类,这些类使用 .NET XmlSerializer 序列化为 XML 或从 XML 反序列化。其中一些类包含 DateTime 属性。

我需要使用特定格式(例如“u”)序列化所有 DateTime 属性。 :

System.DateTime.Now.ToString("u");
//returns something like "2008-06-15 21:15:07Z"

反序列化方法似乎适用于这种格式,但序列化不适用 - 它只是给我硬编码格式与我在托管系统中默认使用的格式相同(也许我可以通过更改线程设置来欺骗序列化程序?) (检查更新 #1)。

注意: 我已经检查了几个相关问题,它们通过修改原始属性和添加新属性来解决。对我来说,这是 Not Acceptable ,因为我必须进行大量修改。

下面是我用来测试这个问题的示例测试类。

using System;
using System.Linq;
using System.Text;
using System.Xml;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Xml.Linq;
using System.Xml.Serialization;
using System.Globalization;
using System.IO;

namespace FunkyTests
{
    [TestClass]
    public class SerializationTests
    {
        [TestMethod]
        public void FunkySerializationTest()
        {
            var date = DateTime.Now;
            var dummy = new Dummy() { DummyProperty = date };
            var xml = SerializeToXml(dummy);//serializes with "o" format

            var expected = date.ToString("u", new CultureInfo("en-US"));//in this example I want "u" format
            var actual = XDocument.Parse(xml).Descendants("DummyProperty").Single().Value;
            Assert.AreEqual(expected, actual);
        }

        [TestMethod]
        public void FunkyDeserializationTest()
        {
            var date = DateTime.Now;
            var dummy = new Dummy() { DummyProperty = date };
            var xml = SerializeToXml(dummy);//serializes with "o" format
            var deserializedDummy = DeserializeFromXml<Dummy>(xml);

            Assert.AreEqual(dummy.DummyProperty, deserializedDummy.DummyProperty);
        }

        private static T DeserializeFromXml<T>(string xml)
        {
            using (var textReader = new StringReader(xml))
            {
                using (var reader = XmlReader.Create(textReader))
                {
                    var serializer = new XmlSerializer(typeof(T));
                    var result = serializer.Deserialize(reader);
                    return (T)result;
                }
            }
        }

        private static string SerializeToXml<T>(T objectToSerialize)
        {
            var xml = new StringBuilder();
            using (var writer = XmlWriter.Create(xml, new XmlWriterSettings { OmitXmlDeclaration = true }))
            {
                var ns = new XmlSerializerNamespaces();
                ns.Add(string.Empty, string.Empty);

                var serializer = new XmlSerializer(typeof(T));
                serializer.Serialize(writer, objectToSerialize, ns);
            }
            return xml.ToString();
        }

        public class Dummy
        {
            public DateTime DummyProperty { get; set; }
        }
    }
}

有什么有趣的想法吗?

更新#1: 我认为日期序列化依赖于 Thread.CurrentCulture。如果我正确解释 XmlSerializer 代码,格式是硬编码的(谢谢 ILSpy)。

// System.Xml.Serialization.XmlCustomFormatter
internal static string FromDateTime(DateTime value)
{
    if (XmlCustomFormatter.Mode == DateTimeSerializationSection.DateTimeSerializationMode.Local)
    {
        return XmlConvert.ToString(value, "yyyy-MM-ddTHH:mm:ss.fffffffzzzzzz");
    }
    return XmlConvert.ToString(value, XmlDateTimeSerializationMode.RoundtripKind);
}

更新 #2: 我添加了一个测试方法,该方法使用 Alexander Petrov 提出的答案。它在 Ticks 断言时失败。根据Serialize DateTime as binary问题出在 DateTime 的内部,是由丢失 DateTime 的“种类”引起的。建议使用 DateTimeOffset,这会导致另一个序列化问题,详见 How can I XML Serialize a DateTimeOffset Property?

最佳答案

尝试使用自定义的 xml 读取器和写入器。

public class CustomDateTimeWriter : XmlTextWriter
{
    public CustomDateTimeWriter(TextWriter writer) : base(writer) { }
    public CustomDateTimeWriter(Stream stream, Encoding encoding) : base(stream, encoding) { }
    public CustomDateTimeWriter(string filename, Encoding encoding) : base(filename, encoding) { }

    public override void WriteRaw(string data)
    {
        DateTime dt;

        if (DateTime.TryParse(data, out dt))
            base.WriteRaw(dt.ToString("u", new CultureInfo("en-US")));
        else
            base.WriteRaw(data);
    }
}

public class CustomDateTimeReader : XmlTextReader
{
    public CustomDateTimeReader(TextReader input) : base(input) { }
    // define other required constructors

    public override string ReadElementString()
    {
        string data = base.ReadElementString();
        DateTime dt;

        if (DateTime.TryParse(data, null, DateTimeStyles.AdjustToUniversal, out dt))
            return dt.ToString("o");
        else
            return data;
    }
}

使用

var dummy = new Dummy { DummyProperty = DateTime.Now };
Console.WriteLine(dummy.DummyProperty);
var xs = new XmlSerializer(typeof(Dummy));
string xml;

using (var stringWriter = new StringWriter())
using (var xmlWriter = new CustomDateTimeWriter(stringWriter))
{
    xmlWriter.Formatting = Formatting.Indented;

    xs.Serialize(xmlWriter, dummy);
    xml = stringWriter.ToString();
}

Console.WriteLine(xml);

using (var stringReader = new StringReader(xml))
using (var xmlReader = new CustomDateTimeReader(stringReader))
{
    dummy = (Dummy)xs.Deserialize(xmlReader);
    Console.WriteLine(dummy.DummyProperty);
}

优点:

此方法自动将日期时间的所有值序列化(反)序列化为所需格式/从所需格式序列化,无需更改生成的类。

缺点:

这种方法可能会意外更改日期时间等值。

这种方法可能会减慢(反)序列化。

关于c# - 以特定格式 .net 将 DateTime 序列化为 XML,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38737699/

相关文章:

c# - ASP.NET MVC 4 脚本 bundle 在部署时导致错误

c# - Dapper 反序列化 XML

c# - 按照 MVVM 设计模式从 ViewModel 访问 this.content

python - 对使用哪个 XML 处理选项感到困惑

java - 将 XML 转换为属于同一模式的不同版本且稍有不同的 XML

c# - 如何快速检查警报是否存在?

c# - 如何运行可能同步或可能异步的代码?

c# - 如何让 asp.net mvc 复选框触发一个 Action ?

c# - 如何生成与以前在 C# 中随机生成的 key 相同的 key

java - VTD-XML 可以将字符串作为输入吗?