Каков наилучший способ анализа (большого) XML в коде С#?
Я пишу инструмент GIS-клиента на С# для извлечения "функций" в XML-схеме на основе GML (образец ниже) с сервера. Экстракты ограничены 100 000 функциями.
Я бы сказал, что самый большой файл extract.xml может получить около 150 мегабайт, поэтому очевидно, что DOM-парсеры отсутствуют, я пытался решить между XmlSerializer и XSD.EXE сгенерированные привязки --OR-- XmlReader и графический объект, созданный вручную.
Или, может быть, есть лучший способ, который я еще не рассмотрел? Как XLINQ, или????
Пожалуйста, кто-нибудь может мне помочь? Особенно в отношении эффективности памяти любого данного подхода. Если нет, мне придется "prototype" оба решения и проецировать их бок о бок.
Я немного креветка в .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>
Ответы
Ответ 1
Используйте XmlReader
для синтаксического анализа больших XML-документов. XmlReader
обеспечивает быстрый, прямой доступ, не кэшированный доступ к данным XML. (Только для пересылки означает, что вы можете читать XML файл от начала до конца, но не можете перемещаться назад в файле.) XmlReader
использует небольшие объемы памяти и эквивалентно использованию простого считывателя SAX.
using (XmlReader myReader = XmlReader.Create(@"c:\data\coords.xml"))
{
while (myReader.Read())
{
// Process each node (myReader.Value) here
// ...
}
}
Вы можете использовать XmlReader для обработки файлов размером до 2 гигабайт (ГБ).
Ссылка: Как читать XML из файла с помощью Visual С#
Ответ 2
Асат 14 мая 2009 года: я перешел на использование гибридного подхода... см. код ниже.
Эта версия имеет большинство преимуществ обоих:
* XmlReader/XmlTextReader (эффективность памяти → скорость); и
* XmlSerializer (code-gen → ускорение разработки и гибкость).
Он использует XmlTextReader для итерации по документу и создает "doclets", которые он десериализует, используя классы XmlSerializer и "XML binding", сгенерированные с помощью XSD.EXE.
Я предполагаю, что этот рецепт универсально применим, и он быстро... Я разбираю XML-документ 201 МБ, содержащий 56 000 функций GML, примерно через 7 секунд... старая реализация VB6 этого приложения заняла минуты (или даже часы), чтобы разобрать большие выдержки... так что я выгляжу хорошо.
Еще раз, БОЛЬШОЙ Спасибо форумам за то, что вы пожертвовали свое драгоценное время. Я действительно ценю это.
Приветствует всех. Кит.
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 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));
}
}
}
Ответ 3
Просто подведем итоги и сделаем ответ более очевидным для тех, кто находит этот поток в google.
До .NET 2 XmlTextReader был наиболее эффективным с точки зрения памяти XML-парсером, доступным в стандартном API (thanx Mitch; -)
.NET 2 представила класс XmlReader, который лучше снова. Это итератор с прямым доступом (немного похожий на анализатор StAX). (thanx Cerebrus; -)
И помните, что детишки любого экземпляра XML могут быть больше, чем около 500 тыс., НЕ ИСПОЛЬЗУЙТЕ DOM!
Приветствует всех. Кит.
Ответ 4
A SAX может быть то, что вы ищете. SAX не требует, чтобы вы прочитали весь документ в памяти - он постепенно анализирует его и позволяет обрабатывать элементы по мере их поступления. Я не знаю, есть ли SAX-анализатор, предоставленный в .NET, но есть несколько вариантов с открытым исходным кодом, которые вы можете посмотреть:
Вот связанная статья:
Ответ 5
Просто хотел добавить этот простой метод расширения в качестве примера использования 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.");
...
}