2012-12-28 11 views
6

Mam struktury XML wzdłuż linii:Jak iteracyjne nad strukturę XML w boost :: 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> 

które są odczytywane w boost::property_tree, Są 1..Many<SomeElement> s, a następnie w dowolnej głębokości w którym element nie może być 1..Many<ElementIWant> s

Czy iterację na <ElementIWant> bezpośrednio (w jednej pętli) w kolejności, w jakiej appea r w dokumencie?

Mam spojrzał na equal_range

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

Jednak wydaje się, że nie zwróci węzłów (co podejrzewam dlatego equal_range działa na poziomie węzła drzewa dostarczonego), który doprowadza mnie do pytania powyżej. ..

+0

Czy wypróbowałeś 'equal_range (" SomeElement.AnotherElement.ElementIWant ");'? Nie jestem pewien, co się stanie, gdy pojawią się dwie kopie SomeElement. –

+0

Wybuch z przeszłości! @TreborRude Myślę, że w tym czasie nie zachowywało się tak, jak chciałem, lub nie działało. Być może biblioteka została ulepszona, ponieważ to robię - ale w tej chwili jestem daleko od tego obszaru kodu. :) Dzięki za sugestię. – Caribou

Odpowiedz

4

Bezpośrednie powtórzenie wszystkich elementów nie jest możliwe; the documentation says

Nie ma sposobu na powtórzenie całego drzewa.

Teraz można użyć rekursji i zastosować algorytmy STL na każdym poziomie, aby naśladować to; nie pasuje do Twojego wymóg ten sposób w jednej pętli w moim próbki poniżej, ale to nie działa:

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

Tutaj jest wersja, że ​​jest „w pełni” rekurencyjne i zachować kolejność występowania:

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

Byłem pełen nadziei, że może być jakiś trik, ale ja też gram z czymś podobnym. Zobaczę, czy mogę włączyć to, co zrobiłeś powyżej - Thx za odpowiedź. – Caribou

+0

BTW zaakceptuje to, ponieważ jest to właściwa odpowiedź, ale podejrzewam, ale chcę zagrać jeszcze przez jakiś czas, zanim to zrobię :) – Caribou

+0

@Caribou Właściwie gram trochę po mojej stronie, będę edytować z "w pełni" wersja rekursywna, ponieważ w tej pierwszej próbie kolejność pojawiania się nie jest zachowana! – piwi

Powiązane problemy