我给出的奇怪任务是使用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");
}
}
答案 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实例。