[.NET][KB]XmlSerializer序列化,會不會被Culture影響

  • 5376
  • 0

[.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/