C++ XML文件读写及简单封装

背景

        在我们平时的开发中通常会将一些用户信息或是一些配置信息写入到文件中保存起来,但是使用最简单的 .txt 文件文件进行存储显得不够高级。准确的说,打开任意程序安装目录,虽然能看到文件文件的存在,但基本都是 ReadMe.txt,基本看不到有把这类信息保存在文本文件中的情况。
        进而我们寻求一种更加常见的信息存储方式,使用 xml 文件。至于 xml 文件的格式和基本知识,如果有不熟悉的同学请
-------> 戳这

1. xml 文件结构简介

        xml 文件的格式一般是树形结构,如下图:
在这里插入图片描述
        如上图,绿色部分是 xml 的文件头信息,这里的问号是一种特殊格式,属于正常的 xml 文件内容。
        绿色这一样的内容可以这样解释:这个标签的标签名为 xml ,它的版本 version 为 1.0,文件编码 encoding 为 UTF-8。在这里,xml 标识标签值,而有等号连接的是属性,等号左侧为属性名,右侧为属性值。如这个标签有两个属性,它们的属性名分别是 version 和 encoding,属性值分别为 1.0 和 UTF-8。那么下面的标签也是这样解释。
        标签语言有起始标签和结束标签。即<></>。在起始标签和结束标签之间的内容属于本标签的内容。若起始标签和结束标签之间还有别的起始标签和结束标签,那么里面的标签就是当前标签的子级,而当前标签就是内部标签的父级,这样标签之间就有了父子级关联关系,就如同数据结构中的树一般。
        如上图的 excel 标签,他的起始标签和结束标签之间还有两个 table 标签。而 table 标签的起始标签和结束标签之间又有三个 colum 标签。也就是说,excel 标签有两个 table 标签作为子级标签,而每个 table 标签又拥有 colum 标签作为它的子级标签。
        就这样,我们可以根据父子级关系找到任意标签所在位置,亦可以根据位置查找到指定的标签。
        值得一提的是,一个 xml 文件中除去头文件标签后还会剩下一个一个标签作为所有后续加入的标签的祖先节点。如上图中的 excel 标签。xml 文件的读写操作也是建立在这一特性之上的。

2. xml 文件读写

2.1 前期准备

编程环境:

Visual Studio 2015 ;	Windows 10 ;

第三方库源文件下载:

https://sourceforge.net/projects/tinyxml/

在这里插入图片描述
        如上图,解压后我们只需要这几个文件就可以了,把它们加入到我们项目中。


        如果您知道如何将这些文件添加至项目中,可以跳过下面的内容,直接看 2.2。


        新建 vs 控制台应用程序,并将文件拷贝至 stdafx.h 文件所在目录下(这里是防止后面引入后编译过程中引发的 是否忘记添加 #include “stdafx.h”? 的编译错误。对新手并不友好,干脆先简单一点,能用再说。)。如下图所示:
在这里插入图片描述
        然后将这些文件添加到 vs 项目当中,如下图所示:
在这里插入图片描述
        如果不会创建文件夹的话,可以右击你的项目名称,点击添加,选择新建筛选器即可。
在这里插入图片描述
        将文件加入到项目中也是一样的。
在这里插入图片描述
        这些做完后我们需要打开所有 .cpp 文件,并在顶部加上一行预编译头,保证能够编译通过。

#include "stdafx.h"

        如下图所示:
在这里插入图片描述

2.2 验证导入效果

        在 main 函数中加入以下测试代码,编译通过即可。

#include "stdafx.h"
#include <iostream>
#include <string>
#include "tinystr.h"
#include "tinyxml.h"

const std::string strXmlFile = "这里写一个xml文件所在位置,是绝对路径,不要直接照抄哦。";

int main()
{
	TiXmlDocument *pDocument = new TiXmlDocument();
	if (NULL == pDocument)
	{
		std::cout << "NULL == pDocument" << std::endl;
		return -1;
	}
	
	if (!pDocument ->LoadFile(strXmlFile .c_str(), TIXML_ENCODING_UNKNOWN))
	{
		std::cout << "无法加载xml文件!" << std::endl;
		return -1;
	}

	TiXmlElement* pRootElement = pDocument->RootElement();
	if (NULL == pRootElement)
	{
		std::cout << "NULL == pRootElement" << std::endl;
		return -1;
	}
	
	system("pause");
	
    return 0;
}

        如果没有 xml 文件的话就先用我的吧,新建一个文件文件,更改后缀为 .xml 即可。然后用记事本打开,写入以下内容并保存。

<?xml version="1.0" encoding="UTF-8" ?>
<excel version="1.0" author="huangzhihui">
    <table id="1">
        <colum id="1.1" name="Mike1" width="1" height="1" />
        <colum id="1.2" name="John1" width="2" height="2" />
        <colum id="1.3" name="Lucy1" width="3" height="3" />
    </table>
    <table id="2">
        <colum id="2.1" name="Mike1" width="1" height="1" />
        <colum id="2.2" name="John1" width="2" height="2" />
        <colum id="2.3" name="Lucy1" width="3" height="3" />
    </table>
</excel>
2.3 读取 xml 文件内容
/**
*	读取 xml 文件
*	@param [in]  const std::string &strXmlFilePath		  // xml 文件所在路径
*/
void ReadXmlDocument(const std::string &strXmlFilePath)
{
	if (strXmlFilePath.empty())
	{
		std::cout << "strXmlFilePath is empty!" << std::endl;
		return;
	}

	// 1. 创建 xml 文档对象
	TiXmlDocument *pDocument = new TiXmlDocument();
	if (NULL == pDocument)
	{
		std::cout << "NULL == pDocument" << std::endl;
		return ;
	}

	// 2. 加载 xml 文件
	if (!pDocument->LoadFile(strXmlFilePath.c_str(), TIXML_ENCODING_UNKNOWN))
	{
		std::cout << "无法加载 xml 文件!" << std::endl;
		return ;
	}

	// 3. 打印文件内容
	pDocument->Print();
}
2.4 向 xml 文件中写入内容
/**
*	写入 xml 文件
*	@param [in]  strXmlFilePath		xml 文件所在路径
*/
void WriteXmlDocument(const std::string &strXmlFilePath)
{
	if (strXmlFilePath.empty())
	{
		return;
	}

	// 1. 创建XML文档指针
	TiXmlDocument *pDocument = new TiXmlDocument(strXmlFilePath.c_str());
	if (NULL == pDocument)
	{
		return;
	}

	// 2. 创建声明标签
	TiXmlDeclaration *pDeclaration = new TiXmlDeclaration("1.0", "UTF-8", "");
	if (NULL == pDeclaration)
	{
		return;
	}
	pDocument->LinkEndChild(pDeclaration);

	// 3. 创建祖先标签
	TiXmlElement *pRoot = new TiXmlElement("111");	// 这里可以指定标签名,将其作为参数传入即可
	if (NULL == pRoot)
	{
		return;
	}

	// 4. 为标签设置属性	参数:属性名,属性值
	pRoot->SetAttribute("name", "111");

	// 5. 关联XML文档,成为 XML 文档的根节点
	pDocument->LinkEndChild(pRoot);

	// 6. 为祖先标签添加子级标签
	TiXmlElement *pChildFir = new TiXmlElement("222");
	pChildFir->SetAttribute("name", "222");
	pChildFir->SetAttribute("age", "18");

	pRoot->LinkEndChild(pChildFir);	// 关联成为先祖标签的子级标签

	// 7. 保存文件
	pDocument->SaveFile(strXmlFilePath.c_str());
}

写入效果如下,看看是否符合你的预期呢:
在这里插入图片描述

3. 简单封装

        根据标签的特征,抽取出一个基类,用于表示一个标签对象。

3.1 xml 标签节点基类 IXmlNode.h
//
// 文件名称:IXmlNode.h
// 功能描述:xml节点基类
// 创建标识:huang_3366 2021/11/27 21:27:54
//

#include "stdafx.h"
#include <string>
#include <vector>
#include "..\inc\tinystr.h"
#include "..\inc\tinyxml.h"

typedef std::vector<TiXmlAttribute *>	TiXmlAttributes;	// 属性集合

class IXmlNode
{
public:
	/**
	*	获取当前原始数据节点
	*	@return TiXmlElement *
	*/
	virtual TiXmlElement *GetNode() = 0;

	/**
	*	获取节点名称
	*	@return std::string
	*/
	virtual std::string GetNodeName() = 0;

	/**
	*	获取节点属性集合
	*	@return TiXmlAttributes *
	*/
	virtual TiXmlAttributes *GetNodeAttributes() = 0;
	
	/**
	*	获取子级数量
	*	@return int 
	*/
	virtual int GetChildCount() = 0;

	/**
	*	获取属性数量
	*	@return int
	*/
	virtual int GetAttributesCount() = 0;

	/**
	*	在指定位置添加子级节点
	*	@param [in]  TiXmlElement *pInsertNode		// 要插入的节点
	*	@param [in]  int nIndex						// 插入位置索引
	*	@return bool
	*/
	virtual bool InsertChildAt(IXmlNode *pInsertNode, int nIndex) = 0;

	/**
	*	删除指定位置的子级节点
	*	@param [in]  int nIndex						// 删除位置索引
	*	@return bool
	*/
	virtual bool DeleteChildAt(int nIndex) = 0;

	/**
	*	删除所有子集节点
	*	@return bool
	*/
	virtual bool RemoveAllChilds() = 0;

	/**
	*	修改节点名称
	*	@param [in]  const std::string &strNodeName		// 要修改的节点新名称
	*/
	virtual void SetNodeName(const std::string &strNodeName) = 0;

	/**
	*	获取指定属性名的属性值
	*	@param [in]  const std::string &strAttriName	// 属性名
	*	@return std::string								// 属性值
	*/
	virtual std::string GetSpecifiedAttribute(const std::string &strAttriName) = 0;

	/**
	*	设置指定属性名的属性值
	*	@param [in]  const std::string &strAttriName	// 属性名
	*	@param [in]  const std::string &strAttriValue	// 属性值
	*	@return bool
	*/
	virtual bool SetSpecifiedAttribute(const std::string &strAttriName, const std::string &strAttriValue) = 0;

	/**
	*	获取指定位置的子级
	*	@param [in] const int &nIndex		// 位置索引
	*	@return IXmlNode *
	*/
	virtual IXmlNode *GetChildAt(const int &nIndex) = 0;

	/**
	*	根据节点名称获取节点
	*	@param [in]  strNodeName	
	*	@return IXmlNode *
	*/
	virtual IXmlNode *GetChildByName(const std::string &strNodeName) = 0;

	/**
	*	获取父级节点
	*	@return IXmlNode *
	*/
	virtual IXmlNode *GetParent() = 0;

	/**
	*	设置父级节点
	*	@param [in] IXmlNode *pParent	
	*	@return
	*/
	virtual bool SetParent(IXmlNode *pParent) = 0;

protected:

	/**
	*	初始化节点,递归调用
	*	@param [in]  TiXmlElement *pInitNode
	*	@return bool  操作是否成功
	*/
	virtual bool InitXmlNodes(TiXmlElement *pInitNode, IXmlNode *pParent) = 0;
};

typedef std::vector<IXmlNode *>		XmlNodeElements;	// 子级节点集合
3.2 xml 标签节点实现 CXmlNode.h
//
// 文件名称:CXmlNode.h
// 功能描述:xml节点实现类
// 创建标识:huang_3366 2021/11/28 21:55:59
//
#pragma once

#include "IXmlNode.h"

class CXmlNode : public IXmlNode
{
public:
	///
	//			   标准构造析构函数				 //
	///
	
	CXmlNode(TiXmlElement *pInitNode, IXmlNode *pParent);
	~CXmlNode();

public:
	///
	//			   继承 IXmlNode 接口				 //
	///

	virtual TiXmlElement *GetNode() override;

	/**
	*	获取节点名称
	*	@return std::string
	*/
	virtual std::string GetNodeName() override;

	/**
	*	获取节点属性集合
	*	@return TiXmlAttributes *
	*/
	virtual TiXmlAttributes *GetNodeAttributes() override;

	/**
	*	获取子级数量
	*	@return int
	*/
	virtual int GetChildCount() override;

	/**
	*	获取属性数量
	*	@return int
	*/
	virtual int GetAttributesCount() override;

	/**
	*	在指定位置添加子级节点
	*	@param [in]  TiXmlElement *pInsertNode		// 要插入的节点
	*	@param [in]  int nIndex						// 插入位置索引
	*	@return bool
	*/
	virtual bool InsertChildAt(IXmlNode *pInsertNode, int nIndex) override;

	/**
	*	删除指定位置的子级节点
	*	@param [in]  int nIndex						// 删除位置索引
	*	@return bool
	*/
	virtual bool DeleteChildAt(int nIndex) override;

	/**
	*	删除所有子集节点
	*	@return bool
	*/
	virtual bool RemoveAllChilds() override;

	/**
	*	修改节点名称
	*	@param [in]  const std::string &strNodeName		// 要修改的节点新名称
	*/
	virtual void SetNodeName(const std::string &strNodeName) override;

	/**
	*	获取指定属性名的属性值
	*	@param [in]  const std::string &strAttriName	// 属性名
	*	@return std::string								// 属性值
	*/
	virtual std::string GetSpecifiedAttribute(const std::string &strAttriName) override;

	/**
	*	设置指定属性名的属性值
	*	@param [in]  const std::string &strAttriName	// 属性名
	*	@param [in]  const std::string &strAttriValue	// 属性值
	*	@return bool
	*/
	virtual bool SetSpecifiedAttribute(const std::string &strAttriName, const std::string &strAttriValue) override;

	/**
	*	获取指定位置的子级
	*	@param [in] const int &nIndex		// 位置索引
	*	@return TiXmlElement *
	*/
	virtual IXmlNode *GetChildAt(const int &nIndex) override;

	/**
	*	根据节点名称获取节点
	*	@param [in]  strNodeName
	*	@return TiXmlElement *
	*/
	virtual IXmlNode *GetChildByName(const std::string &strNodeName) override;

	/**
	*	获取父级节点
	*	@return TiXmlElement *
	*/
	virtual IXmlNode *GetParent() override;

	/**
	*	设置父级节点
	*	@param [in] TiXmlElement *pParent
	*	@return
	*/
	virtual bool SetParent(IXmlNode *pParent) override;

public:
	///
	//			    本类自定义接口				     //
	///

	/**
	*	获取子级节点集合
	*	@return TiXmlElements *
	*/
	virtual XmlNodeElements *GetChildNodes();

	/**
	*	添加或替换子级节点集合
	*	@param [in]  TiXmlElements *pNewChilds		// 要添加或替换的子级节点集合
	*	@return bool
	*/
	virtual bool AddOrReplaceChilds(const XmlNodeElements &NewChilds);

protected:

	/**
	*	初始化节点
	*	@param [in]  TiXmlElement *pInitNode
	*	@return bool  操作是否成功
	*/
	virtual bool InitXmlNodes(TiXmlElement *pInitNode, IXmlNode *pParent) override;

private:
	TiXmlElement		*_pNode				= NULL;		// 当前节点
	IXmlNode			*_pParent			= NULL;		// 父节点
	XmlNodeElements		_ChildNodes	;					// 子级节点集合
	TiXmlAttributes		_Attributes	;					// 节点属性集合
};

3.3 xml 标签节点实现 CXmlNode.cpp
#include "stdafx.h"
#include "CXmlNode.h"

///
//			   标准构造析构函数				 //
///

CXmlNode::CXmlNode(TiXmlElement *pInitNode, IXmlNode *pParent)
{
	InitXmlNodes(pInitNode, pParent);
}

CXmlNode::~CXmlNode()
{
	if (_pNode != NULL)
	{
		_pNode = NULL;
	}

	if (_pParent != NULL)
	{
		_pParent = NULL;
	}

	if (!_ChildNodes.empty())
	{
		RemoveAllChilds();
	}

	if (!_Attributes.empty())
	{
		_Attributes.clear();
	}
}

///
//			   继承 IXmlNode 接口				 //
///

std::string CXmlNode::GetNodeName()
{
	return std::string(_pNode->Value());
}

TiXmlAttributes *CXmlNode::GetNodeAttributes() 
{
	return &_Attributes;
}

XmlNodeElements *CXmlNode::GetChildNodes()
{
	return &_ChildNodes;
}

int CXmlNode::GetChildCount() 
{
	return _ChildNodes.size();
}

int CXmlNode::GetAttributesCount() 
{
	return _ChildNodes.size();
}

bool CXmlNode::InsertChildAt(IXmlNode *pInsertNode, int nIndex)
{
	if (pInsertNode == NULL || nIndex < 0 || nIndex >= GetChildCount())
	{
		return false;
	}
	
	_ChildNodes.insert(_ChildNodes.begin() + nIndex, pInsertNode);

	return true;
}

bool CXmlNode::AddOrReplaceChilds(const XmlNodeElements &NewChilds)
{
	if (NewChilds.empty())
	{
		return false;
	}

	RemoveAllChilds();

	_ChildNodes = NewChilds;

	return true;
}

bool CXmlNode::DeleteChildAt(int nIndex) 
{
	if (_ChildNodes.empty() || _ChildNodes.size() <= nIndex || nIndex < 0)
	{
		return false;
	}

	IXmlNode *pDeleteNode = _ChildNodes[nIndex];
	_ChildNodes.erase(_ChildNodes.begin() + nIndex);
	if (pDeleteNode != NULL)
	{
		delete pDeleteNode;
	}

	return true;
}

bool CXmlNode::RemoveAllChilds() 
{
	if (!_ChildNodes.empty())
	{
		for (int i = 0; i < _ChildNodes.size(); ++i)
		{
			IXmlNode *pTemp = _ChildNodes[i];
			if (pTemp != NULL)
			{
				//pTemp->RemoveAllChilds();
				delete pTemp;
			}
		}

		_ChildNodes.clear();
	}

	return true;
}

void CXmlNode::SetNodeName(const std::string &strNodeName) 
{
	_pNode->SetValue(strNodeName.c_str());
}

std::string CXmlNode::GetSpecifiedAttribute(const std::string &strAttriName) 
{
	if (_Attributes.empty())
	{
		return "Not Exist.";
	}

	for (int i = 0; i < _Attributes.size(); ++i)
	{
		if (_Attributes[i] == NULL)
		{
			continue;
		}

		if (_Attributes[i]->Name() == strAttriName)
		{
			return _Attributes[i]->Value();
		}
	}

	return "Not Exist.";
}

bool CXmlNode::SetSpecifiedAttribute(const std::string &strAttriName, const std::string &strAttriValue) 
{
	if (_Attributes.empty())
	{
		return false;
	}

	for (int i = 0; i < _Attributes.size(); ++i)
	{
		if (_Attributes[i] == NULL)
		{
			continue;
		}

		if (_Attributes[i]->Name() == strAttriName)
		{
			_Attributes[i]->SetValue(strAttriValue.c_str());

			return true;
		}
	}

	return false;
}

IXmlNode *CXmlNode::GetChildAt(const int &nIndex)
{
	if (_ChildNodes.empty() || _ChildNodes.size() <= nIndex || nIndex < 0)
	{
		return NULL;
	}

	return _ChildNodes[nIndex];
}

IXmlNode *CXmlNode::GetChildByName(const std::string &strNodeName)
{
	if (_ChildNodes.empty() || strNodeName.empty())
	{
		return NULL;
	}

	for (int i = 0; i < _ChildNodes.size(); ++i)
	{
		if (_ChildNodes[i] == NULL)
		{
			continue;
		}

		if (_ChildNodes[i]->GetNodeName() == strNodeName)
		{
			return _ChildNodes[i];
		}
	}

	return NULL;
}

IXmlNode *CXmlNode::GetParent()
{
	return _pParent;
}

bool CXmlNode::SetParent(IXmlNode *pParent)
{
	_pParent = pParent;

	return true;
}

bool CXmlNode::InitXmlNodes(TiXmlElement *pInitNode, IXmlNode *pParent)
{
	this->_pNode = pInitNode;
	this->_pParent = pParent;

	if (_pNode == NULL)
	{
		return false;
	}

	// 1. 遍历当前节点的全部属性
	TiXmlAttribute *pAttr = _pNode->FirstAttribute();//第一个属性
	while (NULL != pAttr) //输出所有属性
	{
		_Attributes.push_back(pAttr);
		pAttr = pAttr->Next();
	}

	// 2. 递归遍历子级节点
	for (TiXmlElement *StuElement = _pNode->FirstChildElement();//第一个子元素
		StuElement != NULL;
		StuElement = StuElement->NextSiblingElement())//下一个兄弟元素
	{
		_ChildNodes.push_back(new CXmlNode(StuElement, this));
	}

	return true;
}

TiXmlElement *CXmlNode::GetNode()
{
	return _pNode;
}

3.4 封装验证
int Test()
{
	TiXmlDocument *_pDocument = new TiXmlDocument();
	if (!_pDocument->LoadFile("..\\配置文件\\a.xml", TIXML_ENCODING_UNKNOWN))
	{
		std::cout << "无法加载xml文件!" << std::endl;
		return -1;
	}

	TiXmlElement* pRootElement = _pDocument->RootElement();		//根节点
	if (pRootElement == NULL)
	{
		std::cout << "Can not get the root node of xml document." << std::endl;
		return -1;
	}

	IXmlNode *_pRootNode = new CXmlNode(pRootElement, NULL);
	if (NULL == _pRootNode)
	{
		return -1;
	}

	int nChildCount = _pRootNode->GetChildCount();
	if (nChildCount != 1)
	{
		IXmlNode *pChildFir = _pRootNode->GetChildAt(0);
		if (pChildFir == NULL)
		{
			std::cout << "Error" << std::endl;
			return -1;
		}

		CXmlNode *pChildFirEx = dynamic_cast<CXmlNode *>(pChildFir);
		std::cout << pChildFir->GetSpecifiedAttribute("id") << std::endl;
	}
}

4. 整套操作流程封装

        第3节只是将 xml 标签进行了封装,对于标签操作只需要使用我们自定义的对象即可,接口的多少及扩展都看自己喜好而定。然而我们还是要实现打开文件,获取根节点,并将根节点传入自定义对象中,也就是说我还想偷懒,干脆一步到位好了。那么接下来就是对文件和节点类进行封装。

4.1 IFileDocument.h 文档操作基类
//
// 文件名称:IFileDocument.h
// 功能描述:文件文档基类
// 创建标识:huang_3366 2021/12/01 22:47:04
//
#pragma once

#include <string>

class IFileDocument
{
public:
	/**
	*	纯虚函数,读取文档
	*/
	virtual void ReadDocument() = 0;

	/**
	*	纯虚函数,向指定文档中写入
	*	@param [in]  const std::string &strDestFile	指定文件的文件名
	*/
	virtual void WriteDocument(const std::string &strDestFile) = 0;
};
4.2 CXmlDocument.h xml文档操作实现类
//
// 文件名称:CXmlDocument.h
// 功能描述:xml 文件实现类
// 创建标识:huang_3366 2021/12/01 22:51:31
//
#pragma once

#include "IFileDocument.h"
#include "CXmlNode.h"

class CXmlDocument : public IFileDocument
{
public:
	CXmlDocument(const std::string &strXmlFile);
	~CXmlDocument();

public:
	/**
	*	纯虚函数,读取文档
	*/
	virtual void ReadDocument() override;

	/**
	*	纯虚函数,向指定文档中写入
	*	@param [in]  const std::string &strDestFile	指定文件的文件名
	*/
	virtual void WriteDocument(const std::string &strDestFile) override;

public:
	/**
	*	获取xml文件的根节点
	*	@return IXmlNode *
	*/
	IXmlNode *GetRootNode();

private:
	/**	
	*	拷贝节点属性
	*	@param [in]  pRootSrc	
	*	@param [in]  pRootDest	
	*/
	void CopyNodeAttributes(TiXmlElement *pRootSrc, TiXmlElement *pRootDest);

	/**
	*	拷贝节点
	*	@param [in]  pRootSrc	
	*	@param [in]  pRootDest	
	*/
	void CopyNodeChilds(IXmlNode *pRootSrc, TiXmlElement *pRootDest);

private:
	std::string		_strXmlFileName;		// xml 文件名
	IXmlNode		*_pRootNode;			// xml 根节点
	TiXmlDocument	*_pDocument;			// xml 文档对象
};
4.3 CXmlDocument.cpp
#include "stdafx.h"
#include "CXmlDocument.h"

CXmlDocument::CXmlDocument(const std::string &strXmlFile)
{
	_strXmlFileName = strXmlFile;
	_pRootNode = NULL;
	_pDocument = NULL;
}

CXmlDocument::~CXmlDocument()
{
	if (_pRootNode != NULL)
	{
		//_pRootNode->RemoveAllChilds();
		delete _pRootNode;
	}
	
	if (_pDocument != NULL)
	{
		delete _pDocument;
	}
}

void CXmlDocument::ReadDocument()
{
	_pDocument = new TiXmlDocument();
	if (!_pDocument->LoadFile(_strXmlFileName.c_str(), TIXML_ENCODING_UNKNOWN))
	{
		std::cout << "无法加载xml文件!" << std::endl;
		return;
	}

	TiXmlElement* pRootElement = _pDocument->RootElement();		//根节点
	if (pRootElement == NULL)
	{
		std::cout << "Can not get the root node of xml document." << std::endl;
		return;
	}

	_pRootNode = new CXmlNode(pRootElement, NULL);
}

void CXmlDocument::WriteDocument(const std::string & strDestFile)
{
	if (strDestFile.empty())
	{
		return;
	}

	// 1. 创建XML文档指针
	TiXmlDocument *pDocument = new TiXmlDocument(strDestFile.c_str());
	if (NULL == pDocument)
	{
		return;
	}

	// 2. 声明XML
	TiXmlDeclaration *pDeclaration = new TiXmlDeclaration("1.0", "UTF-8", "");
	if (NULL == pDeclaration)
	{
		return;
	}
	pDocument->LinkEndChild(pDeclaration);

	// 3. 获取源数据根节点
	TiXmlElement *pRootSrc = GetRootNode()->GetNode();
	if (pRootSrc == NULL)
	{
		return;
	}

	// 4. 创建写入数据根节点
	TiXmlElement *pRoot = new TiXmlElement(pRootSrc->Value());
	if (NULL == pRoot)
	{
		return;
	}
	// 6. 关联XML文档,成为 XML 文档的根节点
	pDocument->LinkEndChild(pRoot);

	// 7. 拷贝节点
	CopyNodeChilds(GetRootNode(), pRoot);

	// 8. 保存文件
	pDocument->SaveFile(strDestFile.c_str());
}

IXmlNode * CXmlDocument::GetRootNode()
{
	if (_pRootNode == NULL)
	{
		ReadDocument();
	}

	return _pRootNode;
}

void CXmlDocument::CopyNodeAttributes(TiXmlElement *pRootSrc, TiXmlElement *pRootDest)
{
	if (pRootSrc == NULL || pRootDest == NULL)
	{
		return;
	}

	// 遍历当前节点的全部属性
	TiXmlAttribute *pAttr = pRootSrc->FirstAttribute();//第一个属性
	while (NULL != pAttr) //输出所有属性
	{
		pRootDest->SetAttribute(pAttr->Name(), pAttr->Value());

		pAttr = pAttr->Next();
	}
}

void CXmlDocument::CopyNodeChilds(IXmlNode *pRootSrc, TiXmlElement *pRootDest)
{
	if (pRootSrc == NULL || pRootDest == NULL)
	{
		return;
	}

	CXmlNode *pRootSrcEx = dynamic_cast<CXmlNode *>(pRootSrc);
	if (pRootSrcEx == NULL)
	{
		return;
	}

	CopyNodeAttributes(pRootSrcEx->GetNode(), pRootDest);

	XmlNodeElements *pChilds = pRootSrcEx->GetChildNodes();
	for (int i = 0; i < pChilds->size(); ++i)
	{
		TiXmlElement *pTempSrc = pChilds->at(i)->GetNode();
		if (pTempSrc == NULL)
		{
			continue;
		}

		TiXmlElement *pTempDest = new TiXmlElement(pTempSrc->Value());
		if (pTempDest == NULL)
		{
			continue;
		}

		// 1. 先拷贝子级属性
		CopyNodeAttributes(pTempSrc, pTempDest);

		// 2.递归拷贝子级
		CopyNodeChilds(pChilds->at(i), pTempDest);

		// 3.成为子级
		pRootDest->LinkEndChild(pTempDest);
	}
}

4.4 封装验证
int Test()
{
	CXmlDocument xmlDoc("..\\配置文件\\a.xml");
	IXmlNode *pRootNode = xmlDoc.GetRootNode();
	if (pRootNode == NULL)
	{
		std::cout << "Error" << std::endl;
		return -1;
	}

	std::string strNodeName = pRootNode->GetNodeName();

	IXmlNode *pChildEle = pRootNode->GetChildAt(0);
	if (pChildEle == NULL)
	{
		std::cout << "Error1" << std::endl;
		return -1;
	}

	xmlDoc.WriteDocument("..\\配置文件\\b.xml");
}
XML即可扩展标记语言(Extensible Markup Language)是一种用于存储和传输数据的通用标记语言。在使用XML进行读写操作时,可以通过封装和解析XML来实现。 首先,我们可以使用编程语言中的XML库来读取XML文件。这些库通常提供了一组函数或方法,用于加载XML文件并将其解析为可供我们使用的数据结构。这样,我们就可以通过访问和操作数据结构来获取XML文件中的信息。例如,我们可以使用库提供的函数来获取指定标签的值,或者遍历整个XML文档以获取所需的数据。 另外,我们也可以使用XML库来创建XML文件。通过使用库提供的函数或方法,我们可以定义XML文档的结构和内容,并将其保存为一个新的XML文件。我们可以按照XML的语法规则创建标签、属性和文本,并根据需要进行嵌套。这样,我们就可以按照自己的需求创建一个具有特定结构和内容的XML文件。 在封装XML的过程中,我们可以根据实际需求将读写操作封装为函数或方法。这样,我们可以将读取XML文件的过程封装为一个函数,以便在需要时调用,并返回所需的数据。同样地,我们也可以将创建XML文件的过程封装为一个函数,以便在需要时调用并生成所需的XML文件。 通过封装XML读写操作,我们可以提高代码的可重用性和可维护性。我们可以在多个项目中使用相同的封装函数,而不必重复编写相同的代码。同时,如果需要对读写操作进行优化或修改,也可以集中在封装函数中进行,而不必修改所有使用XML读写的代码。 总而言之,通过封装XML读写操作,我们可以方便地读取和创建XML文件,并能够更加高效地处理和管理XML数据。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值