Каков наилучший способ разбора (большого) XML в C # Code? - PullRequest
58 голосов
/ 24 марта 2009

Я пишу клиентский инструмент ГИС в C # для извлечения «функций» в XML-схеме на основе GML (пример ниже) с сервера. Выдержки ограничены 100 000 функций.

Я предполагаю, что самый большой extract.xml может занять около 150 мегабайт, поэтому очевидно, что анализаторы DOM отсутствуют, я пытался выбрать между XmlSerializer и XSD .EXE сгенерированные привязки --OR-- XmlReader и граф объектов, созданный вручную.

Или, может быть, есть лучший способ, который я еще не рассмотрел? Как XLINQ, или ????

Пожалуйста, кто-нибудь может направить меня? Особенно в отношении эффективности памяти любого данного подхода. Если нет, мне придется «прототипировать» оба решения и профилировать их параллельно.

Я немного сырая креветка в .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>

Ответы [ 5 ]

61 голосов
/ 24 марта 2009

Используйте 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 C #

16 голосов
/ 14 мая 2009

Asat 14 мая 2009 г .: я перешел на гибридный подход ... см. Код ниже.

Эта версия имеет большинство преимуществ обоих:
* XmlReader / XmlTextReader (эффективность использования памяти -> скорость); и
* XmlSerializer (code-gen -> скорость разработки и гибкость).

Он использует XmlTextReader для итерации по документу и создает «доклеты», которые он десериализует с использованием классов XmlSerializer и «XMLinding», сгенерированных с помощью 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'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));
        }
    }
}
12 голосов
/ 24 марта 2009

Просто, чтобы подвести итог, и сделать ответ немного более очевидным для тех, кто находит эту тему в Google.

До .NET 2 XmlTextReader был самым экономичным анализатором XML, доступным в стандартном API (спасибо Mitch; -)

.NET 2 представил класс XmlReader, который снова стал лучше. Это итератор элементов, предназначенный только для пересылки (немного похоже на анализатор StAX). (спасибо Церебру; -)

И помните детишек, у любого экземпляра XML есть потенциал, который может быть больше чем приблизительно 500 КБ, НЕ ИСПОЛЬЗУЙТЕ ДОМ!

Ура всем. Кит.

6 голосов
/ 24 марта 2009

A SAX Парсер может быть то, что вы ищете. SAX не требует, чтобы вы прочитали весь документ в память - он анализирует его постепенно и позволяет обрабатывать элементы по ходу работы. Я не знаю, есть ли в .NET анализатор SAX, но есть несколько вариантов с открытым исходным кодом, на которые вы можете посмотреть:

Вот похожий пост:

1 голос
/ 27 мая 2013

Просто хотел добавить этот простой метод расширения в качестве примера использования 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.");

    ...
}
...