不要序列化或从序列化的日期时间对象中删除时区

本文关键字:序列化 对象 删除 时间 时区 日期 | 更新日期: 2023-09-27 18:32:11

接到的奇怪任务是使用 XML 序列化序列化一个 LARGE 对象。 此对象包含多个嵌套的用户定义类,以及多个日期时间字段。日期时间数据的要求是,它必须始终以最初创建和设置数据的用户的时区显示。 因此,我不能使用 UTC 或本地时间,因为在反序列化时,它们不会与以前相同。 我也无法以 UTC 显示值,它们必须以本地时间显示。 我需要的是一些奇怪的序列化格式,它代表了"绝对本地时间"的概念......那将是"没有时区的本地时间"。

我可以使用正则表达式从日期字符串中删除 TZ,这很容易。 但是我正在处理的对象的巨大大小意味着我经常得到一个 OutOfMemoryException。 我看到它在没有调试的情况下运行一次,并且在操作过程中我的使用内存从 100k 飙升到 800k。 不好。 那是较小的文件之一。

Doc.DocumentElement.InnerXML = Regex.Replace(Doc.DocumentElement.InnerXML, "(''d{4}-''d{2}-''d{2}T''d{2}:''d{2}:''d{2})(''+|-)(''d{2}:''d{2})", "$1")

到目前为止,我看到的唯一选择是创建所有 dateTime 字段的副本,将 DT 字段本身设置为"XmlIgnore()",然后在重新加载文档后从序列化字符串数据中手动恢复所有日期。 这也是不切实际的。请参阅自定义日期时间 XML 序列化

有没有办法强制序列化引擎在没有时区数据的情况下序列化 DateTime 对象? 最好是不必单独应用于对象中的每个 DT 属性的通用内容?

!!编辑!!

我可能已经找到了部分解决方案。 这至少可能有助于向前迈进。 DateTimeKind.Un指定在序列化时似乎没有任何时区数据附加到它。 这是我正在寻找的解决方案吗? 使用 DateTime.SpecifyKind 强制转换我的所有日期时间数据?

public DateTime? StartDate
    {
        get 
        { return _StartDate; }
        set
        {
            if (_StartDate == value)
                return;
            if (value != null)
                _StartDate = DateTime.SpecifyKind(value.Value, DateTimeKind.Unspecified);
            else
                _StartDate = value;
            OnPropertyChanged("StartDate");
        }
    }

不要序列化或从序列化的日期时间对象中删除时区

我认为

您需要重新评估您的要求或假设。

你写道:

日期时间数据的要求是,它必须始终以最初创建和设置数据的用户的时区显示。因此,我不能使用 UTC 或本地时间,因为在反序列化时,不会与以前相同。

我认为你的分析不正确。在我看来,您不必要地将序列化到存储与向用户"显示"混合在一起。 但这两件事不应该相关。 据我了解,要求是:

  • 您希望序列化和反序列化许多不同的时间值。
  • "显示"这些时间时,您希望显示使用原始时区。

这些是不同的要求。

序列化日期时间将存储某个时刻,但会丢失时区信息。在我看来,您需要单独序列化时区信息,每个XML文档一次。 如果这样做,则时间反序列化会自动工作 - 您始终可以从最初放入存储的存储中获取确切时刻。

显示时间时,请使用单独存储在 XML 文档中的时区信息。 如果原始对象中不存在包含时区的属性,那么在我看来,您的对象模型不太适合应用程序的要求,在这种情况下,您需要修改对象定义以包含标识时区的字符串。(见 http://msdn.microsoft.com/en-us/library/system.timezoneinfo.aspx)

至于内存不足错误,这可能是一个不相关的问题。这也可能是由于您使用大型 XmlDocument 对象进行摸索。使用 Xml 序列化时,这应该是不必要的。

我建议创建自定义类型来保存这样的日期。它将允许您以任何您想要的方式处理序列化。作为更简单的方法,请考虑将它们保存为字符串,格式为 ISO8601 (2012-05-04-26T12:57),没有专门为这种情况指定的时区。

从序列化数据中批量删除时区可能不是一个好主意,因为一旦您实际需要节省绝对时间,它就会引起有趣的问题。特别是如果代码是共享的。

找到答案。 这不是我想要的,但可以作为一种有效的解决方法。

private static readonly Regex DTCheck = new Regex(@"('d{4}-'d{2}-'d{2}T'd{2}:'d{2}:'d{2})(['+|-]'d{2}:'d{2})");
    /// <summary>
    /// Removes any instances of the TimeZoneOffset from the RigX after it has been serialized into an XMLString ++ Called from the "Save" process
    /// </summary>
    /// <param name="rigx"></param>
    /// <returns>StringReader referencing the re-formatted XML String</returns>
    private static StringReader RemoveTZOffsetFromRigX(RigX rigx)
    {
        StringBuilder sb = new StringBuilder();
        StringWriter sw = new StringWriter(sb);
        XmlSerializer ser = new XmlSerializer(typeof(RigX));
        ser.Serialize(sw, rigx);
        string xmlText = sb.ToString();
        if (DTCheck.IsMatch(xmlText))
            xmlText = DTCheck.Replace(xmlText, "$1");
        StringReader Sreader = new StringReader(xmlText);
        return Sreader;
    }
    /// <summary>
    /// Removes the TimeZone offset from a RigX as referenced by stream.  Returns a reader linked to the new stream  ++ Called from the "Load" process
    /// </summary>
    /// <param name="stream">stream containing the initial RigX XML String</param>
    /// <returns>StringReader referencing the re-formatted XML String</returns>
    private StringReader RemoveTZOffsetFromXML(MemoryStream stream)
    {
        stream.Position = 0;
        StreamReader reader = new StreamReader(stream, Encoding.UTF8);
        string xmlText = reader.ReadToEnd();
        reader.Close();
        stream.Close();
        if (DTCheck.IsMatch(xmlText))
            xmlText = DTCheck.Replace(xmlText, "$1");
        StringReader Sreader = new StringReader(xmlText);
        return Sreader;
    }

从文件中读取 XML 后,在通过序列化程序运行它之前,在裸 XML 文本上运行正则表达式以删除偏移量。 该函数返回针对修改后的 XML 字符串运行的字符串读取器,然后可以通过对对象的反序列化来运行该读取器。

您可以使用

stringBuilder 截获序列化的 xml,而不是使用序列化程序将 xml 直接保存到输出流。 然后,使用与加载过程中相同的过程,通过正则表达式删除时区偏移量,然后返回链接到修改文本的 StringReader,然后使用该字符串读取器将数据写回文件中。

有点黑客的感觉,但有效。 虽然内存非常密集,但如果可以的话,请避免直接调试函数,或者如果必须,尽量不要计算字符串,上次我尝试它完全崩溃了我的 VS 实例。