XmlReader和XElement组合之读取大型xml文档

简介

在.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/

时间: 2024-10-09 07:24:16

XmlReader和XElement组合之读取大型xml文档的相关文章

Java获取XML节点总结之读取XML文档节点

dom4j是Java的XML API,用来读写XML文件的.目前有很多场景中使用dom4j来读写xml的.要使用dom4j开发,需要下载导入dom4j相应的jar文件.官网下载:http://www.dom4j.org/dom4j-1.6.1/github下载:http://dom4j.github.io/下载解压之后如图所示: 我们只需要把dom4j-1.6.1.jar文件构建到我们开发项目中就可以了. 下面就以Eclipse创建java项目的构建方法为例说明:声明:本Java项目的开发环境J

java中XML操作:xml与string互转、读取XML文档节点及对XML节点增删改查

一.XML和String互转: 使用dom4j程式变得很简单 //字符串转XML String xmlStr = \"......\"; Document document = DocumentHelper.parseText(xmlStr); // XML转字符串 Document document = ...; String text = document.asXML(); //这里的XML DOCUMENT为org.dom4j.Document 二.读取XML文档节点: pack

.Net中使用xsd验证xml文档

在.NET中使用XSD可以方便的验证一个XML文档是否符合规范.这里的XSD本质上是一个xml文件,XSD相当于数据库中的表结构或者C#语言中的一个抽象类,它规定了被验证的目标xml的结构,如目标xml具有哪些节点,每个节点的顺序关系,每个节点的数据类型,每个节点的出现次数等信息. .NET中通过XmlReader和XmlReaderSettrings类可以方便的对XML进行验证,示例代码如下: 1         static void Main(string[] args) 2 { 3 //

xml文档的读取

xml文档有写入,当然就有读取了,在这里还是简单介绍一个读取的方法,就是利用XMLTextReader读取xml文档. XmlReader是一种快速.无缓冲,向前并只读的游标,用于读取xml文档,并且隐藏了底层数据交换的复杂性,XMLReader最主要的一个就是他更易使用,其次性能更高,难度更低. 1 public string ReadXml(string path) 2 { 3 //由于要进行大量的字符串的拼接,所以先创建StringBuilder对象 4 StringBuilder sb

用C#读取XML文档

本文将以一个非常简单的例子来说明如何使用C#访问一个XML文件并且读取其中的信息.例子本身并无任何实际意义,它只是简单的介绍了如何调用微软的XML标准以及如何运用到实际当中去.希望能够对初次接触C#或者未尝试过通过C#读取XML文件的读者有所启发.本文旨在抛砖引玉,希望能与更多的朋友交流和分享经验.    制作过程 1. 运行Visual Studio.NET,新建Visual C#.NET工程,这里取名为ReadXML. 2. 在解决方案资源管理器中,将Form1.cs改名为frmAuthor

读取带命名空间的Xml文档的方法备忘

很久没有做xml方面的解析,今天接到一个小任务,需要解析xml里面的字段内容,顺带复习了一下xpath方面的知识.需要解析的xml是那种待namespace的文档,一开始解析就碰到问题,我就感觉是命名空间在作怪,印象中之前也处理过这种问题,但时间久远,都忘记了,也没有记录下来. 折腾了一会总算找到了方法,.NET中读取带xml命名空间的xml文档的方式确实麻烦,不够直接.不管怎样,还是记录一下解决方法,省得以后又要被折腾一次. StringReader textReader = new Stri

详解xml文件描述,读取方法以及将对象存放到xml文档中,并按照指定的特征寻找的方案

主要的几个功能: 1.完成多条Emp信息的XML描述2.读取XML文档解析Emp信息3.将Emp(存放在List中)对象转换为XML文档4.在XML文档中查找指定特征的Emp信息 dom4j,jaxen 官网下载页面: http://sourceforge.net/projects/dom4j/files/dom4j-2.0.0-ALPHA-2/ 也可以在网盘上面下载:http://yunpan.cn/cwaNde7UYN83d  提取码 e247 1 完成多条Emp信息的XML描述 1.1 问

从多个XML文档中读取数据用于显示webapi帮助文档

前言: 你先得知道HelpPageConfig文件,不知道说明你现在不需要这个,所以下文就不用看了,等知道了再看也不急.当然如果你很知道这个,下文也不用看了,因为你会了. 方法一: new XmlDocumentationProvider(HttpContext.Current.Server.MapPath("~/App_Data/Documentation.xml")) 替换成 new XmlDocumentationProvider("PluginsFolder/*.xm

对xml文档数据的读取

在短暂的C#程序开发过程中,我发现使用xml文档来存储一些数据还是很不错的!当然有了数据当然要读取,在开发过程我遇到了两种对xml文档数据进行读取的方法. 就是最基本的利用System.Xml命名空间,通过各个节点来获得xml中的数据. 例如下面的Xml数据: <?xml version="1.0"?> <BookStore> <book> <title>C#入门经典</title> <author>Karli W