程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> C#入門知識 >> XmlReader和XElement組合之讀取大型xml文檔,xmlreaderxelement

XmlReader和XElement組合之讀取大型xml文檔,xmlreaderxelement

編輯:C#入門知識

XmlReader和XElement組合之讀取大型xml文檔,xmlreaderxelement


簡介

在.NET framework 中存在大量操作xml數據的類庫和api,但在.NET framework 3.5後我們的首選一般就是linq to xml。

linq to xml操作xml數據無論是XElement.Load方法還是XElement.Parse方法都會將整個xml文件加載到內存中,在xml文件超級大的情況下linq to xml就不太適合。

對於大型的xml文件最好的方法就是每次只讀取一部分,這樣逐漸的讀取整個xml文件,這個剛好對應XmlReader類。

XmlReader使用起來效率高,但操作沒有linq to xml方便,所以就希望取兩者之長:既有效率使用起來也如linq to xml一樣方便。

思路

XElement類有一個方法ReadFrom,此方法接受一個XmlReader參數 : XNode.ReadFrom 方法 (XmlReader)

在上面的鏈接MSDN上,其實已經有了對應的組合方式了,而且名字也不錯:執行大型 XML 文檔的流式轉換

static IEnumerable<XElement> StreamXElements(string uri, string matchname)
{
    XmlReaderSettings settings = new XmlReaderSettings();
    settings.IgnoreComments = true;
    settings.IgnoreWhitespace = true;

    using (XmlReader reader = XmlReader.Create(uri, settings))
    {
        reader.MoveToContent();
        while (reader.Read())
        {
            switch (reader.NodeType)
            {
                case XmlNodeType.Element:
                    if (reader.Name == matchname)
                    {
                        XElement el = XElement.ReadFrom(reader) as XElement;
                        if (el != null)
                        {
                            yield return el;
                        }
                    }
                    break;
                       
            }
        }
    }
}

 以上代碼就是用XmlReader一直Read下去,然後碰到XmlNodeType.Element類型時就可以XElement.ReadFrom(reader)構建XElement,最重要的就是最後的yield return。

這樣目前為止,so far so good.

但在測試的時候,發現此方法有一個比較嚴重的bug,每次讀取一個XElement之後就會跳過一個XElement:

如以上的xml,在讀取第一個470002048節點之後,470002049節點就被跳過了。

這裡其實就是XmlReader不小心Read too far的一個問題,read too far其實就是多read了一次,可以這樣理解:

initial read;
(while "we're not at the end") {
    do stuff;
    read;
}

再回到我們上面的代碼,其實在XElement.ReadFrom(reader)構建XElement之後,內部已經read了一次,但在while語句中我們還是在reader,這樣下一個XElement是不會讀到的。

那知道原因之後,解決起來也簡單了,這裡就用reader.EOF 做判斷條件並去掉多余的一次read,具體代碼如下:

static IEnumerable<XElement> StreamXElements(string uri, string matchname)
{
    XmlReaderSettings settings = new XmlReaderSettings();
    settings.IgnoreComments = true;
    settings.IgnoreWhitespace = true;

    using (XmlReader reader = XmlReader.Create(uri, settings))
    {
        reader.MoveToContent();
        while (!reader.EOF)
        {
            if (reader.NodeType == XmlNodeType.Element
                && reader.Name == matchname)
            {
                XElement el = XElement.ReadFrom(reader) as XElement;
                if (el != null)
                {
                    yield return el;
                }
            }
            else
            {
                reader.Read();
            }
        }
    }
}

 總結

組合XmlReader和XElement的方式在MSDN中其實已經有了相應的文章介紹,但自己摸索的過程中還是有很多的收獲,參考文章如下:

http://stackoverflow.com/questions/2299632/why-does-xmlreader-skip-every-other-element-if-there-is-no-whitespace-separator

https://msdn.microsoft.com/en-us/library/mt693229.aspx

http://stackoverflow.com/questions/2441673/reading-xml-with-xmlreader-in-c-sharp

https://blogs.msdn.microsoft.com/xmlteam/2007/03/24/streaming-with-linq-to-xml-part-2/

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved