tinyxml2源码分析-1

换一种方式来对源码分析,从零开始“写”一个tinyxml2。提前郑重声明,本博客所有tinyxml代码都直接copy自tinyxml源码。

在开始分析源码前,如下图所示,是tinyxml2的各个主要类型,XMLAttribute其实是一个单向链表,放在后面实现。

tinyxml2把xml文档建立成一棵DOM树,具体实现用的是firstchild–nextsiblingtree,如下图


下面的xml文件:

<?This is a Declaration!?>
<!--This is a Document Comment!-->
<School>
    <!--This is a School Comment!-->
    <teachers>
        <Wang/>
        <Li/>
        <Zhao/>
    </teachers>
    <students>
        <LiMing sex="male" height="174" weight="80.400000000000006" Is_good_at_math="false">Li Ming is a good Student!</LiMing>
        <CuiHua>this is a Text!</CuiHua>
        <Hanmeimei><![CDATA[this is a CData Text:if (a < b && a < 0)]]></Hanmeimei>
    </students>
    <!this is a Unknow!>
</School>
将被建立成DOM树如下,蓝色:_firstChild,褐色_lastChild,绿色:_next,紫色:_prev,剩下的_parent


所以,先从XMLNode开始编写,如上图,一个节点必须有_parent;_firstChild;_lastChild;_prev;_next;成员变量,所以,我们先定义XMLNode类型,如下所示:

class XMLNode
{
public:
protected:
	XMLNode();
	virtual ~XMLNode();
	XMLNode*		_parent;
	XMLNode*		_firstChild;
	XMLNode*		_lastChild;
	XMLNode*		_prev;
	XMLNode*		_next;
	XMLDocument*	_document;
private:
	MemPool*		_memPool;
	XMLNode(const XMLNode&);	// not supported
	XMLNode& operator=(const XMLNode&);	// not supported
};

但是多一个元素XMLDocument* _document,这个主要是因为tinyxml2的内存由_document管理,添加子节点等时需要判断是否是同要给Document。首先,构造函数和析构函数为protected,因为XMLNode类型用户不需要实例化,只需要实例化子类型即可。一个节点必须有的5个节点指针,还有,禁止拷贝构造和赋值。

有了上面这些元素,我们就可以编写最基本的接口:

	const XMLDocument* GetDocument() const	{
		TIXMLASSERT(_document);
		return _document;
	}
	/// Get the XMLDocument that owns this XMLNode.
	XMLDocument* GetDocument(){
		TIXMLASSERT(_document);
		return _document;
	}

	/// Get the parent of this node on the DOM.
	const XMLNode*	Parent() const{
		return _parent;
	}

	XMLNode* Parent(){
		return _parent;
	}

	/// Returns true if this node has no children.
	bool NoChildren() const{
		return !_firstChild;
	}

	/// Get the first child node, or null if none exists.
	const XMLNode*  FirstChild() const{
		return _firstChild;
	}

	XMLNode*		FirstChild(){
		return _firstChild;
	}
	/// Get the last child node, or null if none exists.
	const XMLNode*	LastChild() const{
		return _lastChild;
	}

	XMLNode*		LastChild()	{
		return _lastChild;
	}
	/// Get the previous (left) sibling node of this node.
	const XMLNode*	PreviousSibling() const	{
		return _prev;
	}

	XMLNode*	PreviousSibling(){
		return _prev;
	}
	/// Get the next (right) sibling node of this node.
	const XMLNode*	NextSibling() const{
		return _next;
	}

	XMLNode*	NextSibling(){
		return _next;
	}

好像什么都没做,但是有了上面的接口,我们就可以遍历整个DOM树,可是还少点什么。首先,InsertChildPreamble,在插入节点前,必须断开连接,so,代码如下:

void XMLNode::InsertChildPreamble(XMLNode* insertThis) const
{
	TIXMLASSERT(insertThis);
	TIXMLASSERT(insertThis->_document == _document);

	if (insertThis->_parent)
		insertThis->_parent->Unlink(insertThis);
	else
		insertThis->_memPool->SetTracked();
}
如果父节点不为NULL,那么将连接断开,memPool以后分析。Unlink一个Child,只需要断开_parent;_prev;_next;所以如下:

void XMLNode::Unlink(XMLNode* child)
{
	TIXMLASSERT(child);
	TIXMLASSERT(child->_document == _document);
	TIXMLASSERT(child->_parent == this);
	if (child == _firstChild) {
		_firstChild = _firstChild->_next;
	}
	if (child == _lastChild) {
		_lastChild = _lastChild->_prev;
	}

	if (child->_prev) {
		child->_prev->_next = child->_next;
	}
	if (child->_next) {
		child->_next->_prev = child->_prev;
	}
	child->_parent = 0;
}
在修改_prev,_next前,先检查是否是父节点的_firstChild;_lastChild

InsertFirstChild,就是插入第一个孩子。下面是注释:

Add a child node as the first (left) child.If the child node is already part of the document,it is moved from its old location to the new location.
Returns the addThis argument or 0 if the node does not belong to the same document.

XMLNode* XMLNode::InsertFirstChild(XMLNode* addThis)
{
	TIXMLASSERT(addThis);
	if (addThis->_document != _document) {
		TIXMLASSERT(false);
		return 0;
	}
	InsertChildPreamble(addThis);

	if (_firstChild) {
		TIXMLASSERT(_lastChild);
		TIXMLASSERT(_firstChild->_prev == 0);

		_firstChild->_prev = addThis;
		addThis->_next = _firstChild;
		_firstChild = addThis;

		addThis->_prev = 0;
	}
	else {
		TIXMLASSERT(_lastChild == 0);
		_firstChild = _lastChild = addThis;

		addThis->_prev = 0;
		addThis->_next = 0;
	}
	addThis->_parent = this;
	return addThis;
}
首先检查是否是同一个Document,在插入节点前,删除节点自身的连接,如果_firstChild不为NULL,那么插入首节点,否则,该节点没用孩子,将_firstChild和_lastChild都置为插入节点。

InsertAfterChild

Add a node after the specified child node.If the child node is already part of the document,it is moved from its old location to the new location.
Returns the addThis argument or 0 if the afterThis node is not a child of this node, or if the node does not belong to the same document.

XMLNode* XMLNode::InsertAfterChild(XMLNode* afterThis, XMLNode* addThis)
{
	TIXMLASSERT(addThis);
	if (addThis->_document != _document) {
		TIXMLASSERT(false);
		return 0;
	}

	TIXMLASSERT(afterThis);

	if (afterThis->_parent != this) {
		TIXMLASSERT(false);
		return 0;
	}

	if (afterThis->_next == 0) {
		// The last node or the only node.
		return InsertEndChild(addThis);
	}
	InsertChildPreamble(addThis);
	addThis->_prev = afterThis;
	addThis->_next = afterThis->_next;
	afterThis->_next->_prev = addThis;
	afterThis->_next = addThis;
	addThis->_parent = this;
	return addThis;
}
InsertEndChild:

Add a child node as the last (right) child.If the child node is already part of the document,it is moved from its old location to the new location.
Returns the addThis argument or 0 if the node does not belong to the same document.

XMLNode* XMLNode::InsertEndChild(XMLNode* addThis)
{
	TIXMLASSERT(addThis);
	if (addThis->_document != _document) {
		TIXMLASSERT(false);
		return 0;
	}
	InsertChildPreamble(addThis);

	if (_lastChild) {
		TIXMLASSERT(_firstChild);
		TIXMLASSERT(_lastChild->_next == 0);
		_lastChild->_next = addThis;
		addThis->_prev = _lastChild;
		_lastChild = addThis;

		addThis->_next = 0;
	}
	else {
		TIXMLASSERT(_firstChild == 0);
		_firstChild = _lastChild = addThis;

		addThis->_prev = 0;
		addThis->_next = 0;
	}
	addThis->_parent = this;
	return addThis;
}
检查是否同源,然后添加 。

	XMLNode* LinkEndChild(XMLNode* addThis)	{
		return InsertEndChild(addThis);
	}

DeleteChild:

