[.NET][KB]XmlSerializer序列化,會不會被Culture影響
前言
在之前有碰到,使用XmlSerializer產生出來的XML檔案,在日期的部份有上午/下午的時間,導致在不同server上反序列化失敗。當時透過將CurrentThread的Culture修改成CultureInfo.InvariantCulture之後就沒有問題了。
原始程式
public static void EntityToXml<T>(T entity, string path)
{
XmlSerializer serializer = new XmlSerializer(typeof(T));
var ns = new XmlSerializerNamespaces();
ns.Add(string.Empty, string.Empty);
using (TextWriter WriteFileStream = new StreamWriter(path, false, Encoding.UTF8))
{
serializer.Serialize(WriteFileStream, entity, ns);
}
}
修改後,就沒再碰到上午/下午的問題了
public static void EntityToXml<T>(T entity, string path)
{
XmlSerializer serializer = new XmlSerializer(typeof(T));
var ns = new XmlSerializerNamespaces();
ns.Add(string.Empty, string.Empty);
using (TextWriter WriteFileStream = new StreamWriter(path, false, Encoding.UTF8))
{
var currentCulture = Thread.CurrentThread.CurrentCulture;
Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture;
serializer.Serialize(WriteFileStream, entity, ns);
Thread.CurrentThread.CurrentCulture = currentCulture;
}
}
以結果來看,因為改了Thread的Culture,所以對於時間格式的問題就消除了。所以,每次在作序列化或反序列化時,都應該留意Thread的Culture特性。
但是,事實真是如此嗎?
模擬問題
當我針對一個Entity,其property type為DateTime時,我想要重新模擬出一樣的問題時,卻模擬不出來如何透過XmlSerializer產生出有上午/下午的資料。這實在很詭異,我很確定Culture是有被改變的(拿了很多特殊國家的格式來試,log中也可以看到日期輸出時是特殊的格式),但是序列化完的結果,仍是標準格式。
納悶的我去找了不少相關的資料,在這一篇文章中作者指出『Xml序列化與文化特性是獨立的』,也就是不會相互影響。我也認為這是很合理的,因為序列化反序列化,一旦與文化特性牽扯,就失去了序列化/反序列化的彈性。但是,只有這一篇作者提到,讓我有點不安。反而是在MSDN上,我找不到任何相關的reference,提到XmlSerializer與Culture的關聯。但,也正因找不到任何相關說明,讓我更加堅信,XmlSerializer與Culture應該沒有關聯才對。另外,如果每次做序列化/反序列化,都要考慮到文化特性,那線上的系統應該早就哀鴻遍野了,而且網路上的範例文章也不至於沒有提到這件事。
但是,之前的現象是確實存在的,因為手邊還留有『具有上午/下午時間』的Xml檔案。這不鬼擋牆了嗎?
追根究柢
追根究柢後發現,果然十之八九的鬼擋牆現象,都有一個白爛的自己。
問題的原因,就如同一些朋友所懷疑的:是不是哪邊有用到DateTime的ToString(),或是DateTime的Parse()。當時,我很確定的回答:沒有!因為使用Entity的時候,該用DateTime就是DateTime,沒有印象自己去ToString()作業或是Parse()轉換型別。
事實證明,在Entity的深處,有存在著這樣的處理動作。
實際的原因是,『某個Entity因為一個特殊的需求:當XML內容沒有某個property的tag的時候,仍然要能反序列化成功,且塞空值或初始值。』因為這個需求,所以在Entity上針對XML序列化/反序列化的屬性,動了點手腳,如下所示:
/// <summary>
/// Employee 的摘要描述
/// </summary>
[XmlRoot("Employee")]
public class Employee
{
/// <summary>
/// 生日
/// </summary>
/// <value>
/// The birthday.
/// </value>
[XmlIgnore]
public DateTime? Birthday { get; set; }
/// <summary>
/// Gets or sets the _ birthday_ surrogate.
/// </summary>
/// <value>
/// The _ birthday_ surrogate.
/// </value>
[XmlElement("Birthday")]
public string _Birthday_Surrogate
{
get
{
return (Birthday.HasValue) ? Birthday.ToString() : string.Empty;
}
set
{
if (!value.Equals(string.Empty))
{
Birthday = DateTime.Parse(value);
}
}
}
}
在設計程式時,都是使用Birthday,而在做XML序列化/反序列化時,是透過_Birthday_Surrogate。因為用了ToString()跟DateTime.Parse,所以就會與Culture有關。而且,這個屬性的格式轉換,因為是透過Xml相關的Attribute來做個簡單的轉換,所以基本上只會發生在XML序列化/反序列化的動作。而自己在測試XML序列化的測試案例,當然都是使用單純的DateTime格式,所以不會發生Culture的問題。
結論
XmlSerializer在序列化/反序列化時,實測結果,應該是不會被culture所影響。另外,有用到與文化特性相關的方法時,記得要考慮文化特性的問題,最好可以拉到一個地方統一設定。
最後,不要不信邪,以為某種情況一定不會發生、一定沒有、一定不是這樣,越是鬼擋牆、鬼遮眼的問題,通常越是讓人忽略的低級錯誤或是很小的細節問題。
blog 與課程更新內容,請前往新站位置:http://tdd.best/