Boost库实现XML文件 不用指定路径遍历所有内容
XML:可扩展标记语言,互联网数据传输的重要工具,用来标记数据、定义数据类型。
Boost库中的property_tree是一个保存了多个属性值的树形结构,用类似路径的的方式访问任意节点,且每个节点可以用迭代器的的方式遍历子节点。
目的:现在想做一个工具,在不知道XML根节点的情况下遍历出所有的内容,再进行对某个节点的内容更新并写入。
自己以前对XML只是了解,简单的读取数据进行处理。在书上和网上找的相关例子均是已经知道根节点在遍历获取其内容的,自己参考了《Boost程序库完全开发指南》和网上的博客实现了自己想要的功能,特此记录。
Boost库:包含头文件:
#include "boost/property_tree/ptree.hpp"
#include "boost/property_tree/xml_parser.hpp"
using namespace boost::property_tree;
因为要重新生成XML文件,所以读取到的每个节点都需要保存信息,定义结构如下:
//保存节点信息
struct XmlNode_s
{
std::string strNodeName; //当前节点名字
std::unordered_map<std::string, std::string> xmlattr; //当前节点的属性
list<std::string> listSubName; //子节点名字
std::string strParentName; //父节点 记录了从根节点到上一个节点的名字,用"."隔开
std::string strValue; //当前节点的值
};
参见Boost的property_tree库的核心类basic_ptree摘要,value_type是一个std::pair,节点的数据结构,first是节点的属性,second是节点自身。获取属性值是需要在属性名字前加上:"<xmlattr>."
递归解析XML,在读XML文件时,传入的ptree对象直接传给解析函数
list<XmlNode_s> listNode;
ptree pxml;
std::string strFile = "./1.xml";
//xml格式化
read_xml(strFile, pxml, boost::property_tree::xml_parser::trim_whitespace);
XMLParse(pxml, listNode);
//boost::property_tree::xml_parser::trim_whitespace
//读的时候传入这个参数可以忽略空格换行符等, 在生成XML的时候可以生成标准格式的XML文件
//递归解析xml文件
void XMLParse(const ptree &pxml, std::list<XmlNode_s> &listNode, std::string strNode = "")
{
for (const auto &node : pxml)
{
std::string NodeKey = node.first.data(); //属性或者子节点
if (NodeKey == "<xmlattr>") //判断是否是属性
{
continue; //属性已经在父节点中做了保存
}
XmlNode_s xmlnode;
xmlnode.strNodeName = std::string(node.first.data()); //节点名
xmlnode.strValue = node.second.data(); //值
xmlnode.strParentName = strNode; //父节点是外面传入进来的
//获取该节点的属性
for (const auto &attr : node.second)
{
std::string strAttr = attr.first.data();
if (std::string(strAttr) == "<xmlattr>") //是属性
{
//获取属性的值
for (auto &att : attr.second)
{
cout << att.first.data() << endl; //属性名字
cout << att.second.data() << endl; //属性值
xmlnode.xmlattr[att.first.data()] = att.second.data();
}
}
else //节点自身
{
xmlnode.listSubName.emplace_back(strAttr); //当前节点的子节点名字
}
}
listNode.emplace_back(xmlnode); //保存当前节点
if (!NodeKey.empty()) //当前节点
{
xmlnode.xmlattr.clear();
//获取子节点信息 递归
std::string strParentName;
if (!xmlnode.strParentName.empty())
{
strParentName += xmlnode.strParentName + "." + xmlnode.strNodeName;
}
else
{
strParentName = xmlnode.strNodeName;
}
//生成父节点 写入XML的时候需要知道父节点
XMLParse(node.second, listNode, strParentName); //递归遍历
}
}
}
生成XML文件
//设置节点内容
void MakeXmlNode(ptree &pt, XmlNode_s &node)
{
pt.put_value(node.strValue); //添加值
for (auto &itA : node.xmlattr)
{
std::string strA = "<xmlattr>.";
pt.add(strA + itA.first, itA.second); //设置属性
}
}
//添加节点
void AddXmlNode(ptree &pt, XmlNode_s &node, std::string &strPreNode)
{
auto itPos = strPreNode.find("."); //如果没有"."表示根节点
if (itPos == std::string::npos)
{
ptree tmp;
MakeXmlNode(tmp, node);
if (pt.size() == 0) //节点第一次出现,直接添加
{
pt.add_child(node.strNodeName, tmp);
}
else
{//有多个相同节点,取最后一个,否则数据将只会放在第一个 因为遍历的时候是先把当前节点遍历完了再回来遍历的。类似于二叉树的先序遍历
pt.back().second.add_child(node.strNodeName, tmp); //取最后一个节点
}
}
else
{
std::string strSub = strPreNode.substr(itPos + 1);
auto &ptTmp = pt.back().second; //要引用,否则写入的信息将不会在父节点上
AddXmlNode(ptTmp, node, strSub); //递归写入
}
}
//生成XML
void WriteXML(std::string &strFile, std::list<XmlNode_s> &listNode)
{
ptree ptRoot; //根节点
for (auto it = listNode.begin(); it != listNode.end(); ++it)
{
AddXmlNode(ptRoot, *it, it->strParentName); //添加节点
}
auto settings = boost::property_tree::xml_writer_make_settings<std::string>('\t', 1);
write_xml(strFile, ptRoot, std::locale(), settings);
}
XML文件内容:
<?xml version="1.0" encoding="utf-8"?>
<Document xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Events>
<Event Name="SalesWareHouseOut" MainAction="WareHouseOut">
<DataField>
<Data Code="81302280400502614672" CorpOrderID="20190527102500" Actor="1111" ActDate="2019-05-27 10:26:54" WrongCode="False" ToCorpID=""/>
<Data Code="81302280400502623396" CorpOrderID="20190527102500" Actor="1111" ActDate="2019-05-27 10:26:54" WrongCode="False" ToCorpID=""/>
<Data Code="81302280400502630200" CorpOrderID="20190527102500" Actor="1111" ActDate="2019-05-27 10:26:54" WrongCode="False" ToCorpID=""/>
<Data Code="81302280400502643774" CorpOrderID="20190527102500" Actor="1111" ActDate="2019-05-27 10:26:54" WrongCode="False" ToCorpID=""/>
<Data Code="81302280400502663146" CorpOrderID="20190527102500" Actor="1111" ActDate="2019-05-27 10:26:54" WrongCode="False" ToCorpID=""/>
<Data Code="81302280400499127973" CorpOrderID="20190527102500" Actor="1111" ActDate="2019-05-27 10:26:54" WrongCode="False" ToCorpID=""/>
</DataField>
</Event>
</Events>
<Events>
<Event Name="SalesWareHouseOut" MainAction="WareHouseOut">
<DataField>
<Data Code="81302280400502614672" CorpOrderID="20190527102500" Actor="1111" ActDate="2019-05-27 10:26:54" WrongCode="False" ToCorpID=""/>
<Data Code="81302280400502623396" CorpOrderID="20190527102500" Actor="1111" ActDate="2019-05-27 10:26:54" WrongCode="False" ToCorpID=""/>
<Data Code="81302280400502630200" CorpOrderID="20190527102500" Actor="1111" ActDate="2019-05-27 10:26:54" WrongCode="False" ToCorpID=""/>
<Data Code="81302280400502643774" CorpOrderID="20190527102500" Actor="1111" ActDate="2019-05-27 10:26:54" WrongCode="False" ToCorpID=""/>
<Data Code="81302280400502663146" CorpOrderID="20190527102500" Actor="1111" ActDate="2019-05-27 10:26:54" WrongCode="False" ToCorpID=""/>
<Data Code="81302280400499127973" CorpOrderID="20190527102500" Actor="1111" ActDate="2019-05-27 10:26:54" WrongCode="False" ToCorpID=""/>
</DataField>
</Event>
</Events>
</Document>