问题
The odd task that I have been given is to serialize a LARGE object using XML Serialization. This object contains multiple Nested UserDefined classes, with multiple DateTime fields. The Requirement for the DateTime data is that it must ALWAYS be displayed in the TimeZone of the user who initially created and set the data. Thus, I Cannot use UTC OR Local times because when de-serialized, they wouldn't be the same as they were. I also cannot display the values in UTC, they must be displayed in Local Time. What I need is some odd serialization format that represents the concept of "Absolute Local Time"...that would be "Local Time without TimeZone".
I can strip the TZ from the date string using Regex, that's easy. but the sheer size of the object I'm dealing with means that more often than not I get an OutOfMemoryException. I watched it run without debug once and my used memory spiked from 100k to 800k during the operation. Not nice. And that was one of the smaller files.
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")
So far, the only option I have seen is to create duplicates of ALL the dateTime fields, set the DT fields themselves as "XmlIgnore()", and then manually restore all the dates from the serialized string data after the doc is re-loaded. This is also not practical. See Custom DateTime XML Serialization
Is there any way to force the serialization engine to serialize DateTime objects without their TimeZone data? Preferably something generic that doesn't have to be individually applied to every DT property in the object?
!!EDIT!!
I may have found a partial solution. It might at least help moving forward. DateTimeKind.Unspecified, when serialized, doesn't seem to have any TimeZone data attached to it. Is this the solution I'm looking for. Forcefully cast all my DateTime data using 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");
}
}
回答1:
I think you need to re-evaluate your requirements or assumptions.
you wrote:
The Requirement for the DateTime data is that it must ALWAYS be displayed in the TimeZone of the user who initially created and set the data. Thus, I Cannot use UTC OR Local times because when de-serialized, the wont be the same as they were.
I don't think your analysis is correct. Seems to me you are unnecessarily co-mixing serialization to storage with "display" to the user. But those two things should not be related. The requirements as I understand them are:
- You want to serialize and de-serialize a number of different time values.
- when "displaying" those times, you want the display to use the original timezone.
These are distinct requirements.
Serializing a DateTime will store a moment in time, but you lose the TimeZone information. It seems to me you need to separately serialize the timezone information, once for each XML document. If you do that, then deserialization of times works automatically - you always get the exact moment in time out of storage that you had originally put into storage.
When you display the time, use the timezone information that is separately stored in the XML document. If a property containing TimeZone is not present in the original object, then it seems to me that your object model is not well-suited to the requirements of the application, in which case you need to modify the object definition to include a string identifying the TimeZone. (See http://msdn.microsoft.com/en-us/library/system.timezoneinfo.aspx)
As for the out-of-memory error, that may be an unrelated problem. It also may be due to you futzing with large XmlDocument objects. That should be unnecessary when using Xml serialization.
回答2:
I would recommend to create custom type to hold such date. It will let you deal with serialization any way you want. As simpler approach consider saving them as strings in ISO8601 format (2012-05-04-26T12:57) without timezone which is specified exactly for this case.
Wholesale removing timezone from serialized data may not be a good idea as it will cause interesting problems as soon as you actually need to save absolute time. Especially if the code is shared.
回答3:
Found an answer. This is not exactly what I was looking for, but serves as an effective work around.
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;
}
After reading in the XML From the file, and before running it through the serializer, run the Regex on the bare XML Text to remove the offset. The function returns a string reader running against the modified XML string, which can then be run through a de-serialization into the object.
Rather than using the serializer to save the xml directly to the output stream, you can use a stringBuilder to intercept the serialized xml. Then using the same process as during the loading procedure, you remove the TimeZone offset via RegularExpression, then return a StringReader linked to the modified text, which is then used to write the data back out into a file.
Slightly hackish feeling, but effective. Very memory intensive though, avoid debugging the functions directly if you can, or if you have to, try not to evaluate the strings, last time I tried that it crashed my VS instance completely.
来源:https://stackoverflow.com/questions/10325108/dont-serialize-or-remove-timezone-from-serialized-datetime-objects