void XMLNode::DeleteChild(XMLNode* node)
{
	TIXMLASSERT(node);
	TIXMLASSERT(node->_document == _document);
	TIXMLASSERT(node->_parent == this);
	Unlink(node);
	DeleteNode(node);
}

断开链接,再DeleteNode

void XMLNode::DeleteNode(XMLNode* node)
{
	if (node == 0) {
		return;
	}
	MemPool* pool = node->_memPool;
	node->~XMLNode();
	pool->Free(node);
}

MemPool是一个内存池接口类,代码如下:

class MemPool
{
public:
	MemPool() {}
	virtual ~MemPool() {}
	virtual int ItemSize() const = 0;
	virtual void* Alloc() = 0;
	virtual void Free(void*) = 0;
	virtual void SetTracked() = 0;
	virtual void Clear() = 0;
};

DeleteChildren:

void XMLNode::DeleteChildren()
{
	while (_firstChild) {
		TIXMLASSERT(_lastChild);
		DeleteChild(_firstChild);
	}
	_firstChild = _lastChild = 0;
}

至此,我们的DOM已经支持插入节点,删除节点。

如果向下转型,可能想到的是dynamic_cast,但是也可以在基类中定义转换到子类的函数,再由子类去实现自己的转换函数,这样做违背开放封闭原则,当我们添加新的子类时,就需要修改基类。可是这里是例外,因为XMLNode的子类在可预见的时间内只有这么多子类,这也是可以使用Visitor模式的条件。

	/// Safely cast to an Element, or null.
	virtual XMLElement*		ToElement(){
		return 0;
	}
	/// Safely cast to Text, or null.
	virtual XMLText*		ToText(){
		return 0;
	}
	/// Safely cast to a Comment, or null.
	virtual XMLComment*		ToComment(){
		return 0;
	}
	/// Safely cast to a Document, or null.
	virtual XMLDocument*	ToDocument(){
		return 0;
	}
	/// Safely cast to a Declaration, or null.
	virtual XMLDeclaration*	ToDeclaration()	{
		return 0;
	}
	/// Safely cast to an Unknown, or null.
	virtual XMLUnknown*		ToUnknown(){
		return 0;
	}

	virtual const XMLElement* ToElement() const{
		return 0;
	}
	virtual const XMLText* ToText() const{
		return 0;
	}
	virtual const XMLComment* ToComment() const{
		return 0;
	}
	virtual const XMLDocument* ToDocument() const{
		return 0;
	}
	virtual const XMLDeclaration* ToDeclaration() const	{
		return 0;
	}
	virtual const XMLUnknown* ToUnknown() const{
		return 0;
	}


XMLText,XMLAlement,XMLDocument,都可以has a XMLAlement,所以增加转化为XMLAlement的接口。所有的函数默认返回0,当合适的时候返回正确的指针。

	/** Get the first child element, or optionally the first child
	element with the specified name.
	*/
	const XMLElement* FirstChildElement(const char* name = 0) const;

	XMLElement* FirstChildElement(const char* name = 0)	{
		return const_cast<XMLElement*>(const_cast<const XMLNode*>(this)->FirstChildElement(name));
	}
	/** Get the last child element or optionally the last child
	element with the specified name.
	*/
	const XMLElement* LastChildElement(const char* name = 0) const;

	XMLElement* LastChildElement(const char* name = 0)	{
		return const_cast<XMLElement*>(const_cast<const XMLNode*>(this)->LastChildElement(name));
	}
	/// Get the previous (left) sibling element of this node, with an optionally supplied name.
	const XMLElement*	PreviousSiblingElement(const char* name = 0) const;

	XMLElement*	PreviousSiblingElement(const char* name = 0) {
		return const_cast<XMLElement*>(const_cast<const XMLNode*>(this)->PreviousSiblingElement(name));
	}
	/// Get the next (right) sibling element of this node, with an optionally supplied name.
	const XMLElement*	NextSiblingElement(const char* name = 0) const;

	XMLElement*	NextSiblingElement(const char* name = 0)	{
		return const_cast<XMLElement*>(const_cast<const XMLNode*>(this)->NextSiblingElement(name));
	}
	/** Get the first child element, or optionally the first child
	element with the specified name.
	*/
	const XMLElement* FirstChildElement(const char* name = 0) const;

	XMLElement* FirstChildElement(const char* name = 0)	{
		return const_cast<XMLElement*>(const_cast<const XMLNode*>(this)->FirstChildElement(name));
	}
	/** Get the last child element or optionally the last child
	element with the specified name.
	*/
	const XMLElement* LastChildElement(const char* name = 0) const;

	XMLElement* LastChildElement(const char* name = 0)	{
		return const_cast<XMLElement*>(const_cast<const XMLNode*>(this)->LastChildElement(name));
	}
	/// Get the previous (left) sibling element of this node, with an optionally supplied name.
	const XMLElement*	PreviousSiblingElement(const char* name = 0) const;

	XMLElement*	PreviousSiblingElement(const char* name = 0) {
		return const_cast<XMLElement*>(const_cast<const XMLNode*>(this)->PreviousSiblingElement(name));
	}
	/// Get the next (right) sibling element of this node, with an optionally supplied name.
	const XMLElement*	NextSiblingElement(const char* name = 0) const;

	XMLElement*	NextSiblingElement(const char* name = 0)	{
		return const_cast<XMLElement*>(const_cast<const XMLNode*>(this)->NextSiblingElement(name));
	}
