zoukankan      html  css  js  c++  java
  • ACEXML解析XML文件——我是如何学习并在短时间内掌握一个库的使用方法的

    最近做的C++项目中需要使用xml文件保存一些信息,程序启动时会读取这些信息。最终经过主程的评测,决定使用ACEXML库来读取解析XML文件。

    好吧,至于为什么选择ACEXML库,我就不说了。既然选择了它,就要尽快上手并使用它。可是主程说他没有文档,如何使用自己看着办吧

    那么我是如何在短时间内掌握ACEXML的使用方法呢,下面来分享一下,我的学习历程。

    第一步肯定是谷歌搜索

    对于我来说,第一步肯定不用想,直接上谷歌搜索。

    经过几分钟的搜索后,我并没有得到我想要的信息,谷歌前五页基本上只能找到一个简单的例子程序使用 ACEXML 来解析一个 xml 文件 和一个简介ACEXML Programming Guidelines

    花几分钟看了一下例子程序,代码特别长,有很多无关的东西,决定换下一种方法。

    第二步查看官方的example

    好吧,既然网上没有详细的介绍,那么退而求其次。

    一般的开源库都会有许多例子程序来帮助使用者学习库的使用方式,ACEXML库也不例外。

    找到ACEXML文件夹中有一个examples子文件夹,打开里面的解决方案,看到有一个”SAXPrint”的例子程序,OK,就是它了。

    首先编译程序,然后运行

    示例程序首次运行

    窗口中输出的是命令行参数

    -s : 使用 SAXPrint_Handler,默认是 Priing_handler。

    -l:解析内置的字符串。

    -f:当没有指定-l 时,解析指定的文件。

    -z:使用指定的HTTP 文件。

    接下来在运行的时候指定命令行

    -l -s

    指定命令行参数-l-s

    根据打印出来的信息与内置的字符串比较,xml格式字符串正确的解析出来。

    那么接下来看一下SAXPrint_Handler这个类是如何实现的。

    /**
     * @class ACEXML_SAXPrint_Handler
     *
     * @brief ACEXML_SAXPrint_Handler is an example SAX event handler.
     *
     * This SAX event handler try to regenerate the XML document it
     * reads with correct indentation.
     */
    class ACEXML_SAXPrint_Handler : public ACEXML_DefaultHandler
    {
    public:
      /*
       * Default constructor.
       */
      ACEXML_SAXPrint_Handler (const ACEXML_Char* name);
    
      /*
       * Default destructor.
       */
      virtual ~ACEXML_SAXPrint_Handler (void);
    
      // Methods inherit from ACEXML_ContentHandler.
    
      /*
       * Receive notification of character data.
       */
      virtual void characters (const ACEXML_Char *ch,
                               size_t start,
                               size_t length)
        ;
    
      /*
       * Receive notification of the end of a document.
       */
      virtual void endDocument (void)
        ;
    
      /*
       * Receive notification of the end of an element.
       */
      virtual void endElement (const ACEXML_Char *namespaceURI,
                               const ACEXML_Char *localName,
                               const ACEXML_Char *qName);
    
      /*
       * End the scope of a prefix-URI mapping.
       */
      virtual void endPrefixMapping (const ACEXML_Char *prefix);
    
      /*
       * Receive notification of ignorable whitespace in element content.
       */
      virtual void ignorableWhitespace (const ACEXML_Char *ch,
                                        int start,
                                        int length);
    
      /*
       * Receive notification of a processing instruction.
       */
      virtual void processingInstruction (const ACEXML_Char *target,
                                          const ACEXML_Char *data);
    
      /*
       * Receive an object for locating the origin of SAX document events.
       */
      virtual void setDocumentLocator (ACEXML_Locator *locator);
    
      /*
       * Receive notification of a skipped entity.
       */
      virtual void skippedEntity (const ACEXML_Char *name);
    
      /*
       * Receive notification of the beginning of a document.
       */
      virtual void startDocument (void);
    
      /*
       * Receive notification of the beginning of an element.
       */
      virtual void startElement (const ACEXML_Char *namespaceURI,
                                 const ACEXML_Char *localName,
                                 const ACEXML_Char *qName,
                                 ACEXML_Attributes *atts);
    
      /*
       * Begin the scope of a prefix-URI Namespace mapping.
       */
      virtual void startPrefixMapping (const ACEXML_Char *prefix,
                                       const ACEXML_Char *uri);
    
      // *** Methods inherit from ACEXML_DTDHandler.
    
      /*
       * Receive notification of a notation declaration event.
       */
      virtual void notationDecl (const ACEXML_Char *name,
                                 const ACEXML_Char *publicId,
                                 const ACEXML_Char *systemId);
    
      /*
       * Receive notification of an unparsed entity declaration event.
       */
      virtual void unparsedEntityDecl (const ACEXML_Char *name,
                                       const ACEXML_Char *publicId,
                                       const ACEXML_Char *systemId,
                                       const ACEXML_Char *notationName);
    
      // Methods inherit from ACEXML_EnitityResolver.
    
      /*
       * Allow the application to resolve external entities.
       */
      virtual ACEXML_InputSource *resolveEntity (const ACEXML_Char *publicId,
                                                 const ACEXML_Char *systemId);
    
      // Methods inherit from ACEXML_ErrorHandler.
    
      /*
       * Receive notification of a recoverable error.
       */
      virtual void error (ACEXML_SAXParseException &exception);
    
      /*
       * Receive notification of a non-recoverable error.
       */
      virtual void fatalError (ACEXML_SAXParseException &exception);
    
      /*
       * Receive notification of a warning.
       */
      virtual void warning (ACEXML_SAXParseException &exception);
    
      void inc_indent ();
      void dec_indent ();
      void print_indent ();
    
    private:
    
      size_t indent_;
      ACEXML_Char* fileName_;
      ACEXML_Locator* locator_;
    };

    根据这个类的声明以及注释,可以知道ACEXML的解析是基于事件处理的方式。ACEXML_DefaultHandler 为事件处理的基类,然后用于只需要继承此类,然后实现基类所定义的虚方法即可。

    那么ACEXML_DefaultHandler 类的虚方法们又是在什么时候会触发呢。虽然代码中每个方法都有注释,但是这毕竟太抽象了,至少我凭想象是无法知道每个方法是在什么时候调用的。怎么办,神器——单步调试。

    。。。。。

    虽然单步调试是神器,但是很明显,它在这个地方起不到很大的作用。

    既然深入机理无法了解原理,那么就纵观大局,对总体情况有一个明晰。

    纵观大局——实现自己的Handler类

    纵观大局嘛,给每个从ACEXML_DefaultHandler类派生的虚方法加上日志,记录参数的内容,这样就可以根据所有的日志了解整体的调用过程。

    xml_hanlder代码如下

    #pragma once
    
    #include "ACEXML/common/DefaultHandler.h"
    #include <string>
    #include <iostream>
    using namespace std;
    
    class xml_handler : public ACEXML_DefaultHandler
    {
    public:
        xml_handler(char* path): filename_(path){}
    public:
    
        virtual void characters( const ACEXML_Char *ch, size_t start, size_t length )
        {
            cout << "filename : " << filename_ << ", characters : " << ch << endl;
        }
    
        virtual void endDocument( void )
        {
            cout << "endDocument---------------------------------" << endl;
        }
    
        virtual void endElement( const ACEXML_Char *namespaceURI, const ACEXML_Char *localName, const ACEXML_Char *qName )
        {
            cout << "endElement namespaceURI:" << namespaceURI << ", localName:" << localName << ", qName" << qName << endl;
        }
    
        virtual void endPrefixMapping( const ACEXML_Char *prefix )
        {
            cout << "endPrefixMapping prefix" << prefix << endl;
        }
    
        virtual void ignorableWhitespace( const ACEXML_Char *ch, int start, int length )
        {
            cout << "ignorableWhitespace ch:" << ch << endl;
        }
    
        virtual void processingInstruction( const ACEXML_Char *target, const ACEXML_Char *data )
        {
            cout << "processingInstruction target:" << target << ", data:" << data << endl;
        }
    
        virtual void setDocumentLocator( ACEXML_Locator *locator )
        {
            locator_ = locator;
        }
    
        virtual void skippedEntity( const ACEXML_Char *name )
        {
            cout << "skippedEntity name:" << name << endl;
        }
    
        virtual void startDocument( void )
        {
            cout << "startDocument------------------------------" << endl;
        }
    
        virtual void startElement( const ACEXML_Char *namespaceURI, const ACEXML_Char *localName, const ACEXML_Char *qName, ACEXML_Attributes *atts )
        {
            cout << "startElement namespaceURI:" << namespaceURI << ", localName:" << localName << ", qName" << qName << endl;
        }
    
        virtual void startPrefixMapping( const ACEXML_Char *prefix, const ACEXML_Char *uri )
        {
            cout << "startPrefixmapping prefix:" << prefix << ", uri" << uri << endl;
        }
    
        virtual void notationDecl( const ACEXML_Char *name, const ACEXML_Char *publicId, const ACEXML_Char *systemId )
        {
            cout << "notationDecl name:" << name << ", publicId:" << publicId << ", systemID:" << systemId << endl; 
        }
    
        virtual void unparsedEntityDecl( const ACEXML_Char *name, const ACEXML_Char *publicId, const ACEXML_Char *systemId, const ACEXML_Char *notationName )
        {
            cout << "unparsedEntityDecl name:" << name << ", publicId:" << publicId << ", systemID:" << systemId << ", notationName" << notationName << endl; 
        }
    
        virtual ACEXML_InputSource * resolveEntity( const ACEXML_Char *publicId, const ACEXML_Char *systemId )
        {
            cout << "resolveEntity publicId:" << publicId << ", systemId:" << systemId << endl;
            return 0;
        }
    
        virtual void error( ACEXML_SAXParseException &exception )
        {
            cout << "error" << endl;
        }
    
        virtual void fatalError( ACEXML_SAXParseException &exception )
        {
            cout << "fatalError" << endl;
        }
    
        virtual void warning( ACEXML_SAXParseException &exception )
        {
            cout << "warning" << endl;
        }
    
    private:
    
        size_t indent_;
        string filename_;
        ACEXML_Locator* locator_;
    };

    代码编写完毕,首先编写一个测试用的xml文件

    <?xml version="1.0"?>
    <root>
        <file id="1">test1.txt</file>
        <file id="2">test2.txt</file>
        <file id="3" name="test3" show="true">test3.txt</file>
    </root>

    然后运行程序看一下效果

    ACEXML解析过程示例

    根据程序中输出的信息可以了解整个xml文件解析过程中,handler类中大部分方法的意义以及调用时机。

    Handler类详解

    开始解析的时候会调用”startDocument”方法,解析结束后会调用”endElement”方法。

    解析到每个节点开始的时候会调用”startElement”方法,然后会调用”characters”方法,通过”characters”方法,可以获取到节点的文本内容。

    如果这个节点还有子节点的话,会依次解析子节点。

    节点解析结束后调用”endElement”方法。

    以上就是解析XML时,Handler类的主要方法的调用时机。

    示例xml文件解析流程图

    根据示例中的xml文件,可以整理如下的调用流程图

    示例xml文件解析流程图

    解析file节点的attributes

    根据流程图我们看到,此过程只解析除了file节点的内容,但是没有获得file节点的属性。

    而根据官方的示例程序的截图,看到正确的解析除了所有节点的所有属性,那么我们为什么不参考一下例子程序呢。

    示例程序的startElement方法的代码

    void
    ACEXML_SAXPrint_Handler::startElement (const ACEXML_Char *,
                                           const ACEXML_Char *,
                                           const ACEXML_Char *qName,
                                           ACEXML_Attributes *alist)
    {
    
    
      this->print_indent ();
    
      ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("<%s"),
                  qName));
      if (alist != 0)
        for (size_t i = 0; i < alist->getLength (); ++i)
          {
            ACE_DEBUG ((LM_DEBUG,
                        ACE_TEXT (" %s = "%s""),
                        alist->getQName (i), alist->getValue (i)));
          }
      ACE_DEBUG ((LM_DEBUG, ACE_TEXT (">")));
      this->inc_indent ();
    }

    startElement方法有一个参数alist,其类型为ACEXML_Attributes,这个参数应该就是解析节点时,获取到的属性集合。

    参照此代码,来完善xml_handler类中的startElement方法

    virtual void startElement( const ACEXML_Char *namespaceURI, const ACEXML_Char *localName, const ACEXML_Char *qName, ACEXML_Attributes *atts )
        {
            cout << "startElement namespaceURI:" << namespaceURI << ", localName:" << localName << ", qName" << qName << endl;
            if (atts != 0)
            {
                for (size_t i = 0; i < atts->getLength (); ++i)
                {
                    cout << "localName attributes: " << atts->getQName(i) << " = " << atts->getValue(i) << endl;
                }
            }
        }

    那么我们再来看一下运行效果

    输出示例xml的attributes

    这样我们就可以获取xml文件中所有节点的所有属性和内容,ACEXML解析xml文件的基本方法就掌握了。

    经过完善后的流程图

    示例xml文件完善后的解析流程图

    学习方法总结

    其实ACEXML库是一个非常简单的库,掌握它的使用方法并不难。关键是当接触这样一个完全不了解的库的时候,如何能够迅速熟悉并掌握它,才是本文想要描述的重点。

    下面是我的经验总结:

    1.通过搜索引擎或者专业博客获取介绍信息、使用demo,迅速建立起对这个库的简单的认识。

    2.阅读官方提供的介绍文档。

    3.查看官方提供的示例程序。

    4.参照官方的示例程序,动手实现自己的demo。

    5.总结自己实现demo过程中遇到的问题以及学到的东西。

    系列链接

    ACEXML解析XML文件——我是如何学习并在短时间内掌握一个库的使用方法的

    ACEXML解析XML文件——简单示例程序

  • 相关阅读:
    ZOJ 2859 Matrix Searching
    URAL 1102. Strange Dialog
    ZOJ 1986 Bridging Signals
    POJ 3233 Matrix Power Series
    POJ 1836 Alignment
    POJ 3267 The Cow Lexicon
    ZOJ 3471 Most Powerful
    IIS:HTTP 错误 403.9 禁止访问:连接的用户过多
    使用Command对象执行数据库操作
    C#类型转换
  • 原文地址:https://www.cnblogs.com/hbccdf/p/3502943.html
Copyright © 2011-2022 走看看