XmlSerializer 序列化總結
序列化反序列化例程 參考微軟官網,所有相關問題理論上都能在裡面找到原因。注意裡面說到XmlSerializer new的時候只有倆種方式才不會發生記憶體洩漏。其餘會出現dll不釋放的記憶體洩漏。
XmlSerializer(Type)
XmlSerializer.XmlSerializer(Type, String)
1.節點
碰到的問題,就是1)類節點要重新命名 ,後面找到“XmlTypeAttribute”;2)陣列定義物件不想陣列名佔用節點,找到“XmlElementAttribute”;3)類定義預設節點名稱,使用“XmlTypeAttribute”
節點/屬性重新命名,一般方式是 XmlXXXX(XXXXName=“新的節點名”)
節點參考“Tozhang”的部落格
XmlElement節點重新命名
XmlRoot 根節點重名稱
XmlArrayList集合新增根節點
XmlArrayItemList集合中子節點重新命名
[Serializable] 將該類標記為可以序列化類
[XmlRoot(“root”)]可以指定重新指定xml根節點的名稱,若不加這特性,此類在序列化時候,會預設使用類名作為根節點
[XmlRootAttribute("Slab", IsNullable = false)]
[XmlElement(“code”)]
[XmlIgnore] 此特性是忽略此屬性
[XmlAttribute(“attr”)] 此屬性會作為特性在 元素中
[XmlTypeAttribute(TypeName = "Segment")] //設定類的節點名稱 是“Segment” public class SegmentXXX {
……
}
[XmlRootAttribute("MyCity", Namespace="abc.abc", IsNullable=false)] // 當該類為Xml根節點時,以此為根節點名稱。
public class City
[XmlAttribute("AreaName")] // 表現為Xml節點屬性。<... AreaName="..."/>
public string Name
[XmlElementAttribute("AreaId", IsNullable = false)] // 表現為Xml節點。<AreaId>...</AreaId>
public string Id
[XmlArrayAttribute("Areas")] // 表現為Xml層次結構,根為Areas,其所屬的每個該集合節點元素名為類名。<Areas><Area ... /><Area ... /></Areas>
public Area[] Areas
[XmlElementAttribute("Area", IsNullable = false)] // 表現為水平結構的Xml節點。<Area ... /><Area ... />... 對比上面就是少掉“Areas”節點,少一次縮排。
public Area[] Areas
[XmlIgnoreAttribute] // 忽略該元素的序列化。
2.改變XML序列化的預設值
參考“yubinfeng”部落格。部落格描述很詳細,作為第二個示例
遇到問題,要去除開頭的xml語句,並去掉根節點的xml名空間宣告。
/// <summary> /// 返回去除節點頭部的 /// </summary> /// <param name="filename"></param> /// <param name="xtw"></param> /// <param name="xmlSN"></param> private void ReturnXmlWrite(string filename, out XmlWriter xtw, out XmlSerializerNamespaces xmlSN) { //設定序序化XML格式 MemoryStream ms = new MemoryStream(); XmlWriterSettings xws = new XmlWriterSettings(); xws.Indent = true; //獲取或設定一個值,該值指示是否縮排元素。 xws.OmitXmlDeclaration = true;//獲取或設定一個值,該值指示是否省略XML宣告。 xws.Encoding = System.Text.Encoding.UTF8;//設定編碼,讀取的時候同樣編碼,可以省略xml帶編碼行 xtw = XmlTextWriter.Create(filename, xws); //去掉要結點的 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" 屬性 xmlSN = new XmlSerializerNamespaces(); xmlSN.Add("", ""); }
3.反射錯誤
執行的時候一直報反射錯誤。查很久,發現是“同名屬性錯誤”
/// <summary> /// 平板周長定義,必須。逆時針相對於平板邊界框的左下角給出座標 /// </summary> [XmlTypeAttribute(TypeName = "Zone")] public class MainZone { [XmlElement(ElementName = "Zone")] public Zone unPickZone; public SlabZone() { segments = new Segment[4]; for (int i = 0; i < 4; i++) segments[i] = new Segment(); unPickZone = new Zone(); } }
上面程式碼在執行 = new XmlSerializer(Type) 會彈窗報錯。
原因是 "MainZone "關聯的屬性名“Zone” 和 成員物件的類“Zone”衝突。
更改方法:倆個裡面挑一個重新命名為其它。
4. 注意事項
從yubinfeng轉載過來。
XML序列化一些注意事項
(1)要序列化的類必須有預設的構造的建構函式,才能使用XmlSerializer序列化,需要序列化的類都必須有一個無參的建構函式(通過對基礎中類和類的例項學習,我們必須知道類不定義建構函式的情況下,會預設生成一個無引數的建構函式);
補充:如果變數只宣告,沒有賦值,序列化後是沒有對應的節點和屬性值。
(2)索引器、私有欄位或只讀屬性(只讀集合屬性除外)不能被序列化;
(3)不想序列化時:當不想序列化一個屬性時,使用[System.Xml.Serialization.XmlIgnore]標記,能用於屬性;[NonSerializable]應用於屬性無效,能用於類,結構體等;
(4)方法不能被序列化(雖然是廢話,但是還是列舉出來);
(5)列舉變數可序列化為字串,無需用[XmlInclude]
(6)匯出非基本型別物件,都必須用[XmlInclude]事先宣告。該規則遞迴作用到子元素 。可以參考spacer_robot
(7)Attribute中的IsNullable引數若等於false,表示若元素為null則不顯示該元素。(針對值型別有效)
(8)某些類就是無法XML序列化的(即使使用了[XmlInclude])
比如:
IDictionary(如HashTable);
父類物件賦予子類物件值的情況;
物件間迴圈引用;
(9)對於無法XML序列化的物件,可考慮:
使用自定義xml序列化(實現IXmlSerializable介面);
實現IDictionary的類,可考慮(1)用其它集合類替代;(2)用類封裝之,並提供Add和this函式;
某些型別需要先經過轉換,然後才能序列化為XML。如XML序列化System.Drawing.Color,可先用ToArgb()將其轉換為整數;
過於複雜的物件用xml序列化不便的話,可考慮用二進位制序列化;
(10)預設建構函式是必須的,因為反序列化本質上使用的是反射,需要預設建構函式來例項化類,如果去掉其中的預設建構函式,則編譯沒有問題,但執行就會報錯。
儘量不要將比較大的屬性放在預設建構函式初始化,那會導致在反序列化時對列表初始化兩次:預設建構函式中執行一次,反序列化時從XML文件讀取再執行一次。
以上十點注意,其中前三點,也就是加黑的這幾點,是重點要知道的。
5.引用
引用來源:1.https://www.cnblogs.com/yubinfeng/p/4631838.html
2.https://www.cnblogs.com/zhang1f/p/11666930.html
3.https://www.cnblogs.com/zsh_robot/articles/1323949.html
4.https://www.cnblogs.com/yukaizhao/archive/2011/07/22/xml-serialization.html 部落格裡面還有其它方式處理xml
5.https://docs.microsoft.com/en-us/dotnet/api/system.xml.serialization.xmlserializer?view=net-5.0 微軟官方文件