使用 TinyXML 在 XML 文档中查找特定节点
Find a specific node in a XML document with TinyXML
我想用TinyXML解析xml文件中的一些数据。
这是我的文字.xml文件内容:
<?xml version="1.0" encoding="iso-8859-1"?>
<toto>
<tutu>
<tata>
<user name="toto" pass="13" indice="1"/>
<user name="tata" pass="142" indice="2"/>
<user name="titi" pass="azerty" indice="1"/>
</tata>
</tutu>
</toto>
我想访问第一个元素"用户"。执行此操作的方法如下:
TiXmlDocument doc("test.xml");
if (doc.LoadFile())
{
TiXmlNode *elem = doc.FirstChildElement()->FirstChildElement()->FirstChildElement()->FirstChildElement();
std::cout << elem->Value() << std::endl;
}
在输出中:用户。
但是代码非常丑陋且不是通用的。我尝试了下面的代码来模拟与上述代码相同的行为,但它不起作用并且发生了错误。
TiXmlElement *getElementByName(TiXmlDocument &doc, std::string const &elemt_value)
{
TiXmlElement *elem = doc.FirstChildElement(); //Tree root
while (elem)
{
if (!std::string(elem->Value()).compare(elemt_value))
return (elem);
elem = elem->NextSiblingElement();
}
return (NULL);
}
也许我错过了库中可以完成这项工作的特殊函数(getElementByName 函数)。我只想获取一个指向值是我正在寻找的元素的指针。有人可以帮助我吗?提前感谢您的帮助。
试试这个
TiXmlElement * getElementByName(TiXmlDocument & doc, std::string const & elemt_value) {
TiXmlElement * elem = doc.RootElement(); //Tree root
while (elem) {
if (!std::string(elem - > Value()).compare(elemt_value)) return (elem);
/*elem = elem->NextSiblingElement();*/
if (elem - > FirstChildElement()) {
elem = elem - > FirstChildElement();
} else if (elem - > NextSiblingElement()) {
elem = elem - > NextSiblingElement();
} else {
while (!elem - > Parent() - > NextSiblingElement()) {
if (elem - > Parent() - > ToElement() == doc.RootElement()) {
return NULL;
}
elem = elem - > Parent() - > NextSiblingElement();
}
}
}
return (NULL);
}
当我只是将其复制粘贴到我的代码中时,Adi 的答案不起作用,但是我修改了它,现在它对我来说效果很好。由于我做了很多更改,我认为我应该在这里发布我的最终代码。
void parseXML(tinyxml2::XMLDocument& xXmlDocument, std::string sSearchString, std::function<void(tinyxml2::XMLNode*)> fFoundSomeElement)
{
if ( xXmlDocument.ErrorID() != tinyxml2::XML_SUCCESS )
{
// XML file is not ok ... we throw some exception
throw DataReceiverException( "XML file parsing failed" );
} // if
//ispired by http://stackoverflow.com/questions/11921463/find-a-specific-node-in-a-xml-document-with-tinyxml
tinyxml2::XMLNode * xElem = xXmlDocument.FirstChild();
while(xElem)
{
if (xElem->Value() && !std::string(xElem->Value()).compare(sSearchString))
{
fFoundSomeElement(xElem);
}
/*
* We move through the XML tree following these rules (basically in-order tree walk):
*
* (1) if there is one or more child element(s) visit the first one
* else
* (2) if there is one or more next sibling element(s) visit the first one
* else
* (3) move to the parent until there is one or more next sibling elements
* (4) if we reach the end break the loop
*/
if (xElem->FirstChildElement()) //(1)
xElem = xElem->FirstChildElement();
else if (xElem->NextSiblingElement()) //(2)
xElem = xElem->NextSiblingElement();
else
{
while(xElem->Parent() && !xElem->Parent()->NextSiblingElement()) //(3)
xElem = xElem->Parent();
if(xElem->Parent() && xElem->Parent()->NextSiblingElement())
xElem = xElem->Parent()->NextSiblingElement();
else //(4)
break;
}//else
}//while
}
(为了完整起见)示例如何调用函数:
tinyxml2::XMLDocument xXmlDocument;
xXmlDocument.Parse(sXmlDocument.c_str());
parseXML(xXmlDocument, "user",[](tinyxml2::XMLNode* xElem)
{
int iPass;
xElem->QueryIntAttribute( "pass", &iPass );
std::cout << iPass << "n";
});
您还可以通过将递归函数与 lamda-function 结合使用作为处理程序来逐个循环访问 XML 元素。
//
// This function will iterate through your XML tree and call the 'parseElement' function for each found element.
//
void RecursiveXMLParse(TiXmlElement* element, std::function<void(TiXmlElement*)>& parseElement)
{
if (element != nullptr)
{
parseElement(element);
auto child = element->FirstChildElement();
if (child != nullptr)
{
RecursiveXMLParse(child, parseElement);
}
for (auto sibling = element->NextSiblingElement(); sibling != nullptr; sibling = sibling->NextSiblingElement())
{
RecursiveXMLParse(sibling, parseElement);
}
}
}
用法:只需将 XML 根元素和数据处理程序 lambda 函数传递给递归解析器函数。
int main()
{
//
// Define your data handler labmda
//
std::function<void(TiXmlElement*)>parseElement = [&](TiXmlElement* e) -> void
{
if (std::string(elem->Value()).compare("user"))
{
// Parse your user data
}
};
// Pass the root element along with the above defined lambda to the recursive function
RecursiveXMLParse(doc.RootElement(), parseElement);
return 0;
}
XMLElement *getElementByName(XMLDocument &ele, std::string const &elemt_value)
{
XMLElement *elem = ele.FirstChildElement(); //Tree root
while (elem)
{
if (!std::string(elem->Value()).compare(elemt_value))
return elem;
if (elem->FirstChildElement())
{
elem = elem->FirstChildElement();
}
else if (elem->NextSiblingElement())
{
elem = elem->NextSiblingElement();
}
else
{
if (elem->Parent()->ToElement()->NextSiblingElement())
{
elem = elem->Parent()->ToElement()->NextSiblingElement();
}
else if (elem->Parent()->ToElement()->FirstChildElement()
&& strcmp(elem->Name(), elem->Parent()->ToElement()->FirstChildElement()->Name()))
{
elem = elem->Parent()->ToElement()->FirstChildElement();
}
else {
break;
}
}
}
return NULL;
}
上面给出的解决方案中的一个小调整
实际上,你想找到第一个"toto/tutu/tata/user"。因为想象文档
<toto>
<tutu>
<user />
</tutu>
<user />
</toto>
您认为第一个"用户"是什么,"toto/tutu/user"或"toto/user"?在您的示例中,使用 TiXmlHandle 可以使事情变得更容易。TiXmlHandle::Child(), TiXmlHandle::FirstChild(), TiXmlHandle::ChildElement() 检查参数是否为 NULL。
TiXmlDocument doc("test.xml");
if (doc.LoadFile()) {
TiXmlHandle h(&doc);
TiXmlElement* elem = h
.FirstChildElement()
.FirstChildElement()
.FirstChildElement()
.Child("user", 0).ToElement(); //0 stands for "first occurrence"
if (elem) {
printf(" %s %sn", elem->Value(), elem->Attribute("pass"));
} else {
printf("None");
}
}
为了更方便,请考虑使用tinyxpath。在那里,您可以通过字符串"/toto/tutu/tata/user"找到您的元素。或者简单地用"*/用户"表示遇到的第一个"用户"。
- A*路径查找-如何有效地更新openNodes优先级队列中的节点值
- N-Ary 树 C++ - 如何查找节点的级别
- 在由邻接列表表示的树中查找节点到另一个给定节点之间的路径
- 用于在链表中查找节点的算法
- 查找树中两个节点之间的最大成本边
- 查找树(不属于任何特定类型的简单连接树)中两个节点之间的路径
- 沿着路径查找节点数量
- 在 n 元树 c++ 中查找给定节点的祖先
- C++ 查找无序树中具有最高值的节点
- 查找二叉树的父节点函数
- 在 C 语言的链表中查找最小的两个节点
- 在从通用树转换的二进制树中查找节点的父
- 在具有任意数量子级的节点树中查找节点
- 如何在 NS2 中查找节点 ID
- 查找节点崩溃的函数
- 递归查找节点函数
- 查找节点树的最大路径和
- 使用 Dijkstra 和最小堆 c++ 查找节点之间的最短路径
- 遍历查找节点中的顶点并返回索引
- c++如何在树上添加/查找节点??:/.