Изменение способа чтения boost:: Property tree переводит строки в bool
Я потерялся в заголовочных файлах для boost property_tree и, учитывая отсутствие документации вокруг нижних слоев, я решил спросить, что простой способ-переехать транслятор потока, чтобы изменить, как анализируются логические значения.
проблема в том, что на входной стороне дерева, собственность, есть пользователи, и они могут изменять файлы конфигурации. Логическое значение может быть указано несколькими способами, например:
dosomething.enabled=true
dosomething.enabled=trUE
dosomething.enabled=yes
dosomething.enabled=ON
dosomething.enabled=1
по умолчанию поведение-проверить 0 или 1, а затем использовать
std::ios_base::boolalpha
чтобы заставить поток попытаться проанализировать значение соответствующим образом для текущей локали...что может быть безумием, если мы попытаемся отправить файл конфигурации международным клиентам.
Итак, каков самый простой способ переопределить это поведение или только bool? Не только проще всего реализовать , но и проще всего использовать-так что пользователям моего класса, производного от iptree, не нужно делать что-то особенное для Boolean ценности.
спасибо!
2 ответов
вы можете специализироваться boost::property_tree::translator_between
чтобы дерево свойств использовало пользовательский переводчик для bool
тип значения. Эта специализация должна быть видимой (т. е. #includ
ed) клиентами, желающими настроенного поведения. Вот рабочий пример:
#include <iostream>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <boost/algorithm/string/predicate.hpp>
// Custom translator for bool (only supports std::string)
struct BoolTranslator
{
typedef std::string internal_type;
typedef bool external_type;
// Converts a string to bool
boost::optional<external_type> get_value(const internal_type& str)
{
if (!str.empty())
{
using boost::algorithm::iequals;
if (iequals(str, "true") || iequals(str, "yes") || str == "1")
return boost::optional<external_type>(true);
else
return boost::optional<external_type>(false);
}
else
return boost::optional<external_type>(boost::none);
}
// Converts a bool to string
boost::optional<internal_type> put_value(const external_type& b)
{
return boost::optional<internal_type>(b ? "true" : "false");
}
};
/* Specialize translator_between so that it uses our custom translator for
bool value types. Specialization must be in boost::property_tree
namespace. */
namespace boost {
namespace property_tree {
template<typename Ch, typename Traits, typename Alloc>
struct translator_between<std::basic_string< Ch, Traits, Alloc >, bool>
{
typedef BoolTranslator type;
};
} // namespace property_tree
} // namespace boost
int main()
{
boost::property_tree::iptree pt;
read_json("test.json", pt);
int i = pt.get<int>("number");
int b = pt.get<bool>("enabled");
std::cout << "i=" << i << " b=" << b << "\n";
}
есть также хороший пример в theboostcpplibraries.com.
основываясь на этом, я написал для пользовательского парсера (объявление опущено):
boost::optional<bool> string_to_bool_translator::get_value(const std::string &s) {
auto tmp = boost::to_lower_copy(s);
if (tmp == "true" || tmp == "1" || tmp == "y" || tmp == "on") {
return boost::make_optional(true);
} else if (tmp == "false" || tmp == "0" || tmp == "n" || tmp == "off") {
return boost::make_optional(false);
} else {
return boost::none;
}
}
это только для bool и std::string, но легко расширяется.
затем,
boost::property_tree::ptree pt;
...
string_to_bool_translator tr;
auto optional_value = pt.get_optional<bool>(key, tr);