【问题标题】:How can I use POCO to parse an xml file and extract a particular node to a std::string?如何使用 POCO 解析 xml 文件并将特定节点提取到 std::string?
【发布时间】:2013-03-15 14:20:53
【问题描述】:

我想使用 POCO 的库提取单个节点,但不知道该怎么做。我是 XML 新手。

XML 本身看起来像这样(缩写):

<?xml version="1.0" encoding="UTF-8"?>
<!-- Created by XMLPrettyPrinter on 11/28/2012 from  -->
<sbml xmlns = "http://www.sbml.org/sbml/level2/version4" level = "2" version = "4">
<model id = "cell">
  <listOfSpecies>
</listOfSpecies>
  <listOfParameters>
     <parameter id = "kk1" value = "1"/>
  </listOfParameters>
  <listOfReactions>
     <reaction id = "J1" reversible = "false">
... much stuff here ..
  </listOfReactions>
</model>
</sbml>

我想提取 listOfReactions 节点中的所有内容并将其存储在 std::string 中,以便以后进行 MD5 哈希处理。

我试过这个:

ifstream in(JoinPath(gTestDataFolder, "Test_1.xml").c_str());
InputSource src(in);
DOMParser parser;
AutoPtr<Document> pDoc = parser.parse(&src);
NodeIterator it(pDoc, Poco::XML::NodeFilter::SHOW_ALL);
Node* pNode = it.nextNode();

while(pNode)
{
    clog<<pNode->nodeName()<<endl;
    string elementID = "listOfReactions";
    if(pNode->nodeName() == "listOfReactions")
    {
         //Extract everything in this node... how???
    }

    pNode = it.nextNode();
}

【问题讨论】:

  • 那么,你有什么尝试?
  • 我已经尝试过他们的例子,涉及一个 DOM 解析器。我能够到达节点,但不知道如何提取其内容。我可以打印 pNode->getNodeValue(),但什么也没打印..
  • 节点“listOfReactions”不保存该值。它有一个名为“#text”的子节点保存该值。这不是很明显,但是当执行“pNode->firstNode()->nodeValue()”时,你会得到值。

标签: c++ xml xml-parsing poco sbml


【解决方案1】:

我自己也遇到过类似的问题。例如,在应用了“Poco::XML::NodeFilter::SHOW_ALL”过滤器的情况下,遍历 XML 文档时将包括所有节点类型(元素、文本、CDataSection 等)。我发现 POCO 不包含从“NextNode()”返回的每个节点中的所有数据。

如果想要访问 XML 节点的属性,首先必须使用“hasAttributes()”查询该节点以检查它是否具有任何属性,如果有,则遍历这些属性中的每一个以查找兴趣。

XML 示例:

<?xml version="1.0"?>
<reaction id="J1" reversible="false">

C++ 示例:

...
Poco::XML::NamedNodeMap* attributes = NULL;
Poco::XML::Node* attribute = NULL;

while(pNode)
{
 if( (pNode->nodeName() == "reaction") && pNode->hasAttributes())
 {
   attributes = pNode->attributes(); 
   for(unsigned int i = 0; i < attributes->length(); i++)
   {
     attribute = attributes->item(i);
     cout << attribute->nodeName() << " : " << attribute->nodeValue() << endl
   }
  }
  pNode = it.nextNode();
}
...

应该输出:

id : J1
reversible : false

如果想要访问两个 XML 标记之间的文本,如下面的 XML 示例所示,首先必须找到名称与感兴趣的标记匹配的节点,就像您在示例中所做的那样,然后通过调用“NextNode()”检查下一个节点,以查看该节点是否具有节点名称“#text”或“#cdata-section”。如果是这种情况,这个“下一个节点”的值将包含 XML 标记之间的文本。

XML 示例:

<?xml version="1.0"?>
<listOfReactions>Some text</listOfReactions>

C++ 示例:

...
while(pNode)
{
 if(pNode->nodeName() == "listOfReactions")
 {
   pNode = it.nextNode();
   if(pNode->nodeName() != "#text")
   {
     continue; //No text node present
   }
   cout << "Tag Text: " << pNode->nodeValue() << endl;
  }
  pNode = it.nextNode();
}
...

