XML 讀寫(XmlDocument、XPathNavigator、XDocument)


 

內存中的 XML 處理

       基於流的 XML 處理提供了最小的負載,但也只提供了最小的靈活性。在很多 XML 處理場景里,你不會在這么低的層次下工作。

       對內存中的 XML 的處理則更加方便,但沒有單一、標准的方式。如下所有的類都支持對 XML 的讀取和導航:

  • XmlDocument :它是 XML 數據的標准化接口,但對時間要求比較多。
  • XPathNavigator :它提供比 XML DOM 稍快、更有效的模型,並增強了一些搜索功能,但不能修改或保存 XML 。
  • XDocument :為處理 XML 提供一個更直觀和有效的 API。從技術上講,它是 LINQ to XML 的一部分,但即使沒有 LINQ 查詢,它也很有用。

 

XmlDocument

       XmlDocument 把信息保存為樹的節點。節點是 XML 文件的基本組成部分,它可以使一個元素、特性、注釋或者元素的一個值。

       為了挖掘樹的所有層次,下面這個示例使用了遞歸來操作 XmlDocument 類:

protected void Page_Load(object sender, EventArgs e)
{
    string xmlFile = Server.MapPath("DvdList.xml");
    XmlDocument doc = new XmlDocument();
    doc.Load(xmlFile); // doc.LoadXml() 可以接收一個XML格式的字符串
    string str = GetChildNodesDescr(doc.ChildNodes, 0);
    Response.Write(str);
}
 
private string GetChildNodesDescr(XmlNodeList nodeList, int level)
{
    string indent = "";
    for (int i = 0; i < level; i++)
    {
        indent += "";
    }
 
    StringBuilder str = new StringBuilder();
    foreach (XmlNode node in nodeList)
    {
        switch (node.NodeType)
        {
            case XmlNodeType.Comment:
                str.Append(indent);
                str.Append("Comment: <b>");
                str.Append(node.Value).Append("</b><br />");
                break;
            case XmlNodeType.Element:
                str.Append(indent);
                str.Append("Element: <b>").Append(node.Name).Append("</b><br />");
                break;
            case XmlNodeType.Text:
                str.Append(indent);
                str.Append(" - Value: <b>");
                str.Append(node.Value).Append("</b><br />");
                break;
            case XmlNodeType.XmlDeclaration:
                str.Append("XML Declaration: <b>").Append(node.Name);
                str.Append(" ").Append(node.Value).Append("</b><br />");
                break;
        }
 
        if (node.Attributes != null)
        {
            foreach (XmlAttribute attribute in node.Attributes)
            {
                str.Append(indent).Append(" - Attribute: <b>");
                str.Append(attribute.Name).Append("</b> - Value: <b>");
                str.Append(attribute.Value).Append("</b><br />");
            }
        }
 
        if (node.HasChildNodes) // node.ChildNodes.Count != 0
        {
            // level 作為遞歸傳遞的變量,進入新方法后自然會遞增
            // 因此這里一定不能用 (level++) 或 ++level 否則排版會亂
            str.Append(GetChildNodesDescr(node.ChildNodes, level+1));
        }
    }
    return str.ToString();
}

image_thumb[1]

       並非所有類型的節點都同時具備名稱和值。例如,對於元素 Title,它的名字就是 Title,不過它的值為空,因為它的值被保存到隨后的 Text 節點中了。

 

XPathNavigator

       XPathNavigator 類(在 System.Xml.XPath 命名空間里)和 XmlDocument 類的工作方式很相似。它把所有信息都加載到內存中並允許你在節點中移動。關鍵的區別是它使用基於游標的方式允許你使用 MoveToNext()之類的方法在 XML 數據間移動。每次 XPathNavigator 類只能定位到一個節點。

       可以使用 XmlDocument.CreateNavigator()方法從 XmlDocument 創建一個 XPathNavigator。

       下面是一個示例:

protected void Page_Load(object sender, EventArgs e)
{
    string xmlFile = Server.MapPath("DvdList.xml");
    XmlDocument doc = new XmlDocument();
    doc.Load(xmlFile);
 
    XPathNavigator xnav = doc.CreateNavigator();
    Response.Write(GetXNavDescr(xnav, 0));
}
 
