2012-12-28 11 views
6

Ho una struttura XML lungo le linee di:Come iterare su struttura XML in 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> 

che viene letto in un boost::property_tree, ci sono 1..Many<SomeElement> s, e poi ad una profondità arbitraria all'interno di tale elemento potrebbe esserci 1..Many<ElementIWant> s

c'è un modo per scorrere il <ElementIWant> direttamente (in un singolo ciclo) nell'ordine che appea r nel doc?

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

Tuttavia sembra non riuscire a tornare nodi (che ho il sospetto è perché equal_range funziona a livello del nodo della struttura in dotazione) che mi porta alla domanda di cui sopra. ..

+0

Hai provato 'equal_range (" SomeElement.AnotherElement.ElementIWant ");'? Non sono sicuro di cosa farà quando ci sono due copie di SomeElement, però. –

+0

Esplosione dal passato! @TreborRude Penso che al momento non si comportasse come volevo, o non ha funzionato. Forse la libreria è stata migliorata dal momento che per farlo - ma io sono ben lontano da quell'area di codice al momento. :) Grazie per il suggerimento però. – Caribou

risposta

4

Non è possibile iterare su tutti gli elementi direttamente; the documentation says

Non è possibile iterare sull'intero albero.

Ora, è possibile utilizzare la ricorsione e applicare gli algoritmi STL a ogni livello per simularlo; non si adatta la vostra esigenza di fare questo in un singolo ciclo nel mio esempio riportato di seguito, ma lo fa opere:

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

Ecco una versione che è "pienamente" ricorsivo e preservare l'ordine di apparizione:

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

Speravo che ci potesse essere qualche trucco, ma ora sto giocando anche con qualcosa di simile. Vedrò se riesco a incorporare ciò che hai fatto sopra - Thx per la risposta. – Caribou

+0

BTW accetterà questo in quanto è la risposta giusta, ma ho il sospetto che voglio giocare per un po 'prima di farlo :) – Caribou

+0

@Caribou In realtà, sto giocando un po' dalla mia parte, così, Io modificare con un "pieno" versione ricorsiva, perché in questo primo tentativo, l'ordine di apparizione non è conservato! – piwi

Problemi correlati