在上一篇文章中分析了如何把XML文件解析为一棵树,现在就来分析一下tinyXML是如何利用这个树输出XML文件,以及更进一步的更改XML文件的内容的。
首先介绍两个用于输出的工具类,以及一个遍历树的函数:
- TiXmlVisitor ,这个类是一个基类,没有数据成员,只定义了一些虚函数。
- TiXmlPrinter,是TiXmlVisitor的派生类,重载了TiXmlVisitor中的visit函数,根据要打印节点的类型采取不同的打印策略打印,并增加了5个数据成员用来设置输出格式。
int depth;//结点距根节点的层数
bool simpleTextPrint;//是否
TIXML_STRING buffer;//缓冲区
TIXML_STRING indent;//缩进
TIXML_STRING lineBreak;//换行符
- Accept( TiXmlVisitor* visitor )函数,接受一个TiXmlVisitor指针,然后调用TiXmlVisitor中针对不同的结点的不同打印方法。Accept函数存在的目的就是为了确定不同的节点类型,以方便TiXmlvisitor调用对应的方法。
document的Accept()函数调用了document版本的VisitEnter函数,然后遍历document的孩子节点,并对不同的节点分别调用对应版本的Accept()和visit函数,输出节点包含的内容
仍然以下面这个解析树为例
TiXmlDocument "demo.xml"
TiXmlDeclaration "version='1.0'" "standalone=no"
TiXmlComment " Our to do list data"
TiXmlElement "ToDo"
TiXmlElement "Item" Attribtutes: priority = 1
TiXmlText "Go to the "
TiXmlElement "bold"
TiXmlText "Toy store!"
TiXmlElement "Item" Attributes: priority=2
TiXmlText "Do bills"
假设TiXmlDocument doc 中已经生成好一棵解析树。
- 首先生成一个TiXmlPrinter printer;默认构造函数就好,以默认的方式打印
- 然后调用TiXmlDocument 的Accept函数–doc.Accept(&printer);告诉printer,是要打印document中的内容。这个版本的Accept遍历document的子节点,分别调用各个子节点版本的Accept函数。如下所示
bool TiXmlDocument::Accept( TiXmlVisitor* visitor ) const
{
if ( visitor->VisitEnter( *this ) )
{
for ( const TiXmlNode* node=FirstChild(); node; node=node->NextSibling() )
{
if ( !node->Accept( visitor ) )
break;
}
}
return visitor->VisitExit( *this );
}
- for循环第一次node类型为 TiXmlDeclaration,调用 TiXmlDeclaration版本的Accept,再调用visit,把 TiXmlDeclaration中保存的version,encoding,standalone按照次序打印出来。
- for循环第二次node的类型为TiXmlComment ,调用TiXmlComment 版本的Accept和visit,把TiXmlComment 中的value打印出来即可。
- for循环第三次node的类型为 TiXmlElement, TiXmlElement版本的Accept和TiXmlDocument的有些类似,都需要遍历自己的子节点,但是TiXmlElement需要先把Element的值打印出来,然后再遍历。
TiXmlElement中有几个点需要注意:
首先每进去一层Element,都需要多打几个空格,表示不同的层次有不同的缩进。
如果Element中没有子节点,则直接输出一个对尖括号,不必像XML标准格式一样输出开始和结束标志。例如输出< Item Attributes: priority=2/>而不是< Item Attributes: priority=2> < Item/>
Element和document这两种内部包含其他结点的类型,最后要VisitEnter进去,再VisitExit出去。原因是,内部不含节点的类型开始标志"< Item"和结束标志 “/>“靠的很近,可以直接输出。而Element和document的必须先输出完内部节点之后才能找到”/>”,所以必须单独实施退出操作。
在了解了解析树是如何生成,以及如何打印之后,如何添加节点,更改节点内容就是水到渠成的事了,这里就不再赘述。
遇到的问题:
- document的Accept()函数中
if ( !node->Accept( visitor ) )
这一句在node=NULL的情况下不会报错吗?
2.为什么TiXmlVisitor中成员函数要定义成virtual?