在.net中序列化讀寫xml方法的總結--轉載過來學習學習

来源:https://www.cnblogs.com/ModBus/archive/2018/02/27/8477025.html
-Advertisement-
Play Games

原文章地址:http://www.cnblogs.com/fish-li/archive/2013/05/05/3061816.html 首先做個大概的總結,XML包括的元素有XmlElement,XmlAttribute ,InnerText。 閱讀目錄 開始 最簡單的使用XML的方法 類型定義與 ...


原文章地址:http://www.cnblogs.com/fish-li/archive/2013/05/05/3061816.html

首先做個大概的總結,XML包括的元素有XmlElement,XmlAttribute ,InnerText。

閱讀目錄

XML是一種很常見的數據保存方式,我經常用它來保存一些數據,或者是一些配置參數。 使用C#,我們可以藉助.net framework提供的很多API來讀取或者創建修改這些XML, 然而,不同人使用XML的方法很有可能並不相同。 今天我打算談談我使用XML的一些方法,供大家參考。

回到頂部

最簡單的使用XML的方法

由於.net framework針對XML提供了很多API,這些API根據不同的使用場景實現了不同層次的封裝, 比如,我們可以直接使用XmlTextReader、XmlDocument、XPath來取數XML中的數據, 也可以使用LINQ TO XML或者反序列化的方法從XML中讀取數據。 那麼,使用哪種方法最簡單呢?

我個人傾向於使用序列化,反序列化的方法來使用XML。 採用這種方法,我只要考慮如何定義數據類型就可以了,讀寫XML各只需要一行調用即可完成。 例如:

// 1. 首先要創建或者得到一個數據對象
Order order = GetOrderById(123);


// 2. 用序列化的方法生成XML
string xml = XmlHelper.XmlSerialize(order, Encoding.UTF8);


// 3. 從XML讀取數據並生成對象
Order order2 = XmlHelper.XmlDeserialize<Order>(xml, Encoding.UTF8);

就是這麼簡單的事情,XML結構是什麼樣的,我根本不用關心, 我只關心數據是否能保存以及下次是否能將它們讀取出來。

說明:XmlHelper是一個工具類,全部源代碼如下: 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Serialization;
using System.IO;
using System.Xml;

// 此處代碼來源於博客【在.net中讀寫config文件的各種方法】的示例代碼
// http://www.cnblogs.com/fish-li/archive/2011/12/18/2292037.html

namespace MyMVC
{
    public static class XmlHelper
    {
        private static void XmlSerializeInternal(Stream stream, object o, Encoding encoding)
        {
            if( o == null )
                throw new ArgumentNullException("o");
            if( encoding == null )
                throw new ArgumentNullException("encoding");

            XmlSerializer serializer = new XmlSerializer(o.GetType());

            XmlWriterSettings settings = new XmlWriterSettings();
            settings.Indent = true;
            settings.NewLineChars = "\r\n";
            settings.Encoding = encoding;
            settings.IndentChars = "    ";

            using( XmlWriter writer = XmlWriter.Create(stream, settings) ) {
                serializer.Serialize(writer, o);
                writer.Close();
            }
        }

        /// <summary>
        /// 將一個對象序列化為XML字元串
        /// </summary>
        /// <param name="o">要序列化的對象</param>
        /// <param name="encoding">編碼方式</param>
        /// <returns>序列化產生的XML字元串</returns>
        public static string XmlSerialize(object o, Encoding encoding)
        {
            using( MemoryStream stream = new MemoryStream() ) {
                XmlSerializeInternal(stream, o, encoding);

                stream.Position = 0;
                using( StreamReader reader = new StreamReader(stream, encoding) ) {
                    return reader.ReadToEnd();
                }
            }
        }

        /// <summary>
        /// 將一個對象按XML序列化的方式寫入到一個文件
        /// </summary>
        /// <param name="o">要序列化的對象</param>
        /// <param name="path">保存文件路徑</param>
        /// <param name="encoding">編碼方式</param>
        public static void XmlSerializeToFile(object o, string path, Encoding encoding)
        {
            if( string.IsNullOrEmpty(path) )
                throw new ArgumentNullException("path");

            using( FileStream file = new FileStream(path, FileMode.Create, FileAccess.Write) ) {
                XmlSerializeInternal(file, o, encoding);
            }
        }

