8wDlpd.png
8wDFp9.png
8wDEOx.png
8wDMfH.png
8wDKte.png

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

maxime1992 2月前

133 0

我正在用 C# 编写一个 GIS 客户端工具,用于从服务器检索基于 GML 的 XML 架构(示例如下)中的“特征”。提取限制为 100,000 个特征。我估计最大的 extract.xml

我正在用 C# 编写一个 GIS 客户端工具,用于从服务器检索基于 GML 的 XML 架构(以下示例)中的“特征”。提取限制为 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>
帖子版权声明 1、本帖标题:在 C# 代码中解析(大)XML 的最佳方法是什么?
    本站网址:http://xjnalaquan.com/
2、本网站的资源部分来源于网络,如有侵权,请联系站长进行删除处理。
3、会员发帖仅代表会员个人观点,并不代表本站赞同其观点和对其真实性负责。
4、本站一律禁止以任何方式发布或转载任何违法的相关信息,访客发现请向站长举报
5、站长邮箱:yeweds@126.com 除非注明,本帖由maxime1992在本站《parsing》版块原创发布, 转载请注明出处!
最新回复 (0)
  • 是的,你说得对。只是在我的特定情况下,我需要给出遗漏元素的完整路径,而不仅仅是它的名称。

  • 很好...一个建议的改进:如果所寻找的元素始终是当前操作的终端(它必须已经将我们的读取器跳过到EOF),则直接在SkipTo中抛出异常而不是返回false...你已经得到了要报告的所寻找的元素名称,因此请使用它而不是在错误消息中重复自己。

  • 只是想添加这个简单的扩展方法作为使用 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.");
    
        ...
    }
    
  • .NET 没有提供原生的 sax 解析器,但是我读过一篇文章(我想是在 slashdot 上),它展示了使用 XmlReader \'primitives\' 来制作自己的 SAX 解析器是多么容易。

  • 比较 Sax 和 XmlTextReader 的性能会很有趣 - 有人尝试过吗

  • 解析器 SAX 可能就是您正在寻找的。SAX 不需要您将整个文档读入内存 - 它会逐步解析它并允许您在解析过程中处理元素。我不知道 .NET 中是否提供了 SAX 解析器,但您可以查看一些开源选项:

    • http://saxdotnet.sourceforge.net/
    • http://www.codeguru.com/csharp/csharp/cs_data/xml/article.php/c4221

    以下是相关文章:

    • SAX 与 XmlTextReader - C# 中的 SAX
  • 只是为了总结一下,让任何在谷歌中找到这个帖子的人都能更清楚地找到答案。

    在 .NET 2 之前,XmlTextReader 是标准 API 中内存效率最高的 XML 解析器(感谢 Mitch;-)

    .NET 2 引入了 XmlReader 类,它再次变得更好,它是一个只进元素迭代器(有点像 StAX 解析器)。(感谢 Cerebrus;-)

    孩子们记住,任何 XML 实例都有可能大于 500k,不要使用 DOM!

    大家欢呼吧,基思。

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

    此版本兼具两者的大部分优点:
    * XmlReader/XmlTextReader(内存效率 --> 速度);和
    * XmlSerializer(代码生成-->开发的便利性和灵活性)。

    它使用 XmlTextReader 遍历文档,并创建 \'doclets\',然后使用 XmlSerializer 和 XSD.EXE 生成的 \'XML binding\' 类对其进行反序列化。

    我想这个方法是普遍适用的,并且速度很快...我正在大约 7 秒内解析一个包含 56,000 个 GML 要素的 201 MB XML 文档...这个应用程序的旧 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));
            }
        }
    }
    
  • drum 2月前 0 只看Ta
    引用 10

    @Nickolay 2GB 的限制在 MSDN 中引用如下:msdn.microsoft.com/en-us/library/ff647804.aspx :\'您只能使用 XmlTextReader 和 XmlValidatingReader 来处理最大 2 GB 的文件。如果您需要处理更大的文件,请将源文件分成多个较小的文件或流。\'

  • JOR 2月前 0 只看Ta
    引用 11

    \'文件大小不超过 2 GB\' - 我找不到解释此限制的参考资料,而且似乎没有其他人提到它。您有解释此限制的链接吗?

  • 在 CF 中也使用特殊的 ctor。XmlReader.Create,因为它经过了一些优化,当然记得利用 .Skip() 跳过不感兴趣的元素!

  • @Cerebrus 和 Mitch:谢谢先生们。我也是这么想的,但在浪费时间走上可能错误的路之前,能听取第二个(明智的)意见确实很好。非常感谢!

  • 据我所知,.NET 2.0 及以后,MS 建议直接使用 XmlReader 类,而不是 XmlTextReader。

  • Gwen 2月前 0 只看Ta
    引用 15

    用于 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 千兆字节 (GB) 的文件。

    参考: 如何使用 Visual C# 从文件读取 XML

返回
作者最近主题: