就TinyXml使用答复一位网友
作者:朱金灿
来源:http://www.cnblogs.com/clever101
一位网友说看了我的TinyXml快速入门系列文章。在修改节点属性值函数ModifyNode_Attribute这个函数的用法参照我的例子运行了下,
<Connection ip="192.168.0.1" timeout="123.456000"/> 如果只有这一个节点的时候是修改正确的,但是如果再加上几个相同名字的节点呢?就像下面一样:
<Connection ip="192.168.0.2" timeout="123.456001"/>
<Connection ip="192.168.0.3" timeout="123.456002"/>
现在他想修改的是<Connection ip="192.168.0.3" timeout="123.456002"/> 这行为<Connection ip="192.168.0.4" timeout="123.456003"/>
请问该如何修改?
鉴于他所提问题有一定代表性,我就再写一篇以作答复。在《TinyXml快速入门(二)》中我提到了无论查询节点、删除节点、修改节点和增加节点,其实都离不开一个函数,就是根据节点名获取相关节点指针,其中一个关键的函数:
* \brief 通过根节点和节点名获取节点指针。
*
* \param pRootEle xml文件的根节点。
* \param strNodeName 要查询的节点名
* \param Node 需要查询的节点指针
* \return 是否找到。true为找到相应节点指针,false表示没有找到相应节点指针。
*/
bool GetNodePointerByName(TiXmlElement* pRootEle,const std::string &strNodeName,TiXmlElement* &Node)
{
// 假如等于根节点名,就退出
if (strNodeName==pRootEle->Value())
{
Node = pRootEle;
return true;
}
TiXmlElement* pEle = pRootEle;
for (pEle = pRootEle->FirstChildElement(); pEle; pEle = pEle->NextSiblingElement())
{
//递归处理子节点
if(GetNodePointerByName(pEle,strNodeName,Node))
return true;
}
return false;
}
具体到这位网友的问题,其实就是增加了一个判断条件,就是要求所求节点不但要求节点名为Connection,还有属性ip的值为192.168.0.3。那么我们可以对GetNodePointerByName函数稍作修改:
/*!
* \brief 通过根节点和节点名以及节点的一个属性值获取节点指针。
*
* \param pRootEle xml文件的根节点。
* \param strNodeName 要查询的节点名
* \param strNodeName 要查询的节点的一个属性值
* \param Node 需要查询的节点指针
* \return 是否找到。true为找到相应节点指针,false表示没有找到相应节点指针。
*/
bool GetNodePointerByName_Attribute(TiXmlElement* pRootEle,
const std::string &strNodeName,
const std::string &strAttributeValue,
TiXmlElement* &Node)
{
assert(NULL!=pRootEle);
// 假如等于根节点名,就退出
if (strNodeName==pRootEle->Value())
{
TiXmlAttribute* pAttr = NULL;
for (pAttr = pRootEle->FirstAttribute(); pAttr; pAttr = pAttr->Next())
{
std::string strAttValue = pAttr->Value();
if (strAttributeValue==strAttValue)
{
Node = pRootEle;
}
}
}
TiXmlElement* pEle = pRootEle;
for (pEle = pRootEle->FirstChildElement(); pEle; pEle = pEle->NextSiblingElement())
{
//递归处理子节点
if(GetNodePointerByName_Attribute(pEle,strNodeName,strAttributeValue,Node))
return true;
}
return false;
}
然后再增加一个修改属性的函数:
/*!
* \brief 修改指定节点的属性。
*
* \param XmlFile xml文件全路径。
* \param strNodeName 指定的节点名。
* \param strAttValue 指定的节点的其中一个属性值。
* \param AttMap 重新设定的属性值,这是一个map,前一个为属性名,后一个为属性值
* \return 是否成功。true为成功,false表示失败。
*/
bool ModifyNode_Attribute2(const std::string& XmlFile,const std::string& strNodeName,
const std::string& strAttValue,
const std::map<std::string,std::string> &AttMap)
{
typedef std::pair <std::string,std::string> String_Pair;
// 定义一个TiXmlDocument类指针
TiXmlDocument *pDoc = new TiXmlDocument();
if (NULL==pDoc)
{
return false;
}
pDoc->LoadFile(XmlFile);
TiXmlElement *pRootEle = pDoc->RootElement();
if (NULL==pRootEle)
{
return false;
}
TiXmlElement *pNode = NULL;
GetNodePointerByName_Attribute(pRootEle,strNodeName,strAttValue,pNode);
if (NULL!=pNode)
{
TiXmlAttribute* pAttr = NULL;
std::string strAttName = _T("");
std::string strAttValue = _T("");
for (pAttr = pNode->FirstAttribute(); pAttr; pAttr = pAttr->Next())
{
strAttName = pAttr->Name();
std::map<std::string,std::string>::const_iterator iter;
for (iter=AttMap.begin();iter!=AttMap.end();iter++)
{
if (strAttName==iter->first)
{
pAttr->SetValue(iter->second);
}
}
}
pDoc->SaveFile(XmlFile);
return true;
}
else
{
return false;
}
}
如果要将
<Connection ip="192.168.0.1" timeout="123.456000"/>
<Connection ip="192.168.0.2" timeout="123.456001"/>
<Connection ip="192.168.0.3" timeout="123.456002"/>
中的<Connection ip="192.168.0.3" timeout="123.456002"/> 这行修改为<Connection ip="192.168.0.4" timeout="123.456003"/>
那么测试代码就如下:
std::string strAttValue = _T("192.168.0.3");
typedef std::pair <std::string,std::string> String_Pair;
std::map<std::string,std::string> AttMap;
AttMap.insert(String_Pair(_T("ip"),_T("192.168.0.4")));
AttMap.insert(String_Pair(_T("timeout"),_T("123.456003")));
ModifyNode_Attribute2(XmlFile,strNodeName,strAttValue,AttMap);