        /// <summary>
        /// 從XML字元串中反序列化對象
        /// </summary>
        /// <typeparam name="T">結果對象類型</typeparam>
        /// <param name="s">包含對象的XML字元串</param>
        /// <param name="encoding">編碼方式</param>
        /// <returns>反序列化得到的對象</returns>
        public static T XmlDeserialize<T>(string s, Encoding encoding)
        {
            if( string.IsNullOrEmpty(s) )
                throw new ArgumentNullException("s");
            if( encoding == null )
                throw new ArgumentNullException("encoding");

            XmlSerializer mySerializer = new XmlSerializer(typeof(T));
            using( MemoryStream ms = new MemoryStream(encoding.GetBytes(s)) ) {
                using( StreamReader sr = new StreamReader(ms, encoding) ) {
                    return (T)mySerializer.Deserialize(sr);
                }
            }
        }

        /// <summary>
        /// 讀入一個文件,並按XML的方式反序列化對象。
        /// </summary>
        /// <typeparam name="T">結果對象類型</typeparam>
        /// <param name="path">文件路徑</param>
        /// <param name="encoding">編碼方式</param>
        /// <returns>反序列化得到的對象</returns>
        public static T XmlDeserializeFromFile<T>(string path, Encoding encoding)
        {
            if( string.IsNullOrEmpty(path) )
                throw new ArgumentNullException("path");
            if( encoding == null )
                throw new ArgumentNullException("encoding");

            string xml = File.ReadAllText(path, encoding);
            return XmlDeserialize<T>(xml, encoding);
        }
    }
}





或許有人會說:我使用XPath從XML讀取數據也很簡單啊。
我認為這種說法有一個限制條件:只需要從XML中讀取少量的數據。
如果要全部讀取,用這種方法會寫出一大堆的機械代碼出來! 所以,我非常反感用這種方法從XML中讀取全部數據。

回到頂部

類型定義與XML結構的映射

如果是一個新項目,我肯定會毫不猶豫的使用序列化和反序列化的方法來使用XML, 然而,有時在維護一個老項目時,面對一堆只有XML卻沒有與之對應的C#類型時, 我們就需要根據XML結構來逆向推導C#類型,然後才能使用序列化和反序列化的方法。 逆向推導的過程是麻煩的,不過,類型推導出來之後,後面的事情就簡單多了。

為了學會根據XML結構逆向推導類型,我們需要關註一下類型定義與XML結構的映射關係。
註意:有時候我們也會考慮XML結構對於傳輸量及可閱讀性的影響,所以關註一下XML也是有必要的。

這裡有一個XML文件,是我從Visual Sutdio的安裝目錄中找到的: 

<DynamicHelp xmlns="http://msdn.microsoft.com/vsdata/xsd/vsdh.xsd" 
            xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
            xsi:schemaLocation="http://msdn.microsoft.com/vsdata/xsd/vsdh.xsd vsdh.xsd">
    <LinkGroup ID="sites" Title="Venus Sites" Priority="1500">
        <Glyph Collapsed="3" Expanded="4"/>
    </LinkGroup>
    <LinkGroup ID="Venus Private Forums" Title="Venus Private Forums" Priority="1400">
        <Glyph Collapsed="3" Expanded="4"/>
    </LinkGroup>
    <LinkGroup ID="ASP.NET Forums" Title="ASP.NET 1.0 Public Forums" Priority="1200">
        <Glyph Collapsed="3" Expanded="4"/>
    </LinkGroup>
    <Context>
        <Links>
            <LItem URL="http://www.asp.net/venus" LinkGroup="sites">Venus Home Page</LItem>
            <LItem URL="http://www.asp.net" LinkGroup="sites">ASP.NET Home Page</LItem>
            <LItem URL="http://www.asp.net/Forums/ShowForum.aspx?tabindex=1&amp;ForumID=77" 
                    LinkGroup="Venus Private Forums">General Discussions</LItem>
            <LItem URL="http://www.asp.net/Forums/ShowForum.aspx?tabindex=1&amp;ForumID=83" 
                    LinkGroup="Venus Private Forums">Feature Requests</LItem>
            <LItem URL="http://www.asp.net/Forums/ShowForum.aspx?tabindex=1&amp;ForumID=78" 
                    LinkGroup="Venus Private Forums">Bug Reports</LItem>
            <LItem URL="http://www.asp.net/Forums/ShowForum.aspx?tabindex=1&amp;ForumID=86" 
                    LinkGroup="Venus Private Forums">ASP.NET 2.0 Related issues</LItem>
            <LItem URL="http://www.asp.net/Forums/ShowForum.aspx?tabindex=1&amp;ForumID=11" 
                    LinkGroup="ASP.NET Forums">Announcements</LItem>
            <LItem URL="http://www.asp.net/Forums/ShowForum.aspx?tabindex=1&amp;ForumID=15" 
                    LinkGroup="ASP.NET Forums">Getting Started</LItem>
            <LItem URL="http://www.asp.net/Forums/ShowForum.aspx?tabindex=1&amp;ForumID=18" 
                    LinkGroup="ASP.NET Forums">Web Forms</LItem>
        </Links>
    </Context>