默认均返回0,由子类去实现自己类型的转换,其他使用默认即可。至此,我们的XMLNode暂时先完成一个阶段的代码。

class XMLNode
{
public:

	/// Safely cast to an Element, or null.  
	virtual XMLElement*     ToElement(){
		return 0;
	}
	/// Safely cast to Text, or null.  
	virtual XMLText*        ToText(){
		return 0;
	}
	/// Safely cast to a Comment, or null.  
	virtual XMLComment*     ToComment(){
		return 0;
	}
	/// Safely cast to a Document, or null.  
	virtual XMLDocument*    ToDocument(){
		return 0;
	}
	/// Safely cast to a Declaration, or null.  
	virtual XMLDeclaration* ToDeclaration() {
		return 0;
	}
	/// Safely cast to an Unknown, or null.  
	virtual XMLUnknown*     ToUnknown(){
		return 0;
	}

	virtual const XMLElement* ToElement() const{
		return 0;
	}
	virtual const XMLText* ToText() const{
		return 0;
	}
	virtual const XMLComment* ToComment() const{
		return 0;
	}
	virtual const XMLDocument* ToDocument() const{
		return 0;
	}
	virtual const XMLDeclaration* ToDeclaration() const {
		return 0;
	}
	virtual const XMLUnknown* ToUnknown() const{
		return 0;
	}
	const XMLDocument* GetDocument() const  {
		TIXMLASSERT(_document);
		return _document;
	}
	/// Get the XMLDocument that owns this XMLNode.  
	XMLDocument* GetDocument(){
		TIXMLASSERT(_document);
		return _document;
	}

	/// Get the parent of this node on the DOM.  
	const XMLNode*  Parent() const{
		return _parent;
	}

	XMLNode* Parent(){
		return _parent;
	}

	/// Returns true if this node has no children.  
	bool NoChildren() const{
		return !_firstChild;
	}

	/// Get the first child node, or null if none exists.  
	const XMLNode*  FirstChild() const{
		return _firstChild;
	}

	XMLNode*        FirstChild()            {
		return _firstChild;
	} 
	const XMLNode*  LastChild() const{
		return _lastChild;
	}

	XMLNode*        LastChild(){
		return _lastChild;
	}  
	const XMLNode*  PreviousSibling() const {
		return _prev;
	}

	XMLNode*    PreviousSibling(){
		return _prev;
	}
	const XMLNode*  NextSibling() const {
		return _next;
	}

	XMLNode*    NextSibling(){
		return _next;
	}

	const XMLElement* FirstChildElement(const char* name = 0) const;

	XMLElement* FirstChildElement(const char* name = 0)	{
		return const_cast<XMLElement*>(const_cast<const XMLNode*>(this)->FirstChildElement(name));
	}

