http://www.microsoft.com/china/MSDN/library/data/xml/Usdnxmlnettrblshtxsd.mspx?mfr=true
XmlSerializer 常見(jiàn)問(wèn)題疑難解答
發(fā)布日期: 6/30/2004 | 更新日期: 6/30/2004
Christoph Schittko
適用于:
Microsoft ?Visual Studio ?.NET
摘要:Christoph Schittko 討論了各種相關(guān)技巧,以便診斷在使用 .NET 框架中的 XML 序列化技術(shù)將 XML 轉(zhuǎn)換為對(duì)象(以及反向轉(zhuǎn)換)時(shí)發(fā)生的常見(jiàn)問(wèn)題。
本頁(yè)內(nèi)容
簡(jiǎn)介
.NET 框架中的 XmlSerializer 是一種很棒的工具,它將高度結(jié)構(gòu)化的 XML 數(shù)據(jù)映射到 .NET 對(duì)象。XmlSerializer 在程序中通過(guò)單個(gè) API 調(diào)用來(lái)執(zhí)行 XML 文檔和對(duì)象之間的轉(zhuǎn)換。轉(zhuǎn)換的映射規(guī)則在 .NET 類中通過(guò)元數(shù)據(jù)屬性來(lái)表示。這一編程模型帶有自己的錯(cuò)誤類別,開(kāi)發(fā)人員需要了解如何診斷這些錯(cuò)誤。例如,元數(shù)據(jù)屬性必須描述序列化程序可以處理的 XML 格式的所有變體。本文研究了在使用 XmlSerializer 構(gòu)建基于 XML 的解決方案時(shí)可能發(fā)生的各種錯(cuò)誤,并且討論了用來(lái)診斷這些錯(cuò)誤的技巧和工具。
XmlSerializer 的內(nèi)部工作方式
為了有效地解決在 XML 序列化過(guò)程中出現(xiàn)的問(wèn)題,需要了解一下在非常簡(jiǎn)單的 XmlSerializer 接口的內(nèi)部發(fā)生了什么事情。與傳統(tǒng)的分析范型相反,.NET 框架中 System.Xml.Serialization 命名空間的 XmlSerializer 將 XML 文檔綁定到 .NET 類的實(shí)例。程序員不再需要編寫(xiě) DOM 或 SAX 分析代碼,而是通過(guò)直接在這些類中附加 .NET 元數(shù)據(jù)屬性來(lái)聲明性地設(shè)置綁定規(guī)則。因?yàn)樗蟹治鲆?guī)則都是通過(guò)屬性表示的,所以 XmlSerializer 的接口非常簡(jiǎn)單。它主要由兩個(gè)方法組成:Serialize() 用于從對(duì)象實(shí)例生成 XML;Deserialize() 用于將 XML 文檔分析成對(duì)象圖。
在使用強(qiáng)類型的、能夠完美地映射到編程對(duì)象的結(jié)構(gòu)嚴(yán)謹(jǐn)?shù)?XML 格式時(shí),這種方法非常有效。如果格式由 W3C架構(gòu)定義,并且該架構(gòu)由不帶混合型內(nèi)容或且不過(guò)度使用通配符(xs:any 和 xs;anyAttribute)的 complexType 組成,則 XML 序列化是處理該數(shù)據(jù)的好方法。
面向消息的應(yīng)用程序就是一個(gè)很好的例子,這些應(yīng)用程序之間的交換格式已預(yù)先定義。因?yàn)樵S多消息驅(qū)動(dòng)的企業(yè)應(yīng)用程序都具有非常高的吞吐量要求,所以 Serialize() 和 Deserialize() 方法被設(shè)計(jì)為具有很高的執(zhí)行速度。實(shí)際上,正是 XmlSerializer 為 System.Messaging 命名空間中的具有高度可伸縮性的庫(kù)、ASP.NET Web 服務(wù)和 BizTalk Server 2004 提供了動(dòng)力。
為獲得 XmlSerializer 的高性能,需要付出雙重代價(jià)。首先是與給定 XmlSerializer 可以處理的 XML 格式有關(guān)的靈活性,其次是實(shí)例的構(gòu)造需要進(jìn)行大量的處理。
當(dāng)您實(shí)例化 XmlSerializer 時(shí),必須傳遞您試圖通過(guò)該序列化程序?qū)嵗M(jìn)行序列化和反序列化的對(duì)象的類型。序列化程序?qū)z查該類型的所有公共字段和屬性,以了解一個(gè)實(shí)例在運(yùn)行時(shí)引用哪些類型。接下來(lái),它將為一組類創(chuàng)建 C# 代碼,以便使用 System.CodeDOM 命名空間中的類處理序列化和反序列化。在此過(guò)程中,XmlSerializer 將檢查 XML 序列化屬性的反射類型,以便根據(jù) XML 格式定義來(lái)自定義所創(chuàng)建的類。這些類隨后被編譯為臨時(shí)程序集,并由 Serialize() 和 Deserialize() 方法調(diào)用以執(zhí)行 XML 到對(duì)象的轉(zhuǎn)換。
這個(gè)設(shè)置 XmlSerializer 的精巧過(guò)程和聲明性編程模型導(dǎo)致了三類錯(cuò)誤,其中一些錯(cuò)誤可能很難解決:
? |
所生成的序列化類期望被序列化的對(duì)象完全符合元數(shù)據(jù)屬性所定義的類型結(jié)構(gòu)。如果 XmlSerializer 遇到未聲明(顯式聲明或者是通過(guò) XML 序列化屬性聲明)的類型,則對(duì)象將無(wú)法序列化。
|
? |
XML 文檔在以下情況下無(wú)法反序列化:該文檔的根元素不能映射對(duì)象類型;該文檔的格式不正確,例如包含 XML 規(guī)范中定義為非法的字符;該文檔違反基礎(chǔ)架構(gòu)的限制(在某些情形下)。
|
? |
最后,序列化類的創(chuàng)建及其隨后的編譯可能由于多種不同的原因而失敗。當(dāng)傳遞給構(gòu)造函數(shù)的類型或者由該類型引用的類型實(shí)現(xiàn)了不受支持的接口或者不能滿足 XmlSerializer 施加的限制時(shí),類的創(chuàng)建可能會(huì)失敗。
當(dāng)附加的屬性生成無(wú)法編譯的 C# 代碼時(shí),編譯步驟可能會(huì)失敗。編譯步驟也可能由于與安全有關(guān)的原因而失敗。
|
下面各個(gè)部分將更深入地研究這些情況,并提供有關(guān)如何解決這些問(wèn)題的指導(dǎo)和建議。
序列化錯(cuò)誤
我們要研究的第一類錯(cuò)誤發(fā)生在 Serialize() 方法中。當(dāng)在運(yùn)行時(shí)傳遞給該方法的對(duì)象圖中的類型與在設(shè)計(jì)時(shí)在類中聲明的類型不匹配時(shí),將發(fā)生此類錯(cuò)誤。您可以通過(guò)字段或?qū)傩缘念愋投x來(lái)隱式聲明類型,也可以通過(guò)附加序列化屬性來(lái)顯式聲明類型。
圖
1.
對(duì)象圖中的類型聲明
這里需要指出的是,依靠繼承是不夠的。開(kāi)發(fā)人員必須通過(guò)將 XmlInclude 屬性附加到基類,或者通過(guò)將 XmlElement 屬性附加到字段(這些字段可以容納從所聲明的類型派生的類型的對(duì)象),來(lái)聲明 XmlSerializer 的派生類型。
例如,請(qǐng)看一下以下類層次結(jié)構(gòu):
public class Base
{
public string Field;
}
public class Derived
{
public string AnotherField;
}
public class Container
{
public Base MyField;
}
如果您依賴?yán)^承并且編寫(xiě)了與下面類似的序列化代碼:
Container obj = new Container();
obj.MyField = new Derived(); // legal assignment in the
//.NET type system
// ...
XmlSerializer serializer = new XmlSerializer( typeof( Container ) );
serializer.Serialize( writer, obj ); // Kaboom!
您將得到發(fā)自 Serialize() 方法的異常,這是因?yàn)闆](méi)有 XmlSerializer 的顯式類型聲明。
發(fā)自 XmlSerializer 的異常
診斷這些問(wèn)題的根源在開(kāi)始時(shí)可能比較困難,這是因?yàn)閬?lái)自 XmlSerializer 的異??雌饋?lái)并沒(méi)有提供有關(guān)其產(chǎn)生原因的大量信息;至少,它們沒(méi)有在開(kāi)發(fā)人員通常會(huì)查看的地點(diǎn)提供信息。
在大多數(shù)情況下,當(dāng)發(fā)生錯(cuò)誤時(shí),Serialize、Deserialize 甚至 XmlSerializer 構(gòu)造函數(shù)都會(huì)引發(fā)一個(gè)相當(dāng)普通的 System.InvalidOperationException。該異常類型可以在 .NET 框架中的許多地方出現(xiàn);它根本不是 XmlSerializer 所特有的。更糟糕的是,該異常的 Message 屬性也僅產(chǎn)生非常普通的信息。在上述示例中,Serialize() 方法會(huì)引發(fā)帶有以下消息的異常:
There was an error generating the XML document.
該消息最多也就是令人討厭的,因?yàn)楫?dāng)您看到 XmlSerializer 引發(fā)異常時(shí),就已經(jīng)猜到了這一點(diǎn)。現(xiàn)在,您只好無(wú)奈地發(fā)現(xiàn)該異常的 Message 無(wú)法幫助您解決問(wèn)題。
奇怪的異常消息和非描述性的異常類型反映了本文前面介紹的 XmlSerializer 內(nèi)部工作方式。Serialize() 方法會(huì)捕獲序列化類中引發(fā)的所有異常,將它們包裝到 InvalidOperationException 中,然后將該異常包沿著堆棧向上傳遞。
讀取異常消息
得到“實(shí)際”的異常信息的竅門是檢查該異常的 InnerException 屬性。InnerException 引用了從序列化類內(nèi)部引發(fā)的實(shí)際異常。它包含有關(guān)該問(wèn)題及其發(fā)生地點(diǎn)的非常詳細(xì)的信息。您在運(yùn)行上述示例時(shí)捕獲的異常將包含帶有以下消息的 InnerException:
The type Derived was not expected. Use the XmlInclude or SoapInclude
attribute to specify types that are not known statically.
您可以通過(guò)直接檢查 InnerException 或者通過(guò)調(diào)用該異常的 ToString() 方法來(lái)得到此消息。下面的代碼片段演示了一個(gè)異常處理程序,它寫(xiě)出了在反序列化對(duì)象的過(guò)程中發(fā)生的所有異常中的信息:
public void SerializeContainer( XmlWriter writer, Container obj )
{
try
{
// Make sure even the construsctor runs inside a
// try-catch block
XmlSerializer ser = new XmlSerializer( typeof(Container));
ser.Serialize( writer, obj );
}
catch( Exception ex )
{
DumpException( ex );
}
}
public static void DumpException( Exception ex )
{
Console.WriteLine( "--------- Outer Exception Data ---------" );
WriteExceptionInfo( ex );
ex = ex.InnerException;
if( null != ex )
{
Console.WriteLine( "--------- Inner Exception Data ---------" );
WriteExceptionInfo( ex.InnerException );
ex = ex.InnerException;
}
}
public static void WriteExceptionInfo( Exception ex )
{
Console.WriteLine( "Message: {0}", ex.Message );
Console.WriteLine( "Exception Type: {0}", ex.GetType().FullName );
Console.WriteLine( "Source: {0}", ex.Source );
Console.WriteLine( "StrackTrace: {0}", ex.StackTrace );
Console.WriteLine( "TargetSite: {0}", ex.TargetSite );
}
聲明序列化類型
要解決上述示例中的問(wèn)題,您只需讀取 InnerException 的消息并實(shí)現(xiàn)建議的解決方案。傳遞給 Serialize 方法的對(duì)象圖中的一個(gè)字段引用了一個(gè)類型為 Derived 的對(duì)象,但并未將該字段聲明為序列化 Derived 類型的對(duì)象。盡管該對(duì)象圖在 .NET 類型系統(tǒng)中完全合法,但 XmlSerializer 的構(gòu)造函數(shù)在遍歷容器類型的字段時(shí),并不知道為 Derived 類型的對(duì)象創(chuàng)建了序列化代碼,這是因?yàn)樗鼪](méi)有找到對(duì) Derived 類型的引用。
要向 XmlSerializer 聲明其他字段和屬性類型,您擁有多種選擇。您可以通過(guò) XmlInclude 屬性(由異常消息提示)聲明基類上的派生類型,如下所示:
[System.Xml.Serialization.XmlInclude( typeof( Derived ) )]
public class Base
{
// ...
}
通過(guò)附加 XmlInclude 屬性,可以讓 XmlSerializer 在字段或?qū)傩员欢x為 Base 類型時(shí)序列化引用 Derived 類型對(duì)象的字段。
或者,您還可以僅在單個(gè)字段或?qū)傩陨下暶饔行ь愋?,而不是在基類上聲明派生類型。您可以?XmlElement、XmlAttribute 或 XmlArrayItem 屬性附加到字段,并且聲明該字段或?qū)傩钥梢砸玫念愋?。然后,XmlSerializer 的構(gòu)造函數(shù)會(huì)將序列化和反序列化這些類型所需的代碼添加到序列化類中。
讀取 StackTrace
InnerException 的 Message 屬性并不是唯一包含有價(jià)值信息的屬性。StackTrace 屬性傳達(dá)了更多有關(guān)錯(cuò)誤根源的詳細(xì)信息。在堆棧跟蹤的最頂端,您可以找到首先引發(fā)異常的方法的名稱。臨時(shí)程序集中的方法名稱對(duì)于序列化類遵循格式 Write_,對(duì)于反序列化類則遵循格式 Read_。在具有上述錯(cuò)誤命名空間的示例中,您可以看到異常源自名為 Read1_MyClass 的方法。稍后,我將向您說(shuō)明如何使用 Visual Studio 調(diào)試器設(shè)置斷點(diǎn)并單步執(zhí)行此方法。不過(guò),首先讓我們看一下圍繞反序列化 XML 文檔發(fā)生的常見(jiàn)問(wèn)題。
反序列化 XML 時(shí)發(fā)生的問(wèn)題
將 XML 文檔反序列化為對(duì)象圖不像將對(duì)象圖序列化為 XML 那樣容易出錯(cuò)。當(dāng)對(duì)象不十分匹配類型定義時(shí),XmlSerializer 會(huì)非常敏感,但如果反序列化的 XML 文檔不十分匹配對(duì)象,則它會(huì)非常寬容。對(duì)于與反序列化對(duì)象中的字段或?qū)傩圆粚?duì)應(yīng)的 XML 元素,XmlSerializer 不再引發(fā)異常,而只是簡(jiǎn)單地引發(fā)事件。如果您需要跟蹤反序列化的 XML 文檔與 XML 格式之間的匹配程度,則可以注冊(cè)這些事件的處理程序。然而,您不需要向 XmlSerializer 注冊(cè)事件處理程序以正確處理未映射的 XML 節(jié)點(diǎn)。
在反序列化過(guò)程中,只有幾種錯(cuò)誤條件會(huì)導(dǎo)致異常。最常見(jiàn)的條件有:
? |
根元素的名稱或其命名空間不匹配期望的名稱。
|
? |
枚舉數(shù)據(jù)類型呈現(xiàn)未定義的值。
|
? |
文檔包含非法 XML。
|
就像序列化的情形一樣,每當(dāng)發(fā)生問(wèn)題時(shí),Deserialize() 方法都會(huì)引發(fā)帶有以下消息的 InvalidOperation 異常
There is an error in XML document (, ).
該異常通常在 InnerException 屬性中包含真正的異常。InnerException 的類型隨讀取 XML 文檔時(shí)發(fā)生的實(shí)際錯(cuò)誤而有所不同。如果序列化程序無(wú)法用傳遞給構(gòu)造函數(shù)的類型、通過(guò) XmlInclude 屬性指定的類型或者在傳遞給 XmlSerializer 構(gòu)造函數(shù)的某個(gè)更為復(fù)雜的重載的 Type[] 中指定的類型來(lái)匹配文檔的根元素,則 InnerException 為 InvalidCastException。請(qǐng)記住,XmlSerializer 將查看 Qname(即元素的名稱)和命名空間,以確定要將文檔反序列化為哪個(gè)類。它們都必須匹配 .NET 類中的聲明,以便 XmlSerializer 正確標(biāo)識(shí)與文檔的根元素相對(duì)應(yīng)的類型。
讓我們看一個(gè)示例:
[XmlRoot( Namespace="urn:my-namespace" )]
public class MyClass
{
public string MyField;
}
反序列化以下 XML 文檔將導(dǎo)致異常,因?yàn)?MyClass 元素的 XML 命名空間并不像通過(guò) .NET 類上的 XmlRoot 屬性所聲明的那樣是 urn:my-namespace。
<MyClass> <MyField>Hello, World</MyField> </MyClass>
讓我們更進(jìn)一步地觀察一下該異常。異常 Message 比您從 Serialize() 方法中捕獲的消息更具描述性;至少它引用了文檔中導(dǎo)致 Deserialize() 失敗的位置。盡管如此,當(dāng)您處理大型 XML 文檔時(shí),查看文檔并確定錯(cuò)誤可能不會(huì)如此簡(jiǎn)單。InnerException 又一次提供了更好的信息。這一次,它顯示:
<MyClass xmlns=''> was not expected.
該消息仍然有一些模糊,但它的確向您指明了導(dǎo)致問(wèn)題的元素。您可以回頭仔細(xì)檢查一下 MyClass 類,并將元素名稱和 XML 命名空間與 .NET 類中的 XML 序列化屬性進(jìn)行比較。
反序列化無(wú)效的 XML
另一個(gè)經(jīng)常報(bào)告的問(wèn)題是無(wú)法反序列化無(wú)效的 XML 文檔。XML 規(guī)范禁止在 XML 文檔中使用某些控制字符。然而,有時(shí)您仍然會(huì)收到包含這些字符的 XML 文檔。正如您猜想的那樣,問(wèn)題暴露在 InvalidOperationException 中。盡管如此,在這種特殊情況下,InnerException 的類型是 XmlException。InnerException 的消息正中要害:
hexadecimal value <value>, is an invalid character
如果您通過(guò)將其 Normalization 屬性設(shè)置為 true 的 XmlTextReader 進(jìn)行反序列化,則可以避免此問(wèn)題。遺憾的是,ASP.NET Web 服務(wù)在內(nèi)部使用的 XmlTextReader 將其 Normalization 屬性設(shè)置為 false;也就是說(shuō),它將不會(huì)反序列化包含這些無(wú)效字符的 SOAP 消息。
來(lái)自構(gòu)造函數(shù)的異常
本文討論的最后一類問(wèn)題發(fā)生在 XmlSerializer 的構(gòu)造函數(shù)對(duì)傳入的類型進(jìn)行分析的時(shí)候。請(qǐng)記住,構(gòu)造函數(shù)將遞歸檢查類型層次結(jié)構(gòu)中的每個(gè)公共字段和屬性,以便創(chuàng)建用來(lái)處理序列化和反序列化的類。然后,它將即時(shí)編譯這些類,并加載得到的程序集。
在這一復(fù)雜的過(guò)程中,可能會(huì)發(fā)生許多不同的問(wèn)題:
? |
根元素的聲明類型或者由屬性或字段引用的類型不提供默認(rèn)的構(gòu)造函數(shù)。
|
? |
層次結(jié)構(gòu)中的某個(gè)類型實(shí)現(xiàn)了集合接口 Idictionary。
|
? |
執(zhí)行對(duì)象圖中某個(gè)類型的構(gòu)造函數(shù)或?qū)傩栽L問(wèn)器時(shí),需要提升安全權(quán)限。
|
? |
生成的序列化類的代碼無(wú)法編譯。
|
試圖向 XmlSerializer 構(gòu)造函數(shù)傳遞不可序列化的類型也會(huì)導(dǎo)致 InvalidOperationException,但這一次該異常不會(huì)包裝其他異常。Message 屬性包含對(duì)構(gòu)造函數(shù)拒絕傳入“類型”的原因的充分解釋。試圖序列化未實(shí)現(xiàn)不帶參數(shù)的構(gòu)造函數(shù)(默認(rèn)構(gòu)造函數(shù))的類的實(shí)例時(shí),將產(chǎn)生帶有以下 Message 的異常:
Test.NonSerializable cannot be serialized because it does not have a default public constructor.
另一方面,解決編譯錯(cuò)誤是非常復(fù)雜的。這些問(wèn)題暴露在帶有以下消息的 FileNotFoundException 中:
File or assembly name abcdef.dll, or one of its dependencies, was not found. File name: "abcdef.dll"
at System.Reflection.Assembly.nLoad( ... )
at System.Reflection.Assembly.InternalLoad( ... )
at System.Reflection.Assembly.Load(...)
at System.CodeDom.Compiler.CompilerResults.get_CompiledAssembly()
....
您可能不知道“找不到文件”異常與實(shí)例化序列化程序?qū)ο笾g有什么關(guān)系,但請(qǐng)記?。簶?gòu)造函數(shù)寫(xiě)入 C# 文件并試圖編譯這些文件。該異常的調(diào)用堆棧提供了一些有用的信息,為這種懷疑提供了依據(jù)。當(dāng) XmlSerializer 試圖加載由調(diào)用 System.Reflection.Assembly.Load 方法的 CodeDOM 生成的程序集時(shí),發(fā)生了該異常。該異常沒(méi)有提供有關(guān) XmlSerializer 根據(jù)推測(cè)要?jiǎng)?chuàng)建的程序集不存在的原因的解釋。通常,該程序集不存在的原因是編譯失敗,這是由于序列化屬性生成了 C# 編譯器無(wú)法編譯的代碼,但這種情況很少出現(xiàn)。
注 當(dāng) XmlSerializer 運(yùn)行時(shí)所屬的帳戶或安全環(huán)境無(wú)法訪問(wèn) temp 目錄時(shí),也會(huì)發(fā)生該錯(cuò)誤。
XmlSerializer 所引發(fā)的任何異常錯(cuò)誤消息都不包含實(shí)際的編譯錯(cuò)誤,甚至連 InnerException 也不包含實(shí)際的編譯錯(cuò)誤。這使得解決這些異常變得非常困難,直到 Chris Sells 發(fā)布了他的 XmlSerializerPrecompiler 工具。
XmlSerializerPreCompiler
XmlSerializer PreCompiler 是一個(gè)命令行程序,它執(zhí)行與 XmlSerializer 的構(gòu)造函數(shù)相同的步驟。它可分析類型,生成序列化類,并編譯這些類 — 因?yàn)樗患兇庠O(shè)計(jì)為故障排除工具,所以它可以安全地向控制臺(tái)寫(xiě)入任何編譯錯(cuò)誤。
該工具使用起來(lái)非常方便。您只需使該工具指向包含導(dǎo)致異常的類型的程序集,并指定要預(yù)編譯的類型。讓我們看一個(gè)示例。當(dāng)您將 XmlElement 或 XmlArrayItem 屬性附加到定義為交錯(cuò)數(shù)組的字段時(shí),會(huì)發(fā)生一個(gè)經(jīng)常報(bào)告的問(wèn)題,如下面的示例所示:
namespace Test
{
public class StringArray
{
[XmlElement( "arrayElement", typeof( string ) )]
public string [][] strings;
}
}
在為類型 Test.StringArray 實(shí)例化 XmlSerializer 對(duì)象時(shí),XmlSerializer 構(gòu)造函數(shù)會(huì)引發(fā) FileNotFoundException。如果您編譯該類并試圖序列化該類的實(shí)例,將得到 FileNotFoundException,但不會(huì)得到有關(guān)該問(wèn)題實(shí)質(zhì)的線索。XmlSerializerPreCompiler 可以為您提供缺少的信息。在我的示例中,StringArray 類被編譯為名為 XmlSer.exe 的程序集,并且我必須用下面的命令行運(yùn)行該工具:
XmlSerializerPreCompiler.exe XmlSer.exe Test.StringArray
第一個(gè)命令行參數(shù)指定了程序集,第二個(gè)參數(shù)定義了該程序集中要預(yù)編譯的類。該工具會(huì)向命令窗口寫(xiě)入大量信息。
圖
2. XmlSerializerPreCompiler
命令窗口輸出
需要查看的重要代碼行是具有編譯錯(cuò)誤的代碼行以及兩個(gè)與以下內(nèi)容類似的代碼行:
XmlSerializer-produced source:
C:\DOCUME~1\\LOCALS~1\Temp\.cs
現(xiàn)在,XmlSerializerPreCompiler 為我們提供了編譯錯(cuò)誤以及含有無(wú)法編譯的代碼的源文件的位置。
調(diào)試序列化代碼
通常情況下,XmlSerializer 會(huì)在不再需要序列化類的 C# 源文件時(shí)將其刪除。然而,有一個(gè)未經(jīng)證實(shí)的診斷開(kāi)關(guān),可用來(lái)指示 XmlSerializer 將這些文件保留在硬盤上。您可以在應(yīng)用程序的 .config 文件中設(shè)置此開(kāi)關(guān):
<?xml version="1.0" encoding="utf-8" ?> <configuration> <system.diagnostics> <switches> <add name="XmlSerialization.Compilation" value="4" /> </switches> </system.diagnostics> </configuration>
若此開(kāi)關(guān)出現(xiàn)在 .config 文件中,C# 源文件將保留在 temp 目錄中。如果您使用的計(jì)算機(jī)運(yùn)行 Windows 2000 或更高版本,則 temp 目錄的默認(rèn)位置是 \Documents and Settings\\LocalSettings\Temp 或 \Temp(對(duì)于在 ASP.NET 帳戶下運(yùn)行的 Web 應(yīng)用程序)。這些 C# 文件很容易丟失,因?yàn)樗鼈兊奈募雌饋?lái)非常奇怪并且是隨機(jī)生成的,例如:bdz6lq-t.0.cs。XmlSerializerPreCompiler 可設(shè)置該診斷開(kāi)關(guān),因此您可以在記事本或 Visual Studio 中打開(kāi)這些文件,以檢查 XmlSerializerPreCompiler 對(duì)其報(bào)告編譯錯(cuò)誤的代碼行。
您甚至可以逐句執(zhí)行這些臨時(shí)序列化類,因?yàn)樵\斷開(kāi)關(guān)還可以將含有調(diào)試符號(hào)的 .pdb 文件保留在硬盤上。如果您需要在序列化類中設(shè)置斷點(diǎn),則可以在 Visual Studio 調(diào)試器下運(yùn)行應(yīng)用程序。一旦您在輸出窗口中看到相關(guān)消息,表明應(yīng)用程序已經(jīng)從 temp 目錄中加載了具有這些奇特名稱的程序集,就可以打開(kāi)具有相應(yīng)名稱的 C# 文件,然后像在您自己的代碼中一樣設(shè)置斷點(diǎn)。
圖
3.
來(lái)自診斷開(kāi)關(guān)的編譯錯(cuò)誤輸出
在序列化類中設(shè)置斷點(diǎn)之后,您需要執(zhí)行代碼以調(diào)用 XmlSerializer 對(duì)象上的 Serialize() 或 Deserialize() 方法。
注 您只能調(diào)試序列化和反序列化,而不能調(diào)試在構(gòu)造函數(shù)中運(yùn)行的代碼生成過(guò)程。
通過(guò)在序列化類中單步執(zhí)行,您能夠查明每個(gè)序列化問(wèn)題。如果您要單步執(zhí)行 SOAP 消息的反序列化,則可以使用上述技巧,這是因?yàn)?ASP.NET Web 服務(wù)和 Web 服務(wù)代理是在 XmlSerializer 之上構(gòu)建的。您需要做的只是將診斷開(kāi)關(guān)添加到您的 config 文件中,然后在反序列化消息的類中設(shè)置斷點(diǎn)。如果 WSDL 在生成代理類時(shí)沒(méi)有準(zhǔn)確地反映消息格式,則我偶爾會(huì)使用上述技巧來(lái)判斷正確的序列化屬性集。
小結(jié)
這些提示應(yīng)該可以幫助您診斷 XmlSerializer 中的序列化問(wèn)題。您遇到的大多數(shù)問(wèn)題都源自 XML 序列化屬性的錯(cuò)誤組合,或源自與要反序列化的類型不匹配的 XML。序列化屬性控制序列化類的代碼生成,并且可能導(dǎo)致編譯錯(cuò)誤或運(yùn)行時(shí)異常。通過(guò)仔細(xì)地檢查由 XmlSerializer 引發(fā)的異常,可幫助您識(shí)別運(yùn)行時(shí)異常的根源。如果您需要進(jìn)一步診斷問(wèn)題,則可以使用 XmlSerializerPreCompiler 工具來(lái)幫助您查找編譯錯(cuò)誤。如果任一種方法都不能幫助您找到問(wèn)題的根源,則可以檢查自動(dòng)創(chuàng)建的序列化類的代碼,并在調(diào)試器中逐句執(zhí)行這些代碼。