</DynamicHelp>

怎樣用反序列化的方式來讀取它的數據呢,我在博客的最後將給出完整的實現代碼。
現在,我們還是看一下這個XML有哪些特點吧。

<LinkGroup ID="sites" Title="Venus Sites" Priority="1500">

對於這個節點來說,它包含了三個數據項(屬性):ID,Title,Priority。 這樣的LinkGroup節點有三個。
類似的還有Glyph節點。

<LItem URL="http://www.asp.net" LinkGroup="sites">ASP.NET Home Page</LItem>

LItem節點除了與LinkGroup有著類似的數據(屬性)之外,還包含著一個字元串:ASP.NET Home Page , 這是另外一種數據的存放方式。

另外,LinkGroup和LItem都允許重覆出現,我們可以用數組或者列表(Array,List)來理解它們。

我還發現一些嵌套關係:LinkGroup可以包含Glyph,Context包含著Links,Links又包含了多個LItem。
不管如何嵌套,我發現數據都是包含在一個一個的XML節點中。

如果用專業的單詞來描述它們,我們可以將ID,Title,Priority這三個數據項稱為 XmlAttribute, LItem,LinkGroup節點稱為 XmlElement,”ASP.NET Home Page“出現的位置可以稱為 InnerText。 基本上,XML就是由這三類數據組成。

下麵我來演示如何使用這三種數據項。

回到頂部

使用 XmlElement

首先,我來定義一個類型:

public class Class1
{
    public int IntValue { get; set; }

    public string StrValue { get; set; }
}

下麵是序列化與反序列的調用代碼:

Class1 c1 = new Class1 { IntValue = 3, StrValue = "Fish Li" };
string xml = XmlHelper.XmlSerialize(c1, Encoding.UTF8);
Console.WriteLine(xml);

Console.WriteLine("---------------------------------------");

Class1 c2 = XmlHelper.XmlDeserialize<Class1>(xml, Encoding.UTF8);
Console.WriteLine("IntValue: " + c2.IntValue.ToString());
Console.WriteLine("StrValue: " + c2.StrValue);

運行結果如下:

<?xml version="1.0" encoding="utf-8"?>
<Class1 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <IntValue>3</IntValue>
    <StrValue>Fish Li</StrValue>
</Class1>
---------------------------------------
IntValue: 3
StrValue: Fish Li

結果顯示,IntValue和StrValue這二個屬性生成了XmlElement。

小結:預設情況下(不加任何Attribute),類型中的屬性或者欄位,都會生成XmlElement。

回到頂部

使用 XmlAttribute

再來定義一個類型:

public class Class2
{
    [XmlAttribute]
    public int IntValue { get; set; }

    [XmlElement]
    public string StrValue { get; set; }
}

註意,我在二個屬性上增加的不同的Attribute.

下麵是序列化與反序列的調用代碼: 

Class2 c1 = new Class2 { IntValue = 3, StrValue = "Fish Li" };
string xml = XmlHelper.XmlSerialize(c1, Encoding.UTF8);
Console.WriteLine(xml);

Console.WriteLine("---------------------------------------");

Class2 c2 = XmlHelper.XmlDeserialize<Class2>(xml, Encoding.UTF8);
Console.WriteLine("IntValue: " + c2.IntValue.ToString());
Console.WriteLine("StrValue: " + c2.StrValue);

運行結果如下(我將結果做了換行處理):

<?xml version="1.0" encoding="utf-8"?>
<Class2 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
        IntValue="3">
    <StrValue>Fish Li</StrValue>
</Class2>
---------------------------------------
IntValue: 3
StrValue: Fish Li

結果顯示:
1. IntValue 生成了XmlAttribute
2. StrValue 生成了XmlElement(和不加[XmlElement]的效果一樣,表示就是預設行為)。

小結:如果希望類型中的屬性或者欄位生成XmlAttribute,需要在類型的成員上用[XmlAttribute]來指出。

回到頂部

使用 InnerText

還是來定義一個類型:

public class Class3
{
    [XmlAttribute]
    public int IntValue { get; set; }

    [XmlText]
    public string StrValue { get; set; }
}

註意,我在StrValue上增加的不同的Attribute.

下麵是序列化與反序列的調用代碼: 

Class3 c1 = new Class3 { IntValue = 3, StrValue = "Fish Li" };
string xml = XmlHelper.XmlSerialize(c1, Encoding.UTF8);
Console.WriteLine(xml);

Console.WriteLine("---------------------------------------");

Class3 c2 = XmlHelper.XmlDeserialize<Class3>(xml, Encoding.UTF8);
Console.WriteLine("IntValue: " + c2.IntValue.ToString());
Console.WriteLine("StrValue: " + c2.StrValue);

運行結果如下(我將結果做了換行處理):

<?xml version="1.0" encoding="utf-8"?>
<Class3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
    IntValue="3">Fish Li</Class3>
---------------------------------------
IntValue: 3
StrValue: Fish Li

結果符合預期:StrValue屬性在增加了[XmlText]之後,生成了一個文本節點(InnerText)

小結:如果希望類型中的屬性或者欄位生成InnerText,需要在類型的成員上用[XmlText]來指出。

回到頂部

重命名節點名稱

看過前面幾個示例,大家應該能發現:通過序列化得到的XmlElement和XmlAttribute都與類型的數據成員或者類型同名。 然而有時候我們可以希望讓屬性名與XML的節點名稱不一樣,那麼就要使用【重命名】的功能了,請看以下示例:

[XmlType("c4")]
public class Class4
{
    [XmlAttribute("id")]
    public int IntValue { get; set; }

    [XmlElement("name")]
    public string StrValue { get; set; }
}

序列化與反序列的調用代碼前面已經多次看到,這裡就省略它們了。
運行結果如下(我將結果做了換行處理):

<?xml version="1.0" encoding="utf-8"?>
<c4 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
    id="3">
    <name>Fish Li</name>
</c4>
---------------------------------------
IntValue: 3
StrValue: Fish Li

看看輸出結果中的紅字粗體字,再看看類型定義中的三個Attribute的三個字元串參數,我想你能發現規律的。

小結:XmlAttribute,XmlElement允許接受一個別名用來控制生成節點的名稱,類型的重命名用XmlType來實現。

回到頂部

列表和數組的序列化

繼續看示例代碼:

Class4 c1 = new Class4 { IntValue = 3, StrValue = "Fish Li" };
Class4 c2 = new Class4 { IntValue = 4, StrValue = "http://www.cnblogs.com/fish-li/" };

// 說明:下麵二行代碼的輸出結果是一樣的。
List<Class4> list = new List<Class4> { c1, c2 };
//Class4[] list = new Class4[] { c1, c2 };

string xml = XmlHelper.XmlSerialize(list, Encoding.UTF8);
Console.WriteLine(xml);

// 序列化的結果,反序列化一定能讀取,所以就不再測試反序列化了。

運行結果如下:

<?xml version="1.0" encoding="utf-8"?>
<ArrayOfC4 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <c4 id="3">
        <name>Fish Li</name>
    </c4>
    <c4 id="4">
        <name>http://www.cnblogs.com/fish-li/</name>
    </c4>
</ArrayOfC4>

現在c4節點已經重覆出現了,顯然,是我們期待的結果。

不過,ArrayOfC4,這個節點名看起來太奇怪了,能不能給它也重命名呢?
繼續看代碼,我可以定義一個新的類型:

// 二種Attribute都可以完成同樣的功能。
//[XmlType("c4List")]
[XmlRoot("c4List")]
public class Class4List : List<Class4> { }

然後,改一下調用代碼:

Class4List list = new Class4List { c1, c2 };

運行結果如下:

<?xml version="1.0" encoding="utf-8"?>
<c4List xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <c4 id="3">
        <name>Fish Li</name>
    </c4>
    <c4 id="4">
        <name>http://www.cnblogs.com/fish-li/</name>
    </c4>
</c4List>

小結:數組和列表都能直接序列化,如果要重命名根節點名稱,需要創建一個新類型來實現。

回到頂部

列表和數組的做為數據成員的序列化

首先,還是定義一個類型:

public class Root
{
    public Class3 Class3 { get; set; }

    public List<Class2> List { get; set; }
}

序列化的調用代碼:

Class2 c1 = new Class2 { IntValue = 3, StrValue = "Fish Li" };
Class2 c2 = new Class2 { IntValue = 4, StrValue = "http://www.cnblogs.com/fish-li/" };

Class3 c3 = new Class3 { IntValue = 5, StrValue = "Test List" };

Root root = new Root { Class3 = c3, List = new List<Class2> { c1, c2 } };

string xml = XmlHelper.XmlSerialize(root, Encoding.UTF8);
Console.WriteLine(xml);

運行結果如下:

<?xml version="1.0" encoding="utf-8"?>
<Root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <Class3 IntValue="5">Test List</Class3>
    <List>
        <Class2 IntValue="3">
            <StrValue>Fish Li</StrValue>
        </Class2>
        <Class2 IntValue="4">
            <StrValue>http://www.cnblogs.com/fish-li/</StrValue>
        </Class2>
    </List>
</Root>

假設這裡需要為List和Class2的節點重命名,該怎麼辦呢?
如果繼續使用前面介紹的方法,是行不通的。

下麵的代碼演示瞭如何重命名列表節點的名稱:

public class Root
{
    public Class3 Class3 { get; set; }

    [XmlArrayItem("c2")]
    [XmlArray("cccccccccccc")]
    public List<Class2> List { get; set; }
}

序列化的調用代碼與前面完全一樣,得到的輸出結果如下:

<?xml version="1.0" encoding="utf-8"?>
<Root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <Class3 IntValue="5">Test List</Class3>
    <cccccccccccc>
        <c2 IntValue="3">
            <StrValue>Fish Li</StrValue>
        </c2>
        <c2 IntValue="4">
            <StrValue>http://www.cnblogs.com/fish-li/</StrValue>
        </c2>
    </cccccccccccc>
</Root>

想不想把cccccccccccc節點去掉呢(直接出現c2節點)?
下麵的類型定義方式實現了這個想法:

public class Root
{
    public Class3 Class3 { get; set; }

    [XmlElement("c2")]
    public List<Class2> List { get; set; }
}

輸出結果如下:

<?xml version="1.0" encoding="utf-8"?>
<Root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <Class3 IntValue="5">Test List</Class3>
    <c2 IntValue="3">
        <StrValue>Fish Li</StrValue>
    </c2>
    <c2 IntValue="4">
        <StrValue>http://www.cnblogs.com/fish-li/</StrValue>
    </c2>
</Root>

小結:數組和列表都在序列化時,預設情況下會根據類型中的數據成員名稱生成一個節點, 列表項會生成子節點,如果要重命名,可以使用[XmlArrayItem]和[XmlArray]來實現。 還可以直接用[XmlElement]控制不生成列表的父節點。

回到頂部

類型繼承與反序列化

列表元素可以是同一種類型,也可以不是同一種類型(某個類型的派生類)。
例如下麵的XML:

<?xml version="1.0" encoding="utf-8"?>
<XRoot xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <List>
        <x1 aa="1" bb="2" />
        <x1 aa="3" bb="4" />
        <x2>
            <cc>ccccccccccc</cc>
            <dd>dddddddddddd</dd>
        </x2>
    </List>
</XRoot>

想像一下,上面這段XML是通過什麼類型得到的呢?

答案如下(註意紅色粗體部分):

public class XBase { }

[XmlType("x1")]
public class X1 : XBase
{
    [XmlAttribute("aa")]
    public int AA { get; set; }

    [XmlAttribute("bb")]
    public int BB { get; set; }
}

[XmlType("x2")]
public class X2 : XBase
{
    [XmlElement("cc")]
    public string CC { get; set; }

    [XmlElement("dd")]
    public string DD { get; set; }
}

public class XRoot
{
    [XmlArrayItem(typeof(X1)),
    XmlArrayItem(typeof(X2))]
    public List<XBase> List { get; set; }
}

