代码之家  ›  专栏  ›  技术社区  ›  corlettk

用C代码解析(大)XML的最佳方法是什么?

  •  55
  • corlettk  · 技术社区  · 16 年前

    我正在用C语言编写一个GIS客户机工具,从服务器上检索基于GML的XML模式(下面的示例)中的“特性”。摘录仅限于100000个功能。

    我觉得最大的 提取XML 可能会增加大约150兆字节,所以很明显,dom解析器不可用,我一直在尝试在 XmlSerializer XSD.EXE 生成的绑定--或-- XmlReader 以及手工制作的对象图。

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

    有人能指导我吗?特别是关于任何给定方法的内存效率。如果没有,我将不得不“原型”这两个解决方案,并介绍他们并排。

    我有点像.net里的生对虾。任何指导都将不胜感激。

    谢谢你。基思。


    示例XML -最多100000个,每个功能最多234600个坐标。

    <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 回复  |  直到 16 年前
        1
  •  59
  •   John Saunders    15 年前

    使用 XmlReader 解析大型XML文档。 XMLRead 提供对XML数据的快速、仅转发、非缓存访问。(仅前进意味着您可以从头到尾读取XML文件,但不能在文件中向后移动。) XMLRead 使用少量内存,相当于使用简单的SAX阅读器。

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

    您可以使用xmlreader来处理最大为2GB(GB)的文件。

    裁判: How to read XML from a file by using Visual C#

        2
  •  16
  •   corlettk    14 年前

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

    此版本具有以下两个优点:
    *xmlReader/xmlTextReader(内存效率-->速度);以及
    *XML序列化程序(代码生成-->开发便利性和灵活性)。

    它使用xmltextreader迭代文档,并创建“doclets”,使用xmlserializer和用xsd.exe生成的“xml绑定”类对其进行反序列化。

    我想这个食谱是普遍适用的,而且很快…我将在大约7秒钟内解析一个包含56000个GML特性的201MB XML文档…该应用程序的旧vb6实现需要几分钟(甚至数小时)来解析大型提取…所以我很高兴去。

    再一次,A 大的 感谢福米特人给你宝贵的时间。我真的很感激。

    干杯。基思。

    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));
            }
        }
    }
    
        3
  •  12
  •   corlettk    16 年前

    简单地总结一下,对于任何在谷歌中找到这个线索的人来说,这个答案都会更加明显。

    在.NET 2之前,xmlTextReader是标准API(thanx-mitch;-)中可用的内存效率最高的XML解析器。

    .NET 2引入了xmlReader类,这一类更好,它还是一个只向前的元素迭代器(有点像stax解析器)。(比小脑;-)

    记住,孩子们,任何XML实例都有可能超过大约500K,不要使用dom!

    干杯。基思。

        4
  •  6
  •   Community CDub    8 年前

    SAX 解析器可能就是您要寻找的。SAX不要求您将整个文档读取到内存中——它以增量方式解析整个文档,并允许您在执行过程中处理元素。我不知道.NET中是否提供了SAX解析器,但您可以查看以下几个OpenSource选项:

    这是一个相关的帖子:

        5
  •  1
  •   Michael Logutov    12 年前

    只想添加这个简单的扩展方法作为使用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.");
    
        ...
    }