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
根据打印出来的信息与内置的字符串比较,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>
然后运行程序看一下效果
根据程序中输出的信息可以了解整个xml文件解析过程中,handler类中大部分方法的意义以及调用时机。
Handler类详解
开始解析的时候会调用”startDocument”方法,解析结束后会调用”endElement”方法。
解析到每个节点开始的时候会调用”startElement”方法,然后会调用”characters”方法,通过”characters”方法,可以获取到节点的文本内容。
如果这个节点还有子节点的话,会依次解析子节点。
节点解析结束后调用”endElement”方法。
以上就是解析XML时,Handler类的主要方法的调用时机。
示例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文件中所有节点的所有属性和内容,ACEXML解析xml文件的基本方法就掌握了。
经过完善后的流程图
学习方法总结
其实ACEXML库是一个非常简单的库,掌握它的使用方法并不难。关键是当接触这样一个完全不了解的库的时候,如何能够迅速熟悉并掌握它,才是本文想要描述的重点。
下面是我的经验总结:
1.通过搜索引擎或者专业博客获取介绍信息、使用demo,迅速建立起对这个库的简单的认识。
2.阅读官方提供的介绍文档。
3.查看官方提供的示例程序。
4.参照官方的示例程序,动手实现自己的demo。
5.总结自己实现demo过程中遇到的问题以及学到的东西。
系列链接
ACEXML解析XML文件——我是如何学习并在短时间内掌握一个库的使用方法的
ACEXML解析XML文件——我是如何学习并在短时间内掌握一个库的使用方法的的更多相关文章
- ACEXML解析XML文件——简单示例程序
掌握了ACMXML库解析XML文件的方法后,下面来实现一个比较完整的程序. 定义基本结构 xml文件格式如下 <?xml version="1.0"?> <roo ...
- XML:使用DOM技术解析xML文件中的城市,实现select级联选择
中国的城市xml格式:cities.xml <?xml version="1.0" encoding="utf-8"?> <china> ...
- 在tomcat启动时解析xml文件,获取特定标签的属性值,并将属性值设置到静态变量里
这里以解析hibernate.cfg.xml数据库配置信息为例,运用dom4j的解析方式来解析xml文件. 1.在javaWeb工程里新建一个java类,命名为GetXmlValue.java,为xm ...
- 【Android学习笔记】XmlResourceParser解析xml文件
最近学习Android时,需要用到解析XML文件里的数据,可以用XmlResourceParser来解析xml文件,正好将此记录下来. XmlResourceParser里常用的字段和方法 首先先给出 ...
- Android开发学习---使用XmlPullParser解析xml文件
Android中解析XML的方式主要有三种:sax,dom和pull关于其内容可参考:http://blog.csdn.net/liuhe688/article/details/6415593 本文将 ...
- cocos2d-x 3.0 使用Sax解析xml文件(中国显示器问题解决)
今天是个好日子.我以为事情可以变得,明天是个好日子.打开门儿春风... 恩,听着歌写文档生活就是这么享受. 今天曾经的邻居大神突然在qq上赞了我一下,这让我异常激动啊.. 这还要从前前前几天说起,那会 ...
- 用js解析XML文件,字符串一些心得
解析XML文件遇到的问题 今天秦博士叫我解析一下XML文件,将里面的所有的X坐标Y坐标放在一个数组里面然后写在文档里让他进行算法比对,大家都知道了啦,解析XML文件获取里面的坐标数据什么的,当然是用前 ...
- 遍历文件 创建XML对象 方法 python解析XML文件 提取坐标计存入文件
XML文件??? xml即可扩展标记语言,它可以用来标记数据.定义数据类型,是一种允许用户对自己的标记语言进行定义的源语言. 里面的标签都是可以随心所欲的按照他的命名规则来定义的,文件名为roi.xm ...
- 用js(JavaScript-jQuery)解析XML文件 无法成功 获得XML对象,字符串一些心得
原文作者:aircraft 原文地址:https://www.cnblogs.com/DOMLX/p/7822962.html 解析XML文件遇到的问题 今天秦博士叫我解析一下XML文件,将里面的所有 ...
随机推荐
- ViewPager循环显示
好久没有写博客了,今天加一个ViewPager页面的循环显示,添加了一个删除页面的小按钮: MainActivity.java package com.yt.viewpagerlooper; impo ...
- JS 传值 传址
在JS中,有两种不同的方式可以操作数据的值,这两种技术分别叫做 传值 和 传址. 传值:在赋值过程中,首先对值进行了一份拷贝,而后将这份拷贝存储到一个变量.对象属性或数组元素中.拷贝的值和原始的值是完 ...
- [OSI]网络间通信流程
PC 连接交换机A,组成内网.DNS Serv 和 Web Serv 连接交换机B 组成外网. 示意图: 内网通信 PC1 到 PC2: PC1 发送的数据先到交换机A,交换机A没有ARP地址缓存表, ...
- 如何去掉div滚动条
1.去掉横向滚动条 style="overflow-x:hidden" 2.去掉纵向滚动条 style="overflow-y:hidden" 3.同时去掉横向 ...
- 转:eclipse以及step into step over step return的区别
首先来讲一下step into step over step return的区别: step into就是单步执行,遇到子函数就进入并且继续单步执行:(F5) step over是在单步执行时,在函数 ...
- Dojo框架学习笔记<二>
一.dojo/dom 该模块定义了Dojo Dom API,主要有以下几种用法: 1.dom.byId();(相当于document.getElementById()) ①最直接的用 ...
- ASP.NET Core 1.0 入门——了解一个空项目
var appInsights=window.appInsights||function(config){ function r(config){t[config]=function(){var i= ...
- 【转】Python中的赋值、浅拷贝、深拷贝介绍
这篇文章主要介绍了Python中的赋值.浅拷贝.深拷贝介绍,Python中也分为简单赋值.浅拷贝.深拷贝这几种"拷贝"方式,需要的朋友可以参考下 和很多语言一样,Python中 ...
- css横向导航条
css横向导航条有两种方法 1. ul li a li{float:left} #navlist li, #navlist a{height:44px;display:block;} a{width: ...
- Android 把电话保存到现有联系人 已有联系人
搜索了很长时间,想找个把电话保存到现有联系人的代码,就是打开选中的联系人编辑界面,然后自动添加电话,再手动保存,就跟手机上的一样,功夫不负有心人,终于给搜到了,很不容易啊,现分享如下, // 保存至现 ...