2012-12-28 9 views
6

Ich habe eine XML-Struktur entlang der Linien von:Wie im Boost-XML-Struktur iterieren :: property_tree

<root> 
<SomeElement> 
    <AnotherElement> 
    <ElementIWant x="1" y="1"/> 
    </AnotherElement> 
</SomeElement> 
<SomeElement> 
    <AnotherElement> 
    <ElementIWant x="1" y="1"/> 
    <ElementIWant x="2" y="1"/> 
    <ElementIWant x="3" y="1"/> 
    </AnotherElement> 
</SomeElement> 
</root> 

, die in eine boost::property_tree gelesen wird, gibt es 1..Many<SomeElement> s, und dann bei einer beliebigen Tiefe innerhalb dieses Element könnte es sein 1..Many<ElementIWant> s

gibt es einen Weg über die <ElementIWant> direkt (in einer einzigen Schleife) in der Reihenfolge iterieren, dass sie appea r im doc?

Ich habe bei equal_range sah

void iterateOverPoints() 
{ 
    const char* test = 
    "<?xml version=\"1.0\" encoding=\"utf-8\"?><root>" 
     "<SomeElement>" 
     "<AnotherElement>" 
     "<ElementIWant x=\"1\" y=\"1\"/>" 
     "</AnotherElement>" 
     "</SomeElement>" 
     "<SomeElement>" 
     "<AnotherElement>" 
     "<ElementIWant x=\"1\" y=\"1\"/>" 
     "<ElementIWant x=\"2\" y=\"1\"/>" 
     "<ElementIWant x=\"3\" y=\"1\"/>" 
     "</AnotherElement>" 
     "</SomeElement>" 
    "</root>"; 

    boost::property_tree::ptree message; 
    std::istringstream toParse(test); 
    boost::property_tree::read_xml(toParse,result_tree); 

    //Now we need to locate the point elements and set the x/y accordingly. 
    std::pair< boost::property_tree::ptree::const_assoc_iterator, 
       boost::property_tree::ptree::const_assoc_iterator > result = 
     message.equal_range("ElementIWant"); 

    for(boost::property_tree::ptree::const_assoc_iterator it = result.first; 
      it != result.second; ++it) 
    { 
     std::cout << it->first << " : "; 
     const boost::property_tree::ptree& x = it->second.get_child("<xmlattr>.x"); 
     const boost::property_tree::ptree& y = it->second.get_child("<xmlattr>.y"); 
     std::cout << x.get_value<int>() << "," << y.get_value<int>() << "\n"; 
    } 

    return; 
} 

jedoch es Knoten zurück zum Scheitern verurteilt scheint (was ich vermute, weil equal_range auf der Ebene des Baumknoten geliefert arbeitet) Was mich auf die Frage oben bringt. ..

+0

Haben Sie versucht, 'equal_range (" SomeElement.AnotherElement.ElementIWant ");'? Nicht sicher, was das tun wird, wenn es zwei Kopien von SomeElement gibt. –

+0

Explosion aus der Vergangenheit! @TreborRude Ich denke, zu der Zeit hat es sich nicht so verhalten, wie ich es wollte, oder es hat nicht funktioniert. Vielleicht wurde die Bibliothek seither verbessert - aber ich bin im Moment weit von diesem Bereich entfernt. :) Danke für den Vorschlag. – Caribou

Antwort

4

Es ist nicht möglich, direkt über alle Elemente zu iterieren; the documentation says

Es gibt keine Möglichkeit, über den gesamten Baum zu iterieren.

Jetzt könnten Sie Rekursion verwenden und STL-Algorithmen auf jeder Ebene anwenden, um das nachzuahmen; sie paßt nicht Ihre Anforderung dies unten in meiner Probe in einer einzigen Schleife zu tun, aber es hat funktioniert:

template <typename InputIt, typename OutputIt, typename Compare> 
void collect(InputIt first, InputIt last, OutputIt dest, Compare comp) 
{ 
    typedef typename std::iterator_traits<InputIt>::reference reference; 

    std::copy_if (
     first, last, dest, 
     [comp] (reference what) { return comp(what.first); }); 

    std::for_each (
     first, last, 
     [dest, comp] (reference what) { collect(what.second.begin(), what.second.end(), dest, comp); }); 
} 


std::vector<std::pair<std::string, ptree>> match; 

collect(
    xml.begin(), xml.end(), std::back_inserter(match), 
    [] (const std::string& key) { return key == "ElementIWant"; }); 

for (auto pair: match) 
{ 
    std::cout << pair.first << std::endl; 
} 

Hier ist eine Version, die „voll“ rekursive und die Erhaltung der Reihenfolge des Erscheinens ist:

template <typename InputIt, typename OutputIt, typename Compare> 
void collect_recursive(InputIt first, InputIt last, OutputIt dest, Compare comp) 
{ 
    typedef typename std::iterator_traits<InputIt>::reference reference; 

    if (first == last) 
    { 
     return; 
    } 

    auto begin = first->second.begin(); 
    auto end = first->second.end(); 

    if (begin != end) 
    { 
     collect_recursive (begin, end, dest, comp); 
    } 

    if (comp (first->first)) 
    { 
     dest = *first; 
    } 

    collect_recursive (++first, last, dest, comp); 
} 
+0

Ich hatte die Hoffnung, dass es einen Trick geben könnte, aber ich spiele jetzt auch mit etwas ähnlichem. Ich werde sehen, ob ich das, was du oben getan hast, einarbeiten kannst - Danke für die Antwort. – Caribou

+0

BTW wird dies akzeptieren, da es die richtige Antwort ist, die ich vermute, aber ich möchte noch eine Weile spielen :) – Caribou

+0

@Caribou Eigentlich spiele ich ein bisschen auf meiner Seite, ich werde mit einem "Fully" bearbeiten rekursive Version, weil bei diesem ersten Versuch die Reihenfolge des Auftretens nicht erhalten bleibt! – piwi