在C#代码中parsing(大)XML的最好方法是什么?

我正在用C#编写一个GIS客户端工具,以便从服务器中检索基于GML的XML模式(下面的示例)中的“function”。 提取物限于100,000个function。

我很抱歉,最大的extract.xml可能会达到150兆字节左右,所以明显的DOMparsing器已经出来了我一直在试图决定之间的XmlSerializer和XSD.EXE生成的绑定 – 或 – XmlReader和手工制作的对象图。

或者也许还有一个我还没有考虑过的更好的方法? 像XLINQ,还是????

请任何人指导我? 特别是关于任何给定方法的记忆效率。 如果没有,我将不得不“原型”这两个解决scheme并configuration他们并排。

在.NET中我是一个未知的大虾。 任何指导将不胜感激。

感谢您。 基思。


XML示例 – 高达100,000个,每个function高达234,600个坐标。

<feature featId="27168306" fType="vegetation" fTypeId="1129" fClass="vegetation" gType="Polygon" ID="0" cLockNr="51598" metadataId="51599" mdFileId="NRM/TIS/VEGETATION/9543_22_v3" dataScale="25000"> <MultiGeometry> <geometryMember> <Polygon> <outerBoundaryIs> <LinearRing> <coordinates>153.505004,-27.42196 153.505044,-27.422015 153.503992 .... 172 coordinates omitted to save space ... 153.505004,-27.42196</coordinates> </LinearRing> </outerBoundaryIs> </Polygon> </geometryMember> </MultiGeometry> </feature> 