	const XMLElement* LastChildElement(const char* name = 0) const;

	XMLElement* LastChildElement(const char* name = 0)	{
		return const_cast<XMLElement*>(const_cast<const XMLNode*>(this)->LastChildElement(name));
	}
	
	const XMLElement*	PreviousSiblingElement(const char* name = 0) const;

	XMLElement*	PreviousSiblingElement(const char* name = 0) {
		return const_cast<XMLElement*>(const_cast<const XMLNode*>(this)->PreviousSiblingElement(name));
	}
	const XMLElement*	NextSiblingElement(const char* name = 0) const;

	XMLElement*	NextSiblingElement(const char* name = 0)	{
		return const_cast<XMLElement*>(const_cast<const XMLNode*>(this)->NextSiblingElement(name));
	}

	XMLNode* InsertFirstChild(XMLNode* addThis);
	XMLNode* InsertAfterChild(XMLNode* afterThis, XMLNode* addThis);
	XMLNode* InsertEndChild(XMLNode* addThis);

	XMLNode* LinkEndChild(XMLNode* addThis) {
		return InsertEndChild(addThis);
	}
	void DeleteChildren();
	void DeleteChild(XMLNode* node);
protected:
	XMLNode(XMLDocument*);
	virtual ~XMLNode();
	XMLNode*        _parent;
	XMLNode*        _firstChild;
	XMLNode*        _lastChild;
	XMLNode*        _prev;
	XMLNode*        _next;
	XMLDocument*    _document;
private:
	MemPool*        _memPool;
	void Unlink(XMLNode* child);
	static void DeleteNode(XMLNode* node);
	void InsertChildPreamble(XMLNode* insertThis) const;
	XMLNode(const XMLNode&);    // not supported  
	XMLNode& operator=(const XMLNode&); // not supported  
};


汗,前面构造函数忘记加document的指针了。

cpp

XMLNode::XMLNode(XMLDocument* doc) :
_document(doc),
_parent(0),
_firstChild(0), _lastChild(0),
_prev(0), _next(0),
_memPool(0)
{
}

XMLNode::~XMLNode()
{
}
void XMLNode::Unlink(XMLNode* child)
{
	TIXMLASSERT(child);
	TIXMLASSERT(child->_document == _document);
	TIXMLASSERT(child->_parent == this);
	if (child == _firstChild) {
		_firstChild = _firstChild->_next;
	}
	if (child == _lastChild) {
		_lastChild = _lastChild->_prev;
	}

	if (child->_prev) {
		child->_prev->_next = child->_next;
	}
	if (child->_next) {
		child->_next->_prev = child->_prev;
	}
	child->_parent = 0;
}
void XMLNode::DeleteNode(XMLNode* node)
{
	if (node == 0) {
		return;
	}
	MemPool* pool = node->_memPool;
	node->~XMLNode();
	pool->Free(node);
}

void XMLNode::InsertChildPreamble(XMLNode* insertThis) const
{
	TIXMLASSERT(insertThis);
	TIXMLASSERT(insertThis->_document == _document);

	if (insertThis->_parent)
		insertThis->_parent->Unlink(insertThis);
	else
		insertThis->_memPool->SetTracked();
}
XMLNode* XMLNode::InsertEndChild(XMLNode* addThis)
{
	TIXMLASSERT(addThis);
	if (addThis->_document != _document) {
		TIXMLASSERT(false);
		return 0;
	}
	InsertChildPreamble(addThis);

	if (_lastChild) {
		TIXMLASSERT(_firstChild);
		TIXMLASSERT(_lastChild->_next == 0);
		_lastChild->_next = addThis;
		addThis->_prev = _lastChild;
		_lastChild = addThis;

		addThis->_next = 0;
	}
	else {
		TIXMLASSERT(_firstChild == 0);
		_firstChild = _lastChild = addThis;

		addThis->_prev = 0;
		addThis->_next = 0;
	}
	addThis->_parent = this;
	return addThis;
}
XMLNode* XMLNode::InsertFirstChild(XMLNode* addThis)
{
	TIXMLASSERT(addThis);
	if (addThis->_document != _document) {
		TIXMLASSERT(false);
		return 0;
	}
	InsertChildPreamble(addThis);

	if (_firstChild) {
		TIXMLASSERT(_lastChild);
		TIXMLASSERT(_firstChild->_prev == 0);

		_firstChild->_prev = addThis;
		addThis->_next = _firstChild;
		_firstChild = addThis;

		addThis->_prev = 0;
	}
	else {
		TIXMLASSERT(_lastChild == 0);
		_firstChild = _lastChild = addThis;

		addThis->_prev = 0;
		addThis->_next = 0;
	}
	addThis->_parent = this;
	return addThis;
}


XMLNode* XMLNode::InsertAfterChild(XMLNode* afterThis, XMLNode* addThis)
{
	TIXMLASSERT(addThis);
	if (addThis->_document != _document) {
		TIXMLASSERT(false);
		return 0;
	}

	TIXMLASSERT(afterThis);

	if (afterThis->_parent != this) {
		TIXMLASSERT(false);
		return 0;
	}

	if (afterThis->_next == 0) {
		// The last node or the only node.
		return InsertEndChild(addThis);
	}
	InsertChildPreamble(addThis);
	addThis->_prev = afterThis;
	addThis->_next = afterThis->_next;
	afterThis->_next->_prev = addThis;
	afterThis->_next = addThis;
	addThis->_parent = this;
	return addThis;
}
void XMLNode::DeleteChild(XMLNode* node)
{
	TIXMLASSERT(node);
	TIXMLASSERT(node->_document == _document);
	TIXMLASSERT(node->_parent == this);
	Unlink(node);
	DeleteNode(node);
}
void XMLNode::DeleteChildren()
{
	while (_firstChild) {
		TIXMLASSERT(_lastChild);
		DeleteChild(_firstChild);
	}
	_firstChild = _lastChild = 0;
}













  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
tinyxml2 是一个轻量级的 C++ XML 解析库,它提供了一个简单的 API,用于解析、修改和创建 XML 文档。下面是对 tinyxml2 源码的一些分析: 1. 文件结构 tinyxml2 的源码包括以下文件: - tinyxml2.h:包含 tinyxml2 的 API。 - tinyxml2.cpp:包含 tinyxml2 的实现代码。 - xmltest.cpp:包含 tinyxml2 的测试代码。 2. 类结构 tinyxml2 基于 DOM(文档对象模型)模式,它将 XML 文档表示为一棵树。以下是 tinyxml2 的主要类: - XMLDocument:表示整个 XML 文档,包括所有元素、属性和文本。 - XMLElement:表示 XML 文档中的元素,包括其标签名、属性和子元素。 - XMLAttribute:表示 XML 元素的属性。 - XMLText:表示 XML 元素中的文本。 3. 解析流程 tinyxml2 的解析流程是基于事件的,它将 XML 文档分为不同的事件,如元素开始、元素结束、文本等。解析器从头到尾扫描 XML 文档,当它遇到一个事件时,它会调用相应的回调函数。 以下是 tinyxml2 的解析流程: - 创建一个 XMLDocument 对象。 - 调用 XMLDocument::LoadFile() 或 XMLDocument::Parse() 方法,将 XML 文档加载到内存中。 - 解析器开始扫描 XML 文档,并调用相应的回调函数处理每个事件。 - 解析器将事件转换为节点,将节点添加到 XMLDocument 对象中。 - 解析器返回 XMLDocument 对象,表示解析完成。 4. 修改流程 tinyxml2 允许修改 XML 文档,包括添加、删除、修改元素和属性等。以下是 tinyxml2 的修改流程: - 创建一个 XMLDocument 对象。 - 调用 XMLDocument::LoadFile() 或 XMLDocument::Parse() 方法,将 XML 文档加载到内存中。 - 使用 tinyxml2 的 API 修改 XMLDocument 对象。 - 调用 XMLDocument::SaveFile() 方法,将修改后的 XML 文档保存到磁盘。 总之,tinyxml2 是一个非常简单、易于使用的 XML 解析库,它可以轻松处理 XML 文档的解析和修改。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值