序列化代碼:

X1 x1a = new X1 { AA = 1, BB = 2 };
X1 x1b = new X1 { AA = 3, BB = 4 };
X2 x2 = new X2 { CC = "ccccccccccc", DD = "dddddddddddd" };
XRoot root = new XRoot { List = new List<XBase> { x1a, x1b, x2 } };

string xml = XmlHelper.XmlSerialize(root, Encoding.UTF8);
Console.WriteLine(xml);

小結:同時為列表成員指定多個[XmlArrayItem(typeof(XXX))]可實現多種派生類型混在一起輸出。

回到頂部

反序列化的實戰演練

接下來,我們將根據前面介紹的知識點,用反序列化的方法來解析本文開頭處貼出的那段XML: 

<DynamicHelp xmlns="http://msdn.microsoft.com/vsdata/xsd/vsdh.xsd" 
            xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
            xsi:schemaLocation="http://msdn.microsoft.com/vsdata/xsd/vsdh.xsd vsdh.xsd">
    <LinkGroup ID="sites" Title="Venus Sites" Priority="1500">
        <Glyph Collapsed="3" Expanded="4"/>
    </LinkGroup>
    <LinkGroup ID="Venus Private Forums" Title="Venus Private Forums" Priority="1400">
        <Glyph Collapsed="3" Expanded="4"/>
    </LinkGroup>
    <LinkGroup ID="ASP.NET Forums" Title="ASP.NET 1.0 Public Forums" Priority="1200">
        <Glyph Collapsed="3" Expanded="4"/>
    </LinkGroup>
    <Context>
        <Links>
            <LItem URL="http://www.asp.net/venus" LinkGroup="sites">Venus Home Page</LItem>
            <LItem URL="http://www.asp.net" LinkGroup="sites">ASP.NET Home Page</LItem>
            <LItem URL="http://www.asp.net/Forums/ShowForum.aspx?tabindex=1&amp;ForumID=77" 
                    LinkGroup="Venus Private Forums">General Discussions</LItem>
            <LItem URL="http://www.asp.net/Forums/ShowForum.aspx?tabindex=1&amp;ForumID=83" 
                    LinkGroup="Venus Private Forums">Feature Requests</LItem>
            <LItem URL="http://www.asp.net/Forums/ShowForum.aspx?tabindex=1&amp;ForumID=78" 
                    LinkGroup="Venus Private Forums">Bug Reports</LItem>
            <LItem URL="http://www.asp.net/Forums/ShowForum.aspx?tabindex=1&amp;ForumID=86" 
                    LinkGroup="Venus Private Forums">ASP.NET 2.0 Related issues</LItem>
            <LItem URL="http://www.asp.net/Forums/ShowForum.aspx?tabindex=1&amp;ForumID=11" 
                    LinkGroup="ASP.NET Forums">Announcements</LItem>
            <LItem URL="http://www.asp.net/Forums/ShowForum.aspx?tabindex=1&amp;ForumID=15" 
                    LinkGroup="ASP.NET Forums">Getting Started</LItem>
            <LItem URL="http://www.asp.net/Forums/ShowForum.aspx?tabindex=1&amp;ForumID=18" 
                    LinkGroup="ASP.NET Forums">Web Forms</LItem>
        </Links>
    </Context>
</DynamicHelp>

那段XML的根元素是DynamicHelp,因此,我們需要定義一個類型,類名為DynamicHelp。
再觀察那段XML,它應該包含一個LinkGroup列表,和一個Context屬性,所以可以這樣定義這三個類型:

public class DynamicHelp
{
    [XmlElement]
    public List<LinkGroup> Groups { get; set; }

    public Context Context { get; set; }
}

public class LinkGroup { }

public class Context { }

再來看LinkGroup,它包含三個數據成員,以及一個子節點:Glyph,因此可以這樣定義它們:

public class LinkGroup 
{
    [XmlAttribute]
    public string ID { get; set; }
    [XmlAttribute]
    public string Title { get; set; }
    [XmlAttribute]
    public int Priority { get; set; }

    public Glyph Glyph { get; set; }
}

public class Glyph
{
    [XmlAttribute]
    public int Collapsed { get; set; }
    [XmlAttribute]
    public int Expanded { get; set; }
}

LItem節點也簡單,它就包含了URL,LinkGroup和一個文本節點,因此可以這樣定義它:

public class LItem
{
    [XmlAttribute]
    public string URL { get; set; }
    [XmlAttribute]
    public string LinkGroup { get; set; }

    [XmlText]
    public string Title { get; set; }
}

Context節點也不複雜,就只包含了一個LItem列表,因此可以這樣定義它:

public class Context 
{
    public List<LItem> Links { get; set; }
}

好了,類型都定義好了,再來試試反序列化:

DynamicHelp help = XmlHelper.XmlDeserializeFromFile<DynamicHelp>("Links.xml", Encoding.UTF8);

foreach( LinkGroup group in help.Groups )
    Console.WriteLine("ID: {0}, Title: {1}, Priority: {2}, Collapsed: {3}, Expanded: {4}",
        group.ID, group.Title, group.Priority, group.Glyph.Collapsed, group.Glyph.Expanded);

foreach( LItem item in help.Context.Links )
    Console.WriteLine("URL: {0}, LinkGroup: {1}, Title: {2}",
        item.URL.Substring(0, 15), item.LinkGroup, item.Title);

屏幕顯示:

未處理的異常:  System.InvalidOperationException: XML 文檔(4, 2)中有錯誤。 
---> System.InvalidOperationException: 不應有 
<DynamicHelp xmlns='http://msdn.microsoft.com/vsdata/xsd/vsdh.xsd'>。

哦,拋異常了。
別急,看看異常說什麼。
好像是在說命名空間不能識別。
根據異常的描述,我還要修改一下DynamicHelp的定義,改成這樣:

[XmlRoot(Namespace = "http://msdn.microsoft.com/vsdata/xsd/vsdh.xsd")]
public class DynamicHelp

再次運行,結果如下:

ID: sites, Title: Venus Sites, Priority: 1500, Collapsed: 3, Expanded: 4
ID: Venus Private Forums, Title: Venus Private Forums, Priority: 1400, Collapsed: 3, Expanded: 4
ID: ASP.NET Forums, Title: ASP.NET 1.0 Public Forums, Priority: 1200, Collapsed: 3, Expanded: 4
URL: http://www.asp., LinkGroup: sites, Title: Venus Home Page
URL: http://www.asp., LinkGroup: sites, Title: ASP.NET Home Page
URL: http://www.asp., LinkGroup: Venus Private Forums, Title: General Discussions
URL: http://www.asp., LinkGroup: Venus Private Forums, Title: Feature Requests
URL: http://www.asp., LinkGroup: Venus Private Forums, Title: Bug Reports
URL: http://www.asp., LinkGroup: Venus Private Forums, Title: ASP.NET 2.0 Related issues
URL: http://www.asp., LinkGroup: ASP.NET Forums, Title: Announcements
URL: http://www.asp., LinkGroup: ASP.NET Forums, Title: Getting Started
URL: http://www.asp., LinkGroup: ASP.NET Forums, Title: Web Forms

小結:根據XML結構推導類型時,要保證類型的層次結構與XML匹配, 數據的存放方式可以通過[XmlElement],[XmlAttribute],[XmlText]方式來指出。

回到頂部

反序列化的使用總結

如果XML是由類型序列化得到那的,那麼反序列化的調用代碼是很簡單的,
反之,如果要面對一個沒有類型的XML,就需要我們先設計一個(或者一些)類型出來,
這是一個逆向推導的過程,請參考以下步驟:
1. 首先要分析整個XML結構,定義與之匹配的類型,
2. 如果XML結構有嵌套層次,則需要定義多個類型與之匹配,
3. 定義具體類型(一個層級下的XML結構)時,請參考以下表格。

XML形式 處理方法 補充說明
XmlElement 定義一個屬性 屬性名與節點名字匹配
XmlAttribute [XmlAttribute] 加到屬性上  
InnerText [XmlText] 加到屬性上 一個類型只能使用一次
節點重命名 根節點:[XmlType("testClass")]
元素節點:[XmlElement("name")]
屬性節點:[XmlAttribute("id")]
列表子元素節點:[XmlArrayItem("Detail")]
列表元素自身:[XmlArray("Items")]
 
回到頂部

排除不需要序列化的成員

預設情況下,類型的所有公開的數據成員(屬性,欄位)在序列化時都會被輸出, 如果希望排除某些成員,可以用[XmlIgnore]來指出,例如:

public class TestIgnore
{
    [XmlIgnore]    // 這個屬性將不會參與序列化
    public int IntValue { get; set; }

    public string StrValue { get; set; }

    public string Url;
}

序列化調用代碼:

TestIgnore c1 = new TestIgnore { IntValue = 3, StrValue = "Fish Li" };
c1.Url = "http://www.cnblogs.com/fish-li/";

string xml = XmlHelper.XmlSerialize(c1, Encoding.UTF8);
Console.WriteLine(xml);

輸出結果如下:

<?xml version="1.0" encoding="utf-8"?>
<TestIgnore xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <Url>http://www.cnblogs.com/fish-li/</Url>
    <StrValue>Fish Li</StrValue>
</TestIgnore>
回到頂部

強制指定成員的序列化順序

前面的示例很奇怪,我明明先定義的StrValue,後定義的Url,可是在輸出時的順序並是我期望的。
如果你希望控制序列化的輸出順序,可以參考下麵的示例代碼(註意紅色粗體文字):

public class TestIgnore
{
    [XmlIgnore]    // 這個屬性將不會參與序列化
    public int IntValue { get; set; }

    [XmlElement(Order = 1)]
    public string StrValue { get; set; }

    [XmlElement(Order = 2)]
    public string Url;
}

最終的輸出結果如下:

<?xml version="1.0" encoding="utf-8"?>
<TestIgnore xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <StrValue>Fish Li</StrValue>
    <Url>http://www.cnblogs.com/fish-li/</

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • jdk1.8.0_144 Map是Java三種集合中的一種位於java.util包中,Map作為一個介面存在定義了這種數據結構的一些基礎操作,它的最終實現類有很多:HashMap、TreeMap、SortedMap等等,這些最終的子類大多有一個共同的抽象父類AbstractMap。在Abstract ...
  • 1.隊列(queue) 用法: 作用: 1)解耦 2)提高效率 class queue.Queue(maxsize=0) #先入先出class queue.LifoQueue(maxsize=0) #後進先出 class queue.PriorityQueue(maxsize=0) #存儲數據時可設 ...
  • 上篇博文對Spring的工作原理做了個大概的介紹,想看的同學請出門左轉。今天詳細說幾點。 (一)Spring IoC容器及其實例化與使用 Spring IoC容器負責Bean的實例化、配置和組裝工作有兩個介面:BeanFactory和ApplicationContext。其中ApplicationC ...
  • 個人對selenium的理解: 1.使用selenium操作瀏覽器,實際上是使用selenium框架下的webdriver啟動各瀏覽器的驅動實現對瀏覽器的操作的。以下兩句代碼即可啟動firefox瀏覽器驅動 from selenium import webdriver brower=webdrive ...
  • 以前寫過一個MVC框架,封裝的有點low,經過一段時間的沉澱,打算重新改造下,之前這篇文章封裝過一個驗證碼類。 這次重新改造MVC有幾個很大的收穫 >全部代碼都是用Ubuntu+Vim編寫,以前都是windows上開發,這次徹底迷上Ubuntu Linux >裸裝php,用php自帶的伺服器解釋執行 ...
  • 原理:根據IP,資源ID,時間戳,一次性Access_Token,APPKEY(暴露在前臺)和APPSERECT(後臺)來生成參數,具體見下麵: 瀏覽器請求頁面=>後臺引用防盜鏈代碼=>生成Access_Token並綁定訪問IP=>生成sign=>生成網址 如:原圖片頁面為http://xxx.ex ...
  • 十分鐘幫助大家理解Java中的動態代理,什麼是動態代理?感興趣的小伙伴們可以參考一下 十分鐘幫助大家理解Java中的動態代理,什麼是動態代理?感興趣的小伙伴們可以參考一下 若代理類在程式運行前就已經存在,那麼這種代理方式被成為 靜態代理 ,這種情況下的代理類通常都是我們在Java代碼中定義的。 通常 ...
  • 題目描述 一元 n 次多項式可用如下的表達式表示: 其中,aixi稱為 i 次項,ai 稱為 i 次項的繫數。給出一個一元多項式各項的次數和繫數,請按照如下規定的格式要求輸出該多項式: 1. 多項式中自變數為 x,從左到右按照次數遞減順序給出多項式。 2. 多項式中只包含繫數不為 0 的項。 3. ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...