Program Tip

C # 코드에서 (큰) XML을 구문 분석하는 가장 좋은 방법은 무엇입니까?

programtip 2020. 12. 8. 19:50
반응형

C # 코드에서 (큰) XML을 구문 분석하는 가장 좋은 방법은 무엇입니까?


서버에서 GML 기반 XML 스키마 (아래 샘플)의 "기능"을 검색하기 위해 C #으로 GIS 클라이언트 도구를 작성하고 있습니다. 추출은 100,000 개의 기능으로 제한됩니다.

나는 가장 큰 extract.xml 이 약 150MB까지 올라갈 수 있다고 생각하므로, 분명히 DOM 파서가 나왔다. XmlSerializerXSD.EXE 생성 바인딩-또는 -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>

XmlReader큰 XML 문서를 구문 분석하는 데 사용 합니다. XmlReaderXML 데이터에 대한 빠르고 정방향 전용이며 캐시되지 않은 액세스를 제공합니다. (Forward-only는 XML 파일을 처음부터 끝까지 읽을 수 있지만 파일에서 뒤로 이동할 수 없음을 의미합니다.) XmlReader적은 양의 메모리를 사용하며 간단한 SAX 판독기를 사용하는 것과 같습니다.

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

XmlReader를 사용하여 최대 2GB (기가 바이트) 크기의 파일을 처리 할 수 ​​있습니다.

참조 : Visual C #을 사용하여 파일에서 XML을 읽는 방법


2009 년 5 월 14 일 Asat : 하이브리드 접근 방식으로 전환했습니다 ... 아래 코드를 참조하십시오.

이 버전은 두 가지 장점을 모두 가지고 있습니다.
  * XmlReader / XmlTextReader (메모리 효율성-> 속도);
  * XmlSerializer (코드 생성기-> 개발 확장 성 및 유연성).

XmlTextReader를 사용하여 문서를 반복하고 XSD.EXE로 생성 된 XmlSerializer 및 "XML 바인딩"클래스를 사용하여 역 직렬화하는 "doclet"을 만듭니다.

이 레시피는 보편적으로 적용 할 수 있고 빠르다고 생각합니다 ... 56,000 GML 기능이 포함 된 201MB XML 문서를 약 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));
        }
    }
}

요약하고 Google 에서이 스레드를 찾는 모든 사람에게 답을 좀 더 분명하게 만드십시오.

.NET 2 이전에는 XmlTextReader가 표준 API에서 사용할 수있는 가장 메모리 효율적인 XML 파서였습니다 (Mitch;-덕분).

.NET 2는 XmlReader 클래스를 도입했습니다. 다시 한 번 더 낫습니다. 이는 전진 전용 요소 반복기입니다 (StAX 파서와 비슷 함). (고맙습니다 Cerebrus ;-)

그리고 XML 인스턴스가 약 500k보다 클 가능성이 있다는 것을 기억하십시오. DOM을 사용하지 마십시오!

모두 건배. 키스.


SAX의 파서는 당신이 찾고있는 무슨 수 있습니다. SAX는 전체 문서를 메모리로 읽어 올 필요가 없습니다. SAX는 문서를 점진적으로 파싱하고 진행하면서 요소를 처리 할 수 ​​있도록합니다. .NET에서 제공되는 SAX 파서가 있는지 모르겠지만 몇 가지 오픈 소스 옵션을 볼 수 있습니다.

다음은 관련 게시물입니다.


Mitch가 대답 한대로 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.");

    ...
}

참고 URL : https://stackoverflow.com/questions/676274/what-is-the-best-way-to-parse-big-xml-in-c-sharp-code

반응형