// xnav 是定位在某一個節點上的游標,而不是節點的集合
private string GetXNavDescr(XPathNavigator xnav, int level)
{
    string indent = "";
    for (int i = 0; i < level; i++)
    {
        indent += "&nbsp;&nbsp;&nbsp;";
    }
 
    StringBuilder str = new StringBuilder();
    switch (xnav.NodeType)
    {
        case XPathNodeType.Comment:
            str.Append(indent);
            str.Append("Comment: <b>").Append(xnav.Value);
            str.Append("</b><br />");
            break;
        case XPathNodeType.Element:
            str.Append(indent);
            str.Append("Element: <b>").Append(xnav.Name);
            str.Append("</b><br />");
            break;            
        case XPathNodeType.Root:
            str.Append("<b>Root</b><br />");
            break;
        case XPathNodeType.Text:
            str.Append(indent);
            str.Append(" - Value: <b>").Append(xnav.Value);
            str.Append("</b><br />");
            break;
    }
 
    if (xnav.HasAttributes)
    {
        xnav.MoveToFirstAttribute();
        do
        {
            str.Append(indent);
            str.Append(" - Attribute: <b>").Append(xnav.Name);
            str.Append("</b> Value: <b>").Append(xnav.Value);
            str.Append("</b><br />");
        } while (xnav.MoveToNextAttribute());
        xnav.MoveToParent();
    }
 
    if (xnav.HasChildren)
    {
        xnav.MoveToFirstChild();
        do
        {
            str.Append(GetXNavDescr(xnav, level + 1));
        } while (xnav.MoveToNext());
        xnav.MoveToParent();
    }
    return str.ToString();
}

       效果和上個示例一樣。代碼中兩處 xnav.MoveToParent()是必須的,因為操作的是節點的游標,遞歸時傳遞的游標也只有一個,里層處理完不返回上一層的話,外層方法循環的條件里無法正確處理游標,導致數據丟失。

 

XDocument

       XDocument 是管理內存中 XML 所有功能的模型!與 XmlDocument 和 XPathNavigator 不同,它擅長構建 XML 內容。XmlDocument 使 XML 構建不必太復雜,而 XPathNavigator 則完全不支持。

       如果要以非線性的方式生成 XML ,例如需要把一系列元素寫入根元素,然后又要在這些元素里添加更多的信息,就必須使用 XDocument 這樣的內存類。

       與 XmlDocument 非常相似,但有一個區別,在 LINQ to XML 模型里,特性沒有被看做單獨的節點而是被看做附加到其他元素的 名稱/值 對。

       從技術層面而言,XDocument 是 LINQ 的一部分。它在 System.Xml.Linq 命名空間里,並且它是 .NET 3.5 中 System.Xml.Linq 程序集的一部分,使用這個類需要添加對該程序集的引用。

 

1. 使用 XDocument 創建 XML

       通過 XDocument 可以使用整潔和精確的代碼生成 XML 內容,用 XElement 類還可以創建一個不表示完整的 XML 內容。

       所有的 LINQ to XML 類都提供了有用的構造函數,它允許在同一步里對其創建和初始化:

XElement element = new XElement("Price", "23.99");

 

       保存的代碼甚至變得更加瘋狂,它們能夠在一行代碼里創建嵌套的節點樹。這兩個類(XDocument、XElement)包含最后一個接收參數數組(params)的構造函數。這個參數數組包含一套嵌套的節點。

       下面的示例創建一個有兩個嵌入元素以及它們內容的元素:

XElement element = new XElement("Starring",
        new XElement("Star", "Tom Hanks"),
        new XElement("Star", "Robin Wright")
);

 

       擴展上述的技術,我們創建一個完整的 XML 文檔,包括全部的元素、文本內容、特性、注釋:

private void WriteXmlWithXDocument()
{
    XDocument doc = new XDocument(
        new XDeclaration("1.0", "utf-8", "yes"),
        new XComment("Created: " + DateTime.Now.ToString()),
        new XElement("DvdList",
            new XElement("DVD",
                new XAttribute("ID", "1"),
                new XAttribute("Category", "Science Fiction"),
                new XElement("Title", "The Matrix"),
                new XElement("Director", "Larry Wachowski"),
                new XElement("Price", "18.74"),
                new XElement("Starring",
                    new XElement("Star", "Keanu Reeves"),
                    new XElement("Star", "Laurence Fishburne")
                )
            ),
 
            new XElement("DVD",
                new XAttribute("ID", "2"),
                new XAttribute("Category", "Drama"),
                new XElement("Title", "Forrest Gump"),
                new XElement("Director", "Robert Zemeckis"),
                new XElement("Price", "23.99"),
                new XElement("Starring",
                    new XElement("Star", "Tom Hanks"),
                    new XElement("Star", "Robin Wright")
                )
            ),
 
            new XElement("DVD",
                new XAttribute("ID", "3"),
                new XAttribute("Category", "Horror"),
                new XElement("Title", "The Others"),
                new XElement("Director", "Alejandro Amen&aacute;bar"),
                new XElement("Price", "22.49"),
                new XElement("Starring",
                    new XElement("Star", "Nicole Kidman"),
                    new XElement("Star", "Cristopher Eccleston")
                )
            ),
 
            new XElement("DVD",
                new XAttribute("ID", "4"),
                new XAttribute("Category", "Mystery"),
                new XElement("Title", "Mulholland Drive"),
                new XElement("Director", "David Lynch"),
                new XElement("Price", "25.74"),
                new XElement("Starring",
                    new XElement("Star", "Laura Harring")
                )
            ),
 
            new XElement("DVD",
                new XAttribute("ID", "5"),
                new XAttribute("Category", "Science Fiction"),
                new XElement("Title", "A.I. Artificial Intelligence"),
                new XElement("Director", "Steven Spielberg"),
                new XElement("Price", "23.99"),
                new XElement("Starring",
                    new XElement("Star", "Haley Joel Osment"),
                    new XElement("Star", "Jude Law")
                )
            )
        )
    );
 
    doc.Save(Server.MapPath("DvdList.xml"));
}

       這段代碼的作用和以前介紹的 XmlTextWrite 代碼的作用完全相同,但是它更短,更容易閱讀。它也比創建內存中的 XmlDocument 的等效代碼更簡單!另外,這樣書寫的代碼通過語句精確的縮進反映了 XML 元素的嵌套關系,這讓你迅速知道 XML 文檔的整體形狀。

