4

给我的奇怪任务是使用 XML 序列化序列化 LARGE 对象。此对象包含多个嵌套的 UserDefined 类,以及多个 DateTime 字段。DateTime 数据的要求是它必须始终显示在最初创建和设置数据的用户的时区中。因此,我不能使用 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()”,然后在重新加载文档后手动从序列化字符串数据中恢复所有日期. 这也不实用。请参阅自定义 DateTime XML 序列化

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

!!编辑!!

我可能已经找到了部分解决方案。它至少可能有助于向前发展。DateTimeKind.Unspecified 在序列化时似乎没有附加任何 TimeZone 数据。这是我正在寻找的解决方案。使用 DateTime.SpecifyKind 强制转换我的所有 DateTime 数据?

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");
        }
    }
4

3 回答 3

3

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

你写了:

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

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

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

这些是不同的要求。

序列化 DateTime 将及时存储一个时刻,但您会丢失 TimeZone 信息。在我看来,您需要单独序列化时区信息,每个 XML 文档一次。如果你这样做了,那么时间的反序列化就会自动工作——你总是能从你最初放入存储中的存储中获得准确的时间。

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

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

于 2012-04-25T23:38:39.700 回答
0

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

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

于 2012-04-26T19:49:34.210 回答
0

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

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 字符串运行的字符串读取器,然后可以通过反序列化将其运行到对象中。

与其使用序列化器将 xml 直接保存到输出流中,不如使用 stringBuilder 来截取序列化的 xml。然后使用与加载过程相同的过程,通过正则表达式删除 TimeZone 偏移量,然后返回链接到修改后的文本的 StringReader,然后用于将数据写回文件中。

有点骇人听闻的感觉,但很有效。虽然非常占用内存,但如果可以,请避免直接调试函数,或者如果必须,尽量不要评估字符串,上次我尝试它完全崩溃了我的 VS 实例。

于 2012-04-27T19:45:16.653 回答