1

我想知道是否有一种方法可以通过遍历父节点的向量来获取子节点中的数据。我有一个我计划经常更改的 XML 文件,因此我想避免对属性名称进行硬编码。因此,我想在我的子节点中提取数据,而不用使用pt.get_child(myparentNodes). 我主要有这个。

非常感谢任何帮助!

vector<string> parentNodes;
ptree pt;
ifstream fileName("myxml");
read_xml(fileName, pt);

for(const ptree::value_type &parent : pt)
{
    cout << parent.first << std::endl;
    parentNodes.push_back(parent.first);
  
}
 for(int i=0; i<parentNodes.size();i++)
 {
BOOST_FOREACH(boost::property_tree::ptree::value_type const &node,pt.get_child(parentNodes[i]))

/* I'm having trouble properly accessing the children nodes here */
4

1 回答 1

1

在您的代码段中(稍微清理一下):

std::vector<std::string> parentNodes;
for(auto const& parent : pt) {
    std::cout << parent.first << std::endl;
    parentNodes.push_back(parent.first);
}

似乎正在将树节点的名称parentNodes收集到. 但是,这假定名称是唯一的或非空的。

属性名称不必是唯一的,也不保证它们不为空。事实上,属性树中的数组经常被建模为只有未命名子节点的节点。

那么你在获取相应节点的子节点时遇到了麻烦。让我们首先以乏味的方式进行(再次假设名称是唯一的):

for (size_t i = 0; i < parentNodes.size(); i++) {
    auto& parent = pt.get_child(parentNodes[i]);

    for (auto const& child : parent) {
        std::cout << child.first << std::endl;
    }
}

当然使用 ranged-for 要容易得多:

for (auto const& name : parentNodes) {
    auto& parent = pt.get_child(name);

    for (auto const& child : parent) {
        std::cout << child.first << std::endl;
    }
}

更好的是

您可以避免关于命名以及第二个循环和向量存储的假设:

for (auto const& parent : pt) {
    std::cout << parent.first << std::endl;

    auto& node = parent.second;
    for (auto const& child : node) {
        std::cout << child.first << std::endl;
    }
}

这是因为迭代器指向一对(key, value). 事实上,在最近的编译器上,您可以使用结构化绑定编写循环:

for (auto const& [name, node] : pt) {
    std::cout << name << std::endl;

    for (auto const& child : node) {
        std::cout << child.first << std::endl;
    }
}

仍然在做同样的事情。

概括

你说你想让这个通用。但是,假设父/子关系的两层层次结构并不让我觉得“通用”。上次我将您链接到一些显示通用遍历(例如在整个树中查找模式)的示例,例如使用 boost 对 xml 文件进行迭代- 该示例中的一个函数:

活在魔杖盒上

#include <boost/property_tree/xml_parser.hpp>
#include <iostream>

using boost::property_tree::ptree;
static auto settings = boost::property_tree::xml_writer_make_settings<std::string>(' ', 4);

template <typename Out>
Out enumerate_nodes(ptree const& pt, ptree::path_type path, Out out) {
    if (path.empty())
        return out;

    if (path.single()) {
        auto name = path.reduce();
        for (auto& child : pt) {
            if (child.first == name)
                *out++ = child.second;
        }
    } else {
        auto head = path.reduce();
        for (auto& child : pt) {
            if (head == "*" || child.first == head) {
                out = enumerate_nodes(child.second, path, out);
            }
        }
    }

    return out;
}

int main() {
    std::ifstream fileName("input.xml");
    ptree pt;
    read_xml(fileName, pt);

    for (auto const& [name, node] : pt) {
        std::cout << name << std::endl;

        for (auto const& child : node)
            std::cout << child.first << std::endl;
    }

    std::vector<std::reference_wrapper<ptree const>> matched;
    enumerate_nodes(pt, "root.parent2.child3", back_inserter(matched));

    for (ptree const& match : matched)
        std::cout << "Matched: " << match.get_value<std::string>() << "\n";
}

使用时input.xml

<?xml version="1.0"?>
<root>
    <parent1>
        <child1>parent1/child1</child1>
        <child2>parent1/child2</child2>
        <child3>parent1/child3</child3>
        <child4>parent1/child4</child4>
    </parent1>
    <parent2>
        <child1>parent2/child1</child1>
        <child2>parent2/child2</child2>
        <child3>parent2/child3</child3>
        <child4>parent2/child4</child4>
    </parent2>
    <parent3>
        <child1>parent3/child1</child1>
        <child2>parent3/child2</child2>
        <child3>parent3/child3</child3>
        <child4>parent3/child4</child4>
    </parent3>
    <parent4>
        <child1>parent4/child1</child1>
        <child2>parent4/child2</child2>
        <child3>parent4/child3</child3>
        <child4>parent4/child4</child4>
    </parent4>
</root>

印刷

root
parent1
parent2
parent3
parent4
Matched: parent2/child3
于 2020-08-07T00:14:54.697 回答