2017-05-03 2 views
2

Ma question est liée à cette question: Boost property_tree: multiple values per key et à une question postée à cette question: Boost property_tree: multiple values per key, on a template class.Boost :: property_tree: Utiliser std :: vector <> dans l'analyseur XML pour stocker plusieurs valeurs dans une clé

J'essaye d'analyser un fichier XML dans lequel plusieurs valeurs sont listées à une seule valeur de clé en utilisant std::vector<>. Le code suivant est ce que je l'ai mis en œuvre jusqu'à présent:

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

namespace boost { namespace property_tree 
{ 

template<typename type> 
struct vector_xml_translator 
{ 
    boost::optional<std::vector<type> > get_value(const std::string& str) 
    { 
     if (!str.empty()) 
     { 
      std::vector<type> values; 
      std::stringstream ss(str); 

      while (ss) 
      { 
       type temp_value; 
       ss >> temp_value; 
       values.push_back(temp_value); 
      } 

      return boost::optional<std::vector<type> >(values); 
     } 
     else 
     { 
      return boost::optional<std::vector<type> >(boost::none); 
     } 
    } 

    boost::optional<std::string> put_value(const std::vector<type>& b) 
    { 
     std::stringstream ss; 
     for (unsigned int i = 0; i < b.size(); i++) 
     { 
      ss << b[i]; 
      if (i != b.size()-1) 
      { 
       ss << " "; 
      } 
     } 
     return boost::optional<std::string>(ss.str()); 
    } 
}; 

template<typename ch, typename traits, typename alloc, typename data_type> 
struct translator_between<std::basic_string<ch, traits, alloc>, std::vector<data_type> > 
{ 
    typedef vector_xml_translator<data_type> type; 
}; 

} // namespace property_tree 
} // namespace boost 

Un exemple minimal pour tester ce code est le suivant:

#include <fstream> 
#include <iostream> 
#include <boost/property_tree/ptree.hpp> 
#include <XML_Vector_Translator.hpp> 


int main() 
{ 
    using boost::property_tree::ptree; 

    std::vector<double> test_vector; 

    test_vector.push_back(1); 
    test_vector.push_back(6); 
    test_vector.push_back(3); 


    ptree pt; 
    pt.add("base", test_vector); 

    std::ofstream os("test_file.xml"); 

    write_xml(os, pt, boost::property_tree::xml_writer_settings<std::string>(' ', 2)); 

    std::ifstream is("test_file.xml"); 

    ptree pt_2; 
    read_xml(is, pt_2); 

    std::vector<int> test_vector_2; 

    test_vector_2 = pt_2.get<std::vector<int> >("base"); 

    for (unsigned int i = 0; i < test_vector_2.size(); i++) 
    { 
     std::cout << test_vector_2[i] << std::endl; 
    } 

    return 0; 
} 

Quand je lance ce code, je reçois un certain nombre d'erreurs, qui conduisent moi de croire que l'enregistrement de la structure du traducteur n'est pas correct. Est-ce que quelqu'un a une idée de comment résoudre ce problème et/ou améliorer ce code?

Répondre

1
  1. Comme l'ancienne réponse aussi des points out¹ vous devez satisfaire aux exigences de boost::property_tree::detail::is_translator, vous devez donc les internal_type/external_type typedefs.

    typedef T internal_type; 
    typedef T external_type; 
    
  2. Ensuite, la boucle est mauvaise, vous devez vérifier le résultat de l'extraction de la valeur:

    while (ss >> temp_value) 
        values.push_back(temp_value); 
    
  3. Il est une mauvaise pratique de mettre vos propres types dans l'espace de noms de boost. Seule la spécialisation de translator_between<> doit être là.

  4. Vous pouvez simplifier et généraliser une grande partie du code

Dans une démonstration de travail:

Live On Coliru

#include <boost/optional.hpp> 
#include <boost/property_tree/ptree.hpp> 
#include <vector> 
#include <list> 

namespace mylib { namespace xml_translators { 

    template<typename T> struct container 
    { 
     // types 
     typedef T internal_type; 
     typedef T external_type; 

     boost::optional<T> get_value(const std::string& str) const 
     { 
      if (str.empty()) 
       return boost::none; 

      T values; 
      std::stringstream ss(str); 

      typename T::value_type temp_value; 
      while (ss >> temp_value) 
       values.insert(values.end(), temp_value); 

      return boost::make_optional(values); 
     } 

     boost::optional<std::string> put_value(const T& b) { 
      std::stringstream ss; 
      size_t i = 0; 
      for (auto v : b) 
       ss << (i++?" ":"") << v; 
      return ss.str(); 
     } 
    }; 

} } 

namespace boost { namespace property_tree { 
    template<typename ch, typename traits, typename alloc, typename T> 
     struct translator_between<std::basic_string<ch, traits, alloc>, std::vector<T> > { 
      typedef mylib::xml_translators::container<std::vector<T> > type; 
     }; 

    template<typename ch, typename traits, typename alloc, typename T> 
     struct translator_between<std::basic_string<ch, traits, alloc>, std::list<T> > { 
      typedef mylib::xml_translators::container<std::list<T> > type; 
     }; 
} } 

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

int main() 
{ 
    std::stringstream ss; 
    using boost::property_tree::ptree; 

    { 
     ptree pt; 
     pt.add("base", std::vector<double> { 1, 6, 3 }); 

     write_xml(ss, pt, boost::property_tree::xml_writer_settings<std::string>(' ', 2)); 
    } 

    { 
     ptree pt; 
     read_xml(ss, pt); 

     std::cout << "As string: '" << pt.get("base", "") << "'\n"; 
     auto roundtrip = pt.get<std::list<int> >("base"); 
     for (auto i : roundtrip) 
      std::cout << i << std::endl; 
    } 
} 

Prints

As string: '1 6 3' 
1 
6 
3 

¹ Boost property_tree: multiple values per key, voir aussi le traducteur d'identité http://www.boost.org/doc/libs/1_64_0/doc/html/boost/property_tree/id_translator.html

+0

Merci pour cette réponse claire et utile – Sjonnie

+0

Graag gedaan. Blij dat het hielp! – sehe