posts - 128,  comments - 280,  trackbacks - 0

上集回顾

    上两集都介绍了Linq to Xml的相关用法,需要注意的一点是Linq to Xml是in-memory的处理方式,所以有多少节点,就要消耗多少内存,如果这个xml很大,但系统内存却有限的情况下怎么办哪?

设置目标

    今天要做的是把某目录下的所有文件和目录输出到一个xml里面去,例如:

<?xml version="1.0" encoding="gb2312"?>
<
folder name="bin">
<
folder name="Debug">
<
file name="ConsoleApplication6.exe" />
<
file name="ConsoleApplication6.exe.config" />
<
file name="ConsoleApplication6.pdb" />
<
file name="ConsoleApplication6.vshost.exe" />
<
file name="ConsoleApplication6.vshost.exe.config" />
<
file name="ConsoleApplication6.vshost.exe.manifest" />
</
folder>
<
folder name="Release">
<
file name="ConsoleApplication6.exe" />
<
file name="ConsoleApplication6.exe.config" />
<
file name="ConsoleApplication6.pdb" />
<
file name="ConsoleApplication6.vshost.exe" />
<
file name="ConsoleApplication6.vshost.exe.config" />
<
file name="ConsoleApplication6.vshost.exe.manifest" />
</
folder>
</
folder>

    当然这个需要可以作用于c盘根目录,也就是需要处理相当多的文件和目录。

使用Linq to Xml尝试

    如果用前两集中介绍的Linq to Xml来做,可以很快地写出:

var di = new DirectoryInfo("..");
XDocument doc = new XDocument(GetFolderContent(di));
doc.Save(Console.Out);

    以及:

static XElement GetFolderContent(DirectoryInfo di)
{
return new XElement("folder",
new XAttribute("name", di.Name),
from subDir in di.GetDirectories()
select GetFolderContent(subDir),
from file in di.GetFiles()
select new XElement("file", new XAttribute("name", file.Name)));
}

    但是,需要注意一点,Linq to Xml是in-memory的方式工作的,也就是如果有1000级的目录嵌套,每级有100个子目录,那么就有100^1000个XElement在内存中创建出来,好吧,这么计算一下,整个过程需要多少内存吧,10的2000次方*每一个XElement消耗的内存,就算只有1个Byte,1G内存也只能处理10的30次方,所以要处理完这个场景,说需要的多少内存可以说是不可能达到的。

分析

    上面的实现得益于Linq to Xml API的简易,但是却有受制于Linq to Xml的in-memory模型,要是有一种既可以受益于Linq to Xml的简易,又可以使用非in-memory的模型就两全其美了。

    天下有这么好的事情吗?先不要急于下定论,让我们来查查msdn吧:如何:执行大型 XML 文档的流式转换

    这里提到了一个XStreamingElement的类,仅仅从名称上,我们就可以知道,这个类是一个类似XElement的类型,但是它又是一个类似Stream的类,并不是in-memory的,当然具体怎么说还要看msdn

    msdn中明确说明:表示支持延迟流输出的 XML 树中的元素。

    而且在备注中说到:如果从输入源(如文本文件)进行流式处理,则可以读取非常大的文本文件,并生成非常大的 XML 文档,同时保持较小的内存需求量。

    也就是明确了XStreamingElement这个类本身就是(或类似)流式处理的,并不像普通的XElement的in-memory的处理方式。

实现

    找到了XStreamingElement这个Linq to Xml的另类API之后,就可以实现了前面的目标了,XStreamingElement的用法与XElement十分相似,因此只需要把前面的方法稍作修改即可:

static XStreamingElement GetFolderContent(DirectoryInfo di)
{
return new XStreamingElement("folder",
new XAttribute("name", di.Name),
from subDir in di.GetDirectories()
select GetFolderContent(subDir),
from file in di.GetFiles()
select new XElement("file", new XAttribute("name", file.Name)));
}

    不过,需要额外修改一下外面的调用方式:

var di = new DirectoryInfo(@"C:\");
GetFolderContent(di).Save(Console.Out);

    这里必须要用XStreamingElement的Save方法,否则延迟求解的特性可能会失效。

总结和下集预告

    本集介绍了Linq to Xml中的异类:XStreamingElement,既得益于Linq to Xml的简易,又拥有streaming的小内存特性,在操作超大xml是这两个特性可以让工作事半功倍。

    但是,如果一个需要流处理的需求,并且部署环境没有安装.net 3.5 framework的话,怎么办哪,这使我们不得不回到xml reader和xml writer的时代,下集将介绍如何使用这两者。

posted on 2009-12-06 14:48 Zhenway 阅读(...) 评论(...) 编辑 收藏