应该输出:

Some text

【讨论】:

    【解决方案2】:

    尝试使用these slidesPoco documentation 作为 API 参考。

    还有一个很好的tutorial here,它有一个简单易懂的例子来说明你正在尝试做什么。

    【讨论】:

    • 已经看过所有这些了。然而,没有人解释如何做这个简单的事情。
    • 谢谢,但该教程是关于使用 XMLConfiguration 的自定义配置文件。我看不出这对我有什么帮助。
    【解决方案3】:

    游戏晚了,但也许仍然有用。我正在研究 Poco XML 以提取以 xml 传递的天气数据。我发现@JBently 提到的 PDF 幻灯片是一个很好的介绍。这提供了 hpp 文件。这个example 涵盖了实现。我省略了 LexicalHandler。

    我查看字符串 listOfReactions,并在找到时将属性名称和值添加到 startElement() 中的字符串。在 characters() 中,我将节点中的文本添加到字符串中,并将其添加到可以遍历的向量中。

    输出:

    id=J1,reversible=false,false move
    id=J2,reversible=true,真移动

    我稍微更改了您的 xml 以进行测试,并转义了双引号以在程序中使用。

    <?xml version=\"1.0\" encoding=\"UTF-8\"?><sbml xmlns = \"http://www.sbml.org/sbml/level2/version4\" level = \"2\" version = \"4\">
        <model id = \"cell\">
            <listOfSpecies>species</listOfSpecies>
            <listOfParameters>
                <parameter id = \"kk1\" value = \"1\"/>
            </listOfParameters>
            <listOfReactions>
                <reaction id = \"J1\" reversible = \"false\">false move</reaction>
                <reaction id = \"J2\" reversible = \"true\">true move</reaction>
            </listOfReactions>
        </model>
    </sbml>
    

    主要:

    #include <iostream>
    
    #include "MyHandler.hpp"
    
    using namespace std;
    
    int main() {
        auto s = {XML file from above};
        MyHandler handler {};
        Poco::XML::SAXParser parser {};
        parser.setFeature(Poco::XML::XMLReader::FEATURE_NAMESPACES, false);
        parser.setFeature(Poco::XML::XMLReader::FEATURE_NAMESPACE_PREFIXES, true);
        parser.setContentHandler(&handler);
    
        try {
            parser.parseString(s);
        } catch (Poco::Exception& e) {
            cerr << e.displayText() << endl;
        }
        auto saved = handler.saved_reactions();
        for (auto& i : saved) {
            cout << i << endl;
        }
        return 0;
    }
    

    MyHandler.hpp:

    #ifndef MYHANDLER_HPP_
    #define MYHANDLER_HPP_
    
    #include <iostream>
    #include <vector>
    #include <Poco/SAX/Attributes.h>
    #include <Poco/SAX/ContentHandler.h>
    #include <Poco/SAX/SAXParser.h>
    
    class MyHandler: public Poco::XML::ContentHandler {
    public:
        MyHandler();
        virtual ~MyHandler();
    
        // ContentHandler overrides, begin.
        void setDocumentLocator(const Poco::XML::Locator* loc);
        void startDocument();
        void endDocument();
        void startElement(
                const Poco::XML::XMLString&,
                const Poco::XML::XMLString&,
                const Poco::XML::XMLString&,
                const Poco::XML::Attributes&);
        void endElement(
                const Poco::XML::XMLString&,
                const Poco::XML::XMLString&,
                const Poco::XML::XMLString&);
        void characters(const Poco::XML::XMLChar ch[], int, int);
        void ignorableWhitespace(const Poco::XML::XMLChar ch[], int, int);
        void processingInstruction(const Poco::XML::XMLString&, const Poco::XML::XMLString&);
        void startPrefixMapping(const Poco::XML::XMLString&, const Poco::XML::XMLString&);
        void endPrefixMapping(const Poco::XML::XMLString&);
        void skippedEntity(const Poco::XML::XMLString&);
        // ContentHandler overrides, end
        std::vector<std::string> saved_reactions();
    
    private:
        bool show = false;
        std::string reactions_s {};
        std::vector<std::string> reactions_v {};
    };
    
    #endif /* MYHANDLER_HPP_ */
    

    MyHandler.cpp:

    #include "MyHandler.hpp"
    
    MyHandler::MyHandler() {}
    MyHandler::~MyHandler() {}
    
    void MyHandler::setDocumentLocator(const Poco::XML::Locator* loc) {
    }
    
    void MyHandler::startDocument() {
    }
    
    void MyHandler::endDocument() {
    }
    
    void MyHandler::startElement(const Poco::XML::XMLString& namespaceURI, const Poco::XML::XMLString& localName, const Poco::XML::XMLString& qname, const Poco::XML::Attributes& attributes) {
        int x {0};
        std::cout << "qname: " << qname << std::endl;
        /*    std::cout << "getValue(): " << attributes.getValue(qname) << std::endl;
        std::cout << "getLength(): " << attributes.getLength() << std::endl;*/
        if (qname == "listOfReactions") {
            show = true;
        }
        if (show) {
            if (attributes.getLength()) {
                reactions_s.clear();
                x = attributes.getLength();
                for (int i = 0; i < x; ++i) {
                    std::cout << "getQName(): " << attributes.getQName(i) << ", getValue(): " << attributes.getValue(i) << std::endl;
                    if (reactions_s.size()) reactions_s += ",";
                    reactions_s += attributes.getQName(i) + "=" + attributes.getValue(i);
                }
            }
        }
    }
    
    void MyHandler::endElement(const Poco::XML::XMLString& allocator,
            const Poco::XML::XMLString& allocator1,
            const Poco::XML::XMLString& allocator2) {
    }
    
    void MyHandler::characters(const Poco::XML::XMLChar ch[], int start, int length) {
        std::cout << std::string(ch + start, length) << std::endl;
        if (show) {
            reactions_s += "," + std::string(ch + start, length);
            reactions_v.emplace_back(reactions_s);
        }
    }
    
    void MyHandler::ignorableWhitespace(const Poco::XML::XMLChar ch[], int start, int length) {
    }
    
    void MyHandler::processingInstruction(const Poco::XML::XMLString& allocator, const Poco::XML::XMLString& allocator1) {
    }
    
    void MyHandler::startPrefixMapping(const Poco::XML::XMLString& allocator, const Poco::XML::XMLString& allocator1) {
    }
    
    void MyHandler::endPrefixMapping(const Poco::XML::XMLString& allocator) {
    }
    
    std::vector<std::string> MyHandler::saved_reactions() {
        return reactions_v;
    }
    
    void MyHandler::skippedEntity(const Poco::XML::XMLString& allocator) {
    }
    

    【讨论】:

      【解决方案4】:

      假设 XML 在文件“hello.xml”中

      <root>
          <headers>
              <header>Hello</header>
              <header>World</header>
          </headers>
      </root>
      

      可以这样解析:-

      #include <string>
      #include <sstream>
      #include <Poco/Exception.h>
      #include <Poco/AutoPtr.h>
      #include <Poco/Util/XMLConfiguration.h>
      
      using namespace std;
      using namespace Poco;
      using namespace Poco::Util;
      
      int main(int argc, char*argv[]) {
      
          int counter = 0;
          AutoPtr apXmlConf(new XMLConfiuration("hello.xml"));
          try {
              while(1) { // Loop breaks by Poco exception
                  stringstream tag;
                  tag << "headers.header[" << counter++ << "]";
                  string header = apXmlConf->getString(tag.str());
                  cout << header << " ";
              }
          } catch(NotFoundException& e) { (void)e; }
          cout << endl;
          return 0;
      }
      

      希望对您有所帮助。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-02-12
        • 2022-11-19
        • 1970-01-01
        • 2019-05-12
        • 2021-01-13
        • 1970-01-01
        • 2019-01-16
        • 2014-03-30
        相关资源
        最近更新 更多