浅谈使用Rapidxml 库遇到的问题和分析过程(分享)
C++解析xml的开源库有很多,在此我就不一一列举了,今天主要说下Rapidxml,我使用这个库也并不是很多,如有错误之处还望大家能够之处,谢谢。
附:
官方链接:http://rapidxml.sourceforge.net/
官方手册:http://rapidxml.sourceforge.net/manual.html
之前有一次用到,碰到了个"坑",当时时间紧迫并未及时查找,今天再次用到这个库,对这样的"坑"不能踩第二次,因此我决定探个究竟。
先写两段示例:
创建xm:
voidCreateXml()
{
rapidxml::xml_document<>doc;
autonodeDecl=doc.allocate_node(rapidxml::node_declaration);
nodeDecl->append_attribute(doc.allocate_attribute("version","1.0"));
nodeDecl->append_attribute(doc.allocate_attribute("encoding","UTF-8"));
doc.append_node(nodeDecl);//添加xml声明
autonodeRoot=doc.allocate_node(rapidxml::node_element,"Root");//创建一个Root节点
nodeRoot->append_node(doc.allocate_node(rapidxml::node_comment,NULL,"编程语言"));//添加一个注释内容到Root,注释没有name所以第二个参数为NULL
autonodeLangrage=doc.allocate_node(rapidxml::node_element,"language","ThisisClanguage");//创建一个language节点
nodeLangrage->append_attribute(doc.allocate_attribute("name","C"));//添加一个name属性到language
nodeRoot->append_node(nodeLangrage);//添加一个language到Root节点
nodeLangrage=doc.allocate_node(rapidxml::node_element,"language","ThisisC++language");//创建一个language节点
nodeLangrage->append_attribute(doc.allocate_attribute("name","C++"));//添加一个name属性到language
nodeRoot->append_node(nodeLangrage);//添加一个language到Root节点
doc.append_node(nodeRoot);//添加Root节点到Document
std::stringbuffer;
rapidxml::print(std::back_inserter(buffer),doc,0);
std::ofstreamoutFile("language.xml");
outFile<
结果:
ThisisClanguage
ThisisC++language
修改xml:
voidMotifyXml()
{
rapidxml::file<>requestFile("language.xml");//从文件加载xml
rapidxml::xml_document<>doc;
doc.parse<0>(requestFile.data());//解析xml
autonodeRoot=doc.first_node();//获取第一个节点,也就是Root节点
autonodeLanguage=nodeRoot->first_node("language");//获取Root下第一个language节点
nodeLanguage->first_attribute("name")->value("MotifyC");//修改language节点的name属性为MotifyC
std::stringbuffer;
rapidxml::print(std::back_inserter(buffer),doc,0);
std::ofstreamoutFile("MotifyLanguage.xml");
outFile<
结果:
ThisisClanguage
ThisisC++language
由第二个结果得出:
第一个language的name属性确实改成我们所期望的值了,不过不难发现xml的声明和注释都消失了。是怎么回事呢?这个问题也困扰了我一段时间,既然是开源库,那我们跟一下看看他都干了什么,从代码可以看出可疑的地方主要有两处:print和parse,这两个函数均需要提供一个flag,这个flag到底都干了什么呢,从官方给的教程来看均使用的0,既然最终执行的是print我们就从print开始调试跟踪吧
找到了找到print调用的地方:
template
inlineOutItprint(OutItout,constxml_node&node,intflags=0)
{
returninternal::print_node(out,&node,flags,0);
}
继续跟踪:
//Printnode
template
inlineOutItprint_node(OutItout,constxml_node*node,intflags,intindent)
{
//Printpropernodetype
switch(node->type())
{
//Document
casenode_document:
out=print_children(out,node,flags,indent);
break;
//Element
casenode_element:
out=print_element_node(out,node,flags,indent);
break;
//Data
casenode_data:
out=print_data_node(out,node,flags,indent);
break;
//CDATA
casenode_cdata:
out=print_cdata_node(out,node,flags,indent);
break;
//Declaration
casenode_declaration:
out=print_declaration_node(out,node,flags,indent);
break;
//Comment
casenode_comment:
out=print_comment_node(out,node,flags,indent);
break;
//Doctype
casenode_doctype:
out=print_doctype_node(out,node,flags,indent);
break;
//Pi
casenode_pi:
out=print_pi_node(out,node,flags,indent);
break;
//Unknown
default:
assert(0);
break;
}
//Ifindentingnotdisabled,addlinebreakafternode
if(!(flags&print_no_indenting))
*out=Ch('\n'),++out;
//Returnmodifiediterator
returnout;
}
跟进print_children发现这实际是个递归,我们继续跟踪
//Printelementnode
template
inlineOutItprint_element_node(OutItout,constxml_node*node,intflags,intindent)
{
assert(node->type()==node_element);
//Printelementnameandattributes,ifany
if(!(flags&print_no_indenting))
...//省略部分代码
returnout;
}
我们发现第8行有一个&判断查看print_no_indenting的定义:
//Printingflags
constintprint_no_indenting=0x1;//!
据此我们就可以分析了,按照开发风格统一的思想,parse也应该有相同的标志定义
省略分析parse流程..
我也顺便去查看了官方文档,确实和我预想的一样,贴一下头文件中对这些标志的描述,详细信息可参考官方文档
//Parsingflags
//!Parseflaginstructingtheparsertonotcreatedatanodes.
//!Textoffirstdatanodewillstillbeplacedinvalueofparentelement,unlessrapidxml::parse_no_element_valuesflagisalsospecified.
//!Canbecombinedwithotherflagsbyuseof|operator.
//!
//!Seexml_document::parse()function.
constintparse_no_data_nodes=0x1;
//!Parseflaginstructingtheparsertonotusetextoffirstdatanodeasavalueofparentelement.
//!Canbecombinedwithotherflagsbyuseof|operator.
//!Notethatchilddatanodesofelementnodetakeprecendenceoveritsvaluewhenprinting.
//!Thatis,ifelementhasoneormorechilddatanodesandavalue,thevaluewillbeignored.
//!Userapidxml::parse_no_data_nodesflagtopreventcreationofdatanodesifyouwanttomanipulatedatausingvaluesofelements.
//!
//!Seexml_document::parse()function.
constintparse_no_element_values=0x2;
//!Parseflaginstructingtheparsertonotplacezeroterminatorsafterstringsinthesourcetext.
//!Bydefaultzeroterminatorsareplaced,modifyingsourcetext.
//!Canbecombinedwithotherflagsbyuseof|operator.
//!
//!Seexml_document::parse()function.
constintparse_no_string_terminators=0x4;
//!Parseflaginstructingtheparsertonottranslateentitiesinthesourcetext.
//!Bydefaultentitiesaretranslated,modifyingsourcetext.
//!Canbecombinedwithotherflagsbyuseof|operator.
//!
//!Seexml_document::parse()function.
constintparse_no_entity_translation=0x8;
//!ParseflaginstructingtheparsertodisableUTF-8handlingandassumeplain8bitcharacters.
//!Bydefault,UTF-8handlingisenabled.
//!Canbecombinedwithotherflagsbyuseof|operator.
//!
//!Seexml_document::parse()function.
constintparse_no_utf8=0x10;
//!ParseflaginstructingtheparsertocreateXMLdeclarationnode.
//!Bydefault,declarationnodeisnotcreated.
//!Canbecombinedwithotherflagsbyuseof|operator.
//!
//!Seexml_document::parse()function.
constintparse_declaration_node=0x20;
//!Parseflaginstructingtheparsertocreatecommentsnodes.
//!Bydefault,commentnodesarenotcreated.
//!Canbecombinedwithotherflagsbyuseof|operator.
//!
//!Seexml_document::parse()function.
constintparse_comment_nodes=0x40;
//!ParseflaginstructingtheparsertocreateDOCTYPEnode.
//!Bydefault,doctypenodeisnotcreated.
//!AlthoughW3CspecificationallowsatmostoneDOCTYPEnode,RapidXmlwillsilentlyacceptdocumentswithmorethanone.
//!Canbecombinedwithotherflagsbyuseof|operator.
//!
//!Seexml_document::parse()function.
constintparse_doctype_node=0x80;
//!ParseflaginstructingtheparsertocreatePInodes.
//!Bydefault,PInodesarenotcreated.
//!Canbecombinedwithotherflagsbyuseof|operator.
//!
//!Seexml_document::parse()function.
constintparse_pi_nodes=0x100;
//!Parseflaginstructingtheparsertovalidateclosingtagnames.
//!Ifnotset,nameinsideclosingtagisirrelevanttotheparser.
//!Bydefault,closingtagsarenotvalidated.
//!Canbecombinedwithotherflagsbyuseof|operator.
//!
//!Seexml_document::parse()function.
constintparse_validate_closing_tags=0x200;
//!Parseflaginstructingtheparsertotrimallleadingandtrailingwhitespaceofdatanodes.
//!Bydefault,whitespaceisnottrimmed.
//!Thisflagdoesnotcausetheparsertomodifysourcetext.
//!Canbecombinedwithotherflagsbyuseof|operator.
//!
//!Seexml_document::parse()function.
constintparse_trim_whitespace=0x400;
//!Parseflaginstructingtheparsertocondenseallwhitespacerunsofdatanodestoasinglespacecharacter.
//!Trimmingofleadingandtrailingwhitespaceofdataiscontrolledbyrapidxml::parse_trim_whitespaceflag.
//!Bydefault,whitespaceisnotnormalized.
//!Ifthisflagisspecified,sourcetextwillbemodified.
//!Canbecombinedwithotherflagsbyuseof|operator.
//!
//!Seexml_document::parse()function.
constintparse_normalize_whitespace=0x800;
//Compoundflags
//!Parseflagswhichrepresentdefaultbehaviouroftheparser.
//!Thisisalwaysequalto0,sothatallotherflagscanbesimplyoredtogether.
//!Normallythereisnoneedtoinconvenientlydisableflagsbyandingwiththeirnegated(~)values.
//!Thisalsomeansthatmeaningofeachflagisanegationofthedefaultsetting.
//!Forexample,ifflagnameisrapidxml::parse_no_utf8,itmeansthatutf-8isenabledbydefault,
//!andusingtheflagwilldisableit.
//!
//!Seexml_document::parse()function.
constintparse_default=0;
//!Acombinationofparseflagsthatforbidsanymodificationsofthesourcetext.
//!Thisalsoresultsinfasterparsing.However,notethatthefollowingwilloccur:
//!
//!- namesandvaluesofnodeswillnotbezeroterminated,youhavetousexml_base::name_size()andxml_base::value_size()functionstodeterminewherenameandvalueends
//!- entitieswillnotbetranslated
//!- whitespacewillnotbenormalized
//!
//!Seexml_document::parse()function.
constintparse_non_destructive=parse_no_string_terminators|parse_no_entity_translation;
//!Acombinationofparseflagsresultinginfastestpossibleparsing,withoutsacrificingimportantdata.
//!
//!Seexml_document::parse()function.
constintparse_fastest=parse_non_destructive|parse_no_data_nodes;
//!Acombinationofparseflagsresultinginlargestamountofdatabeingextracted.
//!Thisusuallyresultsinslowestparsing.
//!
//!Seexml_document::parse()function.
constintparse_full=parse_declaration_node|parse_comment_nodes|parse_doctype_node|parse_pi_nodes|parse_validate_closing_tags;
根据以上提供的信息我们改下之前的源代码:
将
doc.parse<0>(requestFile.data());//解析xml
autonodeRoot=doc.first_node("");//获取第一个节点,也就是Root节点
改为
doc.parse(requestFile.data());//解析xml
autonodeRoot=doc.first_node("Root");//获取第一个节点,也就是Root节点
这里解释一下,parse加入了三个标志,分别是告诉解析器创建声明节点、告诉解析器创建注释节点、和不希望解析器修改传进去的数据,第二句是当有xml的声明时,默认的first_node并不是我们期望的Root节点,因此通过传节点名来找到我们需要的节点。
注:
1、这个库在append的时候并不去判断添加项(节点、属性等)是否存在
2、循环遍历时对项(节点、属性等)进行修改会导致迭代失效
总结:用别人写的库,总会有些意想不到的问题,至今我只遇到了这些问题,如果还有其它问题欢迎补充,顺便解释下"坑"并不一定是用的开源库有问题,更多的时候可能是还没有熟练的去使用这个工具。
感谢rapidxml的作者,为我们提供一个如此高效便利的工具。
以上这篇浅谈使用Rapidxml库遇到的问题和分析过程(分享)就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持毛票票。