c++ 使用TinyXML在XML文档中查找特定节点

tkqqtvp1  于 2023-04-08  发布在  其他
关注(0)|答案(5)|浏览(213)

我想用TinyXML从一个xml文件中解析一些数据。
下面是我的text.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>

我想访问第一个元素'user'。方法如下:

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函数)。我只是想获得一个指向元素的指针,其中的值是我正在寻找的。有人可以帮助我吗?提前感谢您的帮助。

xyhw6mcr

xyhw6mcr1#

试试这个

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);
}
o7jaxewo

o7jaxewo2#

阿迪的答案没有工作时,我只是复制粘贴到我的代码,但我修改了它,现在它为我工作的罚款。因为我做了相当多的变化,我想我应该张贴我的最终代码在这里。

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
    }

(for如何调用函数:

tinyxml2::XMLDocument xXmlDocument;

        xXmlDocument.Parse(sXmlDocument.c_str());

        parseXML(xXmlDocument, "user",[](tinyxml2::XMLNode* xElem)
        {
            int iPass;
            xElem->QueryIntAttribute( "pass", &iPass );
            std::cout << iPass << "\n";
        });
s3fp2yjn

s3fp2yjn3#

还可以使用递归函数和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函数传递给递归Parser-function即可。

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;
}
z9ju0rcb

z9ju0rcb4#

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;
}

//上面给出的解决方案中的一个小调整

qyuhtwio

qyuhtwio5#

实际上,您希望找到第一个“toto/tutu/tata/user”。

<toto>
    <tutu>
        <user />
    </tutu>
    <user />
</toto>

您认为第一个“user”是“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 %s\n", elem->Value(), elem->Attribute("pass"));
    } else {
        printf("None");
    }
}

为了更方便,考虑使用tinyxpath。在那里,您可以通过字符串“/toto/tutu/tata/user”找到您的元素。或者简单地“*/user”表示遇到的第一个“user”。

相关问题