使用XmlReaderparsing大型XML文档。 XmlReader提供对XML数据的快速,唯一的,非caching的访问。 (仅向前意味着您可以从头到尾读取XML文件,但不能在文件中向后移动。) XmlReader使用less量内存,相当于使用简单的SAX读取器。

  using (XmlReader myReader = XmlReader.Create(@"c:\data\coords.xml")) { while (myReader.Read()) { // Process each node (myReader.Value) here // ... } } 

您可以使用XmlReader来处理大小不超过2千兆字节(GB)的文件。

参考: 如何使用Visual C#从文件中读取XML

Asat 2009年5月14日:我已经转向使用混合方法…请参阅下面的代码。

这个版本具有以下两个方面的大部分优点:
* XmlReader / XmlTextReader(内存效率 – >速度); 和
* XmlSerializer(代码 – >开发Exdiancy和灵活性)。

它使用XmlTextReader遍历文档,并创build使用XmlSerializer和使用XSD.EXE生成的“XML绑定”类进行反序列化的“doclet”。

我猜这个方法是普遍适用的,而且速度很快……我在大约7秒钟内parsing了一个包含56,000个GML特征的201 MB XML文档…这个应用程序的旧VB6实现花费了几分钟(甚至几小时)的时间来parsing大提取物…所以我看着很好去。

再次, BIG谢谢您的论坛捐赠您宝贵的时间。 对此,我真的非常感激。

欢呼声 基思。

 using System; using System.Reflection; using System.Xml; using System.Xml.Serialization; using System.IO; using System.Collections.Generic; using nrw_rime_extract.utils; using nrw_rime_extract.xml.generated_bindings; namespace nrw_rime_extract.xml { internal interface ExtractXmlReader { rimeType read(string xmlFilename); } /// <summary> /// RimeExtractXml provides bindings to the RIME Extract XML as defined by /// $/Release 2.7/Documentation/Technical/SCHEMA and DTDs/nrw-rime-extract.xsd /// </summary> internal class ExtractXmlReader_XmlSerializerImpl : ExtractXmlReader { private Log log = Log.getInstance(); public rimeType read(string xmlFilename) { log.write( string.Format( "DEBUG: ExtractXmlReader_XmlSerializerImpl.read({0})", xmlFilename)); using (Stream stream = new FileStream(xmlFilename, FileMode.Open)) { return read(stream); } } internal rimeType read(Stream xmlInputStream) { // create an instance of the XmlSerializer class, // specifying the type of object to be deserialized. XmlSerializer serializer = new XmlSerializer(typeof(rimeType)); serializer.UnknownNode += new XmlNodeEventHandler(handleUnknownNode); serializer.UnknownAttribute += new XmlAttributeEventHandler(handleUnknownAttribute); // use the Deserialize method to restore the object's state // with data from the XML document. return (rimeType)serializer.Deserialize(xmlInputStream); } protected void handleUnknownNode(object sender, XmlNodeEventArgs e) { log.write( string.Format( "XML_ERROR: Unknown Node at line {0} position {1} : {2}\t{3}", e.LineNumber, e.LinePosition, e.Name, e.Text)); } protected void handleUnknownAttribute(object sender, XmlAttributeEventArgs e) { log.write( string.Format( "XML_ERROR: Unknown Attribute at line {0} position {1} : {2}='{3}'", e.LineNumber, e.LinePosition, e.Attr.Name, e.Attr.Value)); } } /// <summary> /// xtractXmlReader provides bindings to the extract.xml /// returned by the RIME server; as defined by: /// $/Release X/Documentation/Technical/SCHEMA and /// DTDs/nrw-rime-extract.xsd /// </summary> internal class ExtractXmlReader_XmlTextReaderXmlSerializerHybridImpl : ExtractXmlReader { private Log log = Log.getInstance(); public rimeType read(string xmlFilename) { log.write( string.Format( "DEBUG: ExtractXmlReader_XmlTextReaderXmlSerializerHybridImpl." + "read({0})", xmlFilename)); using (XmlReader reader = XmlReader.Create(xmlFilename)) { return read(reader); } } public rimeType read(XmlReader reader) { rimeType result = new rimeType(); // a deserializer for featureClass, feature, etc, "doclets" Dictionary<Type, XmlSerializer> serializers = new Dictionary<Type, XmlSerializer>(); serializers.Add(typeof(featureClassType), newSerializer(typeof(featureClassType))); serializers.Add(typeof(featureType), newSerializer(typeof(featureType))); List<featureClassType> featureClasses = new List<featureClassType>(); List<featureType> features = new List<featureType>(); while (!reader.EOF) { if (reader.MoveToContent() != XmlNodeType.Element) { reader.Read(); // skip non-element-nodes and unknown-elements. continue; } // skip junk nodes. if (reader.Name.Equals("featureClass")) { using ( StringReader elementReader = new StringReader(reader.ReadOuterXml())) { XmlSerializer deserializer = serializers[typeof (featureClassType)]; featureClasses.Add( (featureClassType) deserializer.Deserialize(elementReader)); } continue; // ReadOuterXml advances the reader, so don't read again. } if (reader.Name.Equals("feature")) { using ( StringReader elementReader = new StringReader(reader.ReadOuterXml())) { XmlSerializer deserializer = serializers[typeof (featureType)]; features.Add( (featureType) deserializer.Deserialize(elementReader)); } continue; // ReadOuterXml advances the reader, so don't read again. } log.write( "WARNING: unknown element '" + reader.Name + "' was skipped during parsing."); reader.Read(); // skip non-element-nodes and unknown-elements. } result.featureClasses = featureClasses.ToArray(); result.features = features.ToArray(); return result; } private XmlSerializer newSerializer(Type elementType) { XmlSerializer serializer = new XmlSerializer(elementType); serializer.UnknownNode += new XmlNodeEventHandler(handleUnknownNode); serializer.UnknownAttribute += new XmlAttributeEventHandler(handleUnknownAttribute); return serializer; } protected void handleUnknownNode(object sender, XmlNodeEventArgs e) { log.write( string.Format( "XML_ERROR: Unknown Node at line {0} position {1} : {2}\t{3}", e.LineNumber, e.LinePosition, e.Name, e.Text)); } protected void handleUnknownAttribute(object sender, XmlAttributeEventArgs e) { log.write( string.Format( "XML_ERROR: Unknown Attribute at line {0} position {1} : {2}='{3}'", e.LineNumber, e.LinePosition, e.Attr.Name, e.Attr.Value)); } } } 

只是要总结一下,对于任何在google中发现这个主题的人来说,这个答案都会更加明显。

在.NET 2之前,XmlTextReader是标准API中可用的内存效率最高的XMLparsing器(thanx Mitch 😉

.NET 2引入了再次更好的XmlReader类它是一个只向前的元素迭代器(有点像StAXparsing器)。 (thanx Cerebrus 😉

记住任何XML实例的小子有可能大于大约500k,不要使用DOM!

欢呼声 基思。

一个SAXparsing器可能是你正在寻找的东西。 SAX不要求将整个文档读入内存 – 它逐渐parsing它,并允许您随时处理元素。 我不知道是否有.NET提供的SAXparsing器,但有几个开源选项可供您查看:

这里有一个相关的职位:

  • SAX vs XmlTextReader – SAX在C#

只是想添加这个简单的扩展方法作为使用XmlReader的例子(如米奇回答):

 public static bool SkipToElement (this XmlReader xmlReader, string elementName) { if (!xmlReader.Read ()) return false; while (!xmlReader.EOF) { if (xmlReader.NodeType == XmlNodeType.Element && xmlReader.Name == elementName) return true; xmlReader.Skip (); } return false; } 

和用法:

 using (var xml_reader = XmlReader.Create (this.source.Url)) { if (!SkipToElement (xml_reader, "Root")) throw new InvalidOperationException ("XML element \"Root\" was not found."); if (!SkipToElement (xml_reader, "Users")) throw new InvalidOperationException ("XML element \"Root/Users\" was not found."); ... }