近期有个任务要求处理大XML文件,其中有个存了Base64的大节点(>90M,路径已知)。

这种任务只能上XmlReader,即使如此大节点的处理还是头疼了一阵……

最初查MSDN的时候,找到了ReadChars(),可以拿来对付大节点。

方法说明:https://msdn.microsoft.com/zh-cn/library/system.xml.xmltextreader.readchars(v=vs.110).aspx

示例中提到使用方法是:

while( != reader.ReadChars(buffer, , ))
{
// Do something.
// Attribute values are not available at this point.
}

这个处理规范格式的XML没有问题,比如这样的:

<Root>
<LeafNode>Value</LeafNode>
<ParentNode>
<LeafNode>Value</LeafNode>
</ParentNode>
</Root>

但是(没人喜欢这个词,然并卵……),遇到些格式诡异的XML就……

<Root><LeafNode>Value</LeafNode><ParentNode>
<LeafNode>Value</LeafNode></ParentNode>
</Root>

比如这个画风的,用示例代码去读第一个LeafNode的内容,估计会读出“ValueValue”来……

偏偏输入的XML就是这风格的……(*sigh*)

单步执行了一阵,发现这种情况下,XmlTextReader.Name会变化成下个节点的名称(XmlTextReader.LocalName亦如此),可以根据这个判断是否已经达到节点结尾。

改进版为:

string currentName = reader.LocalName;
while(currentName == reader.LocalName && != reader.ReadChars(buffer, , ))
{
// Do something.
// Attribute values are not available at this point.
}

顺便贴上一个转写并对特定节点进行处理的代码:

List<string> processNodePathList = new List<string> {"/Root/Path/to/Target"};
List<string> bigNodePathList = new List<string> { "/Root/Path/to/Big/Node" }; private static void ProcessBigXmlFile(string sourcePath, string targetPath, IList<string> processNodePathList, IList<string> bigNodePathList)
{
var processNodeNameList =
processNodePathList.Select(
processNodePath => processNodePath.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries))
.Select(nodePathParts => nodePathParts[nodePathParts.Length - ])
.ToList();
var bigNodeNameList = bigNodePathList.Select(
bigNodePath => bigNodePath.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries))
.Select(nodePathParts => nodePathParts[nodePathParts.Length - ])
.ToList(); var sourceStream = new FileStream(sourcePath, FileMode.Open, FileAccess.Read);
var reader = new XmlTextReader(sourceStream); var targetStream = new FileStream(targetPath, FileMode.Create, FileAccess.Write);
var writer = new XmlTextWriter(targetStream, Encoding.UTF8); try
{
var pathStack = new Stack<string>();
var readResult = reader.Read();
while (readResult)
{
int skipMode = ;
switch (reader.NodeType)
{
case XmlNodeType.Element:
{
pathStack.Push(reader.Name);
writer.WriteStartElement(reader.LocalName);
if (reader.HasAttributes)
{
while (reader.MoveToNextAttribute())
{
writer.WriteAttributeString(reader.LocalName,
reader.Value);
}
reader.MoveToElement();
} if (processNodeNameList.Contains(reader.LocalName))
{
var index = processNodeNameList.IndexOf(reader.LocalName);
if (CompareNodePath(pathStack, processNodePathList[index]))
{ // Replace node content writer.WriteFullEndElement();
skipMode = ;
}
}
else if (bigNodeNameList.Contains(reader.LocalName))
{
var index = bigNodeNameList.IndexOf(reader.LocalName);
if (CompareNodePath(pathStack, bigNodePathList[index]))
{
reader.MoveToContent();
var buffer = new char[];
int len;
while (reader.LocalName == bigNodePathList[index] &&
(len = reader.ReadChars(buffer, , buffer.Length)) > )
{
writer.WriteRaw(buffer, , len);
}
writer.WriteFullEndElement();
skipMode = ;
}
}
if (reader.IsEmptyElement)
{
pathStack.Pop();
writer.WriteEndElement();
}
break;
}
//case XmlNodeType.Attribute:
//{
// newPackageWriter.WriteAttributeString(oldPackageReader.LocalName, oldPackageReader.Value);
// break;
//}
case XmlNodeType.Text:
{
writer.WriteValue(reader.Value);
break;
}
case XmlNodeType.CDATA:
{
writer.WriteCData(reader.Value);
break;
}
//case XmlNodeType.EntityReference:
//{
// newPackageWriter.WriteEntityRef(oldPackageReader.Name);
// break;
//}
//case XmlNodeType.Entity:
//{
// break;
//}
case XmlNodeType.ProcessingInstruction:
{
writer.WriteProcessingInstruction(reader.Name, reader.Value);
break;
}
case XmlNodeType.Comment:
{
writer.WriteComment(reader.Value);
break;
}
//case XmlNodeType.Document:
//{
// break;
//}
case XmlNodeType.DocumentType:
{
writer.WriteRaw(string.Format("<!DOCTYPE{0} [{1}]>", reader.Name,
reader.Value));
break;
}
//case XmlNodeType.DocumentFragment:
//{
// break;
//}
//case XmlNodeType.Notation:
//{
// break;
//}
case XmlNodeType.Whitespace:
{
writer.WriteWhitespace(reader.Value);
break;
}
//case XmlNodeType.SignificantWhitespace:
//{
// break;
//}
case XmlNodeType.EndElement:
{
pathStack.Pop();
writer.WriteFullEndElement();
break;
}
case XmlNodeType.XmlDeclaration:
{
writer.WriteStartDocument();
break;
}
} switch (skipMode)
{
case :
{
reader.Skip();
pathStack.Pop();
readResult = !reader.EOF;
break;
}
case :
{
pathStack.Pop();
readResult = !reader.EOF;
break;
}
default:
{
readResult = reader.Read();
break;
}
}
}
}
finally
{
writer.Close();
targetStream.Close();
targetStream.Dispose();
reader.Close();
sourceStream.Close();
sourceStream.Dispose();
}
} private static bool CompareNodePath(Stack<string> currentNodePathStack, string compareNodePathString)
{
var currentArray = currentNodePathStack.Reverse().ToArray();
var compareArray = compareNodePathString.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries);
if (compareArray.Length != currentArray.Length)
{
return false;
}
bool isDifferent = false;
for (int i = ; i < currentArray.Length; i++)
{
if (compareArray[i] != currentArray[i])
{
isDifferent = true;
break;
}
}
return !isDifferent;
}

关于大XML文件与大节点处理(System.Xml.XmlTextReader)的更多相关文章

  1. xml文件的根节点layout_width或者layout_height设置无效果的原因分析

    在android开发中相信大家对ListView.GridView等组建都非常熟悉,在使用它们的时候须要自己配置相关的Adapter,而且配置现骨干的xml文件作为ListView等组建的子View, ...

  2. C#中操作xml文件(插入节点、修改、删除)

    已知有一个xml文件(bookstore.xml)如下: <?xml version="1.0" encoding="gb2312"?> <b ...

  3. C#程序中:如何删除xml文件中的节点、元素。

    C#中动态的清理xml文件中的垃圾信息是程序员必会的哦.这就像数据库一样,不会清理数据怎么可以呢?其实xml文件就可以用作一个小的数据库,存储一些简单的信息.所以,用C#程序实现xml文件的增.删.改 ...

  4. C#程序中:如何修改xml文件中的节点(数据)

    要想在web等程序中实现动态的数据内容给新(如网页中的Flash),不会更新xml文件中的节点(数据)是远远不够的,今天在这里说一个简单的xml文件的更新,方法比较基础,很适合初学者看的,保证一看就懂 ...

  5. testng.xml文件结构组成及节点属性说明

    TestNG的DTD检查文件:http://testng.org/testng-1.0.dtd.PHP 更多testng配置及说明,请移步http://testdoc.org/docmaster?pi ...

  6. 节点文件将两个不同格式的XML文件,进行节点对照,并生成一个用于对照功能的XML

    本文纯属个人见解,是对前面学习的总结,如有描述不正确的地方还请高手指正~ 经常有的需求是,需要将一种格式的XML转换成另一种XML.如果要实现这个功能首先需要将两个不同XML手动建立节点对比关系.然后 ...

  7. 使用jdom操作xml文件 去除子节点带有命名空间

    package com.soft.common; import java.util.HashMap; import java.util.Map; import org.jdom2.Namespace; ...

  8. C#程序中:如何向xml文件中插入节点(数据)

    向xml文件中动态的添加节点(数据)是一件很爽的事,可以给你的程序带来很多的方便,比如在web中,如果你的Flash用到了xml文件,这个方法可以让你在后台就轻轻松松的更新你的Flash内容哦!一起研 ...

  9. 读取XML文件的指定节点的值 并转换为Item

    cmb_State_Send.ItemsSource = null; XmlDocument doc = new XmlDocument(); doc.Load("D:\\模板\\Works ...

随机推荐

  1. oracle 12c之前用sequence 和 trigger来生成自动增长的列

    SQL> create table scott.t1 (id number, val varchar2(8)); Table created. SQL> CREATE SEQUENCE s ...

  2. [MongoDB]安装 MongoDB 数据库

    1.tar zxvf mongodb.tgz 2.vim ~/.bashrc  增加 MONGODB_HOME 并添加bin到PATH ,source ~/.bashrc 3.mongod -vers ...

  3. keras与sklearn的结合使用

    keras与sklearn的结合使用 新建 模板 Fly Time: 2017-4-14 引言 代码 引言 众所周知,keras目前没有提供交叉验证的功能,我们要向使用交叉验证,就需要与sklearn ...

  4. jQuery Validate 使用

    jQuery Validate 使用 <script src="js/b/js/jquery.validate.js"></script> <styl ...

  5. Linux下route add 命令加入路由列表

    route add命令的主要作用是加入静态路由,通常的格式是: route ADD 157.0.0.0 MASK 255.0.0.0  157.55.80.1 METRIC 3 IF 2 參数含义:d ...

  6. NSRange and NSString对象

    http://www.cnblogs.com/pengyingh/articles/2451292.html 当谈到NSString方法时,你会发现很多都涉及到NSRange.不必操心.它事实上仅仅只 ...

  7. 一个NHibernate的BUG

    一.背景 我们如今做的项目,用NHibernate实现数据訪问层. 訪问数据时,有的数据库表是确定的:有明白的表名.字段名.这时候依照常规的方法处理就可以:建立数据库表到类的映射.使用HQL读写数据库 ...

  8. 怎样使用ListView实现一个带有网络请求,解析,分页,缓存的公共的List页面来大大的提高工作效率

    在寻常的开发中常常会有非常多列表页面.每做一个列表页就须要创建这个布局文件那个Adapter适配器文件等等一大堆与之相关的附属的不必要的冗余文件. 假设版本号更新迭代比較频繁,如此以往,就会使项目pr ...

  9. Lucene5学习之使用MMSeg4j分词器

      分类:程序语言|标签:C|日期: 2015-05-01 02:00:24 MMSeg4j是一款中文分词器,详细介绍如下: 1.mmseg4j 用 Chih-Hao Tsai 的 MMSeg 算法( ...

  10. crmjs区分窗口是否是高速编辑(2)

    随着crm的版本号不同,有些功能不能使用,这里提供了第二种写法: function loadFrom() {     var formType = Xrm.Page.ui.getFormType(); ...