[英]What is the best way to parse (big) XML in C# Code?
我正在 C# 中編寫一個 GIS 客戶端工具,以從服務器檢索基於 GML 的 XML 模式(下例)中的“特征”。 提取限制為 100,000 個特征。
I guestimate that the largest extract.xml might get up around 150 megabytes, so obviously DOM parsers are out I've been trying to decide between XmlSerializer and XSD.EXE generated bindings --OR-- XmlReader and a hand-crafted object graph.
或者也許有更好的方法我還沒有考慮過? 像 XLINQ,還是????
請問有人可以指導我嗎? 特別是關於任何給定方法的 memory 效率。 如果不是,我將不得不對這兩個解決方案進行“原型設計”並並排分析它們。
我在 .NET 有點像生蝦。 任何指導將不勝感激。
感謝您。 基思。
樣品 XML - 最多 100,000 個,每個特征最多 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>
使用XmlReader
解析大型 XML 文檔。 XmlReader
提供對 XML 數據的快速、只進、非緩存訪問。 (Forward-only意味着你可以從頭到尾讀取XML文件,但不能在文件中向后移動。) XmlReader
使用了少量的memory,相當於使用了一個簡單的SAX閱讀器。
using (XmlReader myReader = XmlReader.Create(@"c:\data\coords.xml"))
{
while (myReader.Read())
{
// Process each node (myReader.Value) here
// ...
}
}
您可以使用 XmlReader 處理最大為 2 GB 的文件。
Asat 2009 年 5 月 14 日:我已改用混合方法……請參閱下面的代碼。
此版本具有兩者的大部分優點:
* XmlReader/XmlTextReader(內存效率 --> 速度); 和
* XmlSerializer(代碼生成 --> 開發權宜性和靈活性)。
它使用 XmlTextReader 遍歷文檔,並創建使用 XmlSerializer 反序列化的“doclet”和用 XSD.EXE 生成的“XML 綁定”類。
我想這個秘訣是普遍適用的,而且速度很快......我正在解析一個 201 MB XML 文檔,其中包含 56,000 個 GML 功能,大約需要 7 秒......這個應用程序的舊 VB6 實現需要幾分鍾(甚至幾小時)來解析大量提取物......所以我對 go 很滿意。
再一次,非常感謝論壇成員貢獻了您寶貴的時間。 對此,我真的非常感激。
祝大家歡呼。 基思。
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));
}
}
}
總結一下,讓在谷歌找到這個帖子的人更清楚答案。
在 .NET 2 之前,XmlTextReader 是 memory 效率最高的 XML 解析器在標准 ZDB974238714CA-ACEDE4F8Z 中可用;
.NET 2 引入了 XmlReader class,它再次變得更好,它是一個只進的元素迭代器(有點像 StAX 解析器)。 (感謝 Cerebrus;-)
記住孩子們,任何 XML 實例都有可能大於 500k,不要使用 DOM!
祝大家歡呼。 基思。
SAX解析器可能是您正在尋找的。 SAX 不需要您將整個文檔讀入 memory - 它會逐步解析它並允許您像 go 一樣處理元素。 我不知道 .NET 中是否提供了 SAX 解析器,但是您可以查看一些開源選項:
這是一個相關的帖子:
只是想添加這個簡單的擴展方法作為使用 XmlReader 的示例(正如 Mitch 回答的那樣):
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.");
...
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.