不要从序列化的DateTime对象中序列化或删除TimeZone

时间:2012-04-25 23:14:29

标签: c# datetime xml-serialization

我给出的奇怪任务是使用XML Serialization序列化LARGE对象。此对象包含多个嵌套的UserDefined类,具有多个DateTime字段。 DateTime数据的要求是它必须始终显示在最初创建和设置数据的用户的TimeZone中。因此,我无法使用UTC或本地时间,因为在反序列化时,它们将不会与它们相同。我也无法以UTC显示值,它们必须以本地时间显示。我需要的是一些奇怪的序列化格式,代表“绝对本地时间”的概念......即“没有TimeZone的本地时间”。

我可以使用正则表达式从日期字符串中删除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()”,然后在doc之后手动恢复序列化字符串数据中的所有日期重新装入。这也不实用。 见Custom DateTime XML Serialization

有没有办法强制序列化引擎在没有TimeZone数据的情况下序列化DateTime对象?优选地,通用的东西不必单独应用于对象中的每个DT属性?

!! EDIT !!

我可能找到了部分解决方案。它至少可以帮助前进。 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");
        }
    }

3 个答案:

答案 0 :(得分:3)

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

你写道:

  

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

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

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

这些是不同的要求。

序列化DateTime会存储片刻,但会丢失TimeZone信息。在我看来,您需要为每个XML文档单独序列化时区信息。如果您这样做,那么反序列化的时间会自动生效 - 您始终可以获得最初 存储的 out 的确切时刻。

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

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

答案 1 :(得分:0)

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

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

答案 2 :(得分: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文本上运行Regex以删除偏移量。该函数返回一个针对修改后的XML字符串运行的字符串阅读器,然后可以通过反序列化将其运行到对象中。

您可以使用stringBuilder拦截序列化的xml,而不是使用序列化程序将xml直接保存到输出流。然后使用与加载过程相同的过程,通过RegularExpression删除TimeZone偏移量,然后返回链接到修改后的文本的StringReader,然后将其用于将数据写回文件。

略微hackish的感觉,但有效。但是内存非常密集,如果可以,请避免直接调试函数,或者如果必须,请尽量不要评估字符串,上次我尝试完全崩溃了我的VS实例。