結論:

       創建 XML 文檔的首選操作類就是 XDocument 。

 

2. 使用 XDocument 讀取流

       XDocument 還可以簡化對 XML 內容的讀取和導航,得到一個含有內容的 XDocument 之后,可以使用 XElement 類的主要屬性和方法深入節點樹。

  • XDocument.Load():從文件、URI 或流中讀取 XML 文檔
  • XDocument.Parse():從一個字符串加載 XML 文檔

XElement 類的核心方法:

Attributes() 獲取這個元素的 Attribute 對象的集合
Attribute() 獲取特定名稱的 Attribute
Elements() 獲取第一層的子 XElement 元素,或者你可以指定元素的名稱。
Element() 獲取由該元素包含的具有指定名稱的 XElement ,如果沒有匹配的就返回空值
Nodes() 獲取該元素包含的所有 XNode 對象,這包括元素以及其他內容,如注釋

       請注意 XDocument 和 XmlDocument 一個很重要的區別。XDocument 嵌套的元素通過方法而不是屬性來暴露,這讓你能夠過濾出你感興趣的那部分元素,當然,你也可以獲取所有元素。

 

       XDocument 類(以及其他 LINQ to XML 類)通常提供更多的成員:

  • 節點成員(FirstNode、LastNode、NextNode、PreviousNode、Parent)
  • 檢查是否有孩子(HasElements)、特性(HasAttributes)、內容(IsEmpty)
  • 插入、移除等其他操作( Add()、AddAfterSelf()AddBeforeSelf()、RemoveNodes()、Remove()、ReplaceWith()等)

       LINQ to XML 的另一個簡化是它不要求你區分元素和其中的文本,它們在 XML DOM 里由兩個單獨的節點表示。你可以通過把他轉換為相應的數據類型得到內部的值:

string title = (string)titleElement;
Decimal price = (Decimal)decimalElement;

       設置元素內的文本內容同樣簡單,只要把新值賦給 Value 屬性:

priceElement = (decimal)priceElement * 2;

       下面這段直觀的代碼重寫之前的 XPathNavigator 的 XML 處理代碼。它掃描可用的元素,並把標題、導演以及價格信息添加到列表里:

private string ReadXML()
{
    string xmlFile = Server.MapPath("DvdList.xml");
    XDocument doc = XDocument.Load(xmlFile);
 
    StringBuilder str = new StringBuilder();
    foreach (XElement element in doc.Element("DvdList").Elements())
    {
        // element.Element("Title").ToString() 出來的格式是
        // <Title>......</Title>,因此取值只能使用下面的轉換
        str.Append("<ul><b>");
        str.Append((string)element.Element("Title"));
        str.Append("</b><li>");
        str.Append((string)element.Element("Director"));
        str.Append("</li><li>");
        str.Append(string.Format("{0:C}", (decimal)element.Element("Price")));
        str.Append("</li></ul>");
    }
    return str.ToString();
}

 

3. 命名空間

       XDocument 類以特別優雅的方式處理命名空間。只要定義一個 XNameSpace 對象,然后使用它作為名稱的一部分創建 XElement 元素即可:

XNamespace ns = "http://www.somecompany.com/DvdList";
XDocument doc = new XDocument(
    new XDeclaration("1.0", "utf-8", "yes"),
    new XComment("Created: " + DateTime.Now.ToString()),
    new XElement(ns + "DvdList",
        new XElement(ns + "DVD",
            new XAttribute("ID", "1"),
            new XAttribute("Category", "Science Fiction"),
            new XElement(ns + "Title", "The Matrix"),
            new XElement(ns + "Director", "Larry Wachowski"),
            new XElement(ns + "Price", "18.74"),
            new XElement(ns + "Starring",
                new XElement(ns + "Star", "Keanu Reeves"),
                new XElement(ns + "Star", "Laurence Fishburne")
            )
        )
    )
    ......
);

       所有元素被放到了新的 XML 命名空間,但特性沒有,因為特性已經是附加到元素的了,所以沒必要特別再把特性放到同一個命名空間里。

 

       如果元素在 XML 命名空間里,對 XML 文檔進行遍歷的時候必須同時考慮命名空間:

XNamespace ns = "http://www.somecompany.com/DvdList";
string title = (string)element.Element(ns + "Title");


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM