Skip to content

Instantly share code, notes, and snippets.

@usagi
Created January 30, 2017 08:45
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save usagi/ad5886604913753a42020b87c8d766ea to your computer and use it in GitHub Desktop.
picojson::value に入った picojson::object のネスト構造をドット区切りのパス文字列で引っ張り出すヘルパー ref: http://qiita.com/usagi/items/da3568d8fa61e4aafede
// 理想: picojson には実装されていないが時折欲しくなる使用例
const auto element_value = root_value.[ "aaa.bbb.ccc" ].as< double >();
// 現実: 毎度こんなに書きたくない
const double element_value
try
{
element_value =
root_value.get< picojson::object >().at( "aaa" )
.get< picojson::object >().at( "bbb" )
.get< picojson::object >().at( "ccc" )
.get< double >()
;
}
catch( ... )
{ ... }
#include <picojson.h>
#include <boost/algorithm/string.hpp>
#include <boost/optional.hpp>
#include <vector>
namespace usagi::json::picojson
{
using object_type = ::picojson::object;
using value_type = ::picojson::value;
/// @brief value_type に対しドット区切りのパスで object_type の階層を辿り value_type を引っ張り出す
static inline auto get_value( const value_type& source, const std::string& dot_separated_path )
-> value_type&
{
std::vector< std::string > path;
boost::split( path, dot_separated_path, boost::is_any_of( "." ) );
auto out = const_cast< const value_type* >( &source );
for ( const auto& path_part : path )
out = &out->get< object_type >().at( path_part );
return const_cast< value_type& >( *out );
}
}
/// @brief get_value が out_of_range や runtime_error など例外で失敗する場合に optional で例外の送出をカバーする版
static inline auto get_value_optional( const value_type& source, const std::string& dot_separated_path )
noexcept
-> boost::optional< value_type& >
{
try
{ return get_value( source, dot_separated_path ); }
catch ( ... )
{ return { }; }
}
/// @brief get_value + picojson::get + 可能な限りの自動的な型変換( double や string を float で取り出したり、 null を string で取り出したりもできる )
/// @param type_conversion true の場合には可能な限りの自動的な型変換を試みる。 false の場合には value_type::get のみ。
template < typename T >
static inline auto get_value_as
( const value_type& source
, const std::string& dot_separated_path
, const bool type_conversion = true
) -> T
{
const auto& v = get_value( source, dot_separated_path );
if ( type_conversion )
{
// T が数値型の場合の変換込みの処理
if ( std::is_integral< T >::value or std::is_floating_point< T >::value )
{
// double -> T
if ( v.is< double >() )
return static_cast< T >( v.get< double >() );
// string -> T
if ( v.is< std::string >() )
{
std::stringstream s;
s << v.get< std::string >();
T out;
s >> out;
if ( s.fail() )
throw std::runtime_error( "cannot convert to a number type from std::string type." );
return out;
}
}
}
throw std::runtime_error( "cannot convert to a number type from value_type type." );
}
template < >
inline auto get_value_as< double >
( const value_type& source
, const std::string& dot_separated_path
, const bool type_conversion
) -> double
{
const auto& v = get_value( source, dot_separated_path );
if ( v.is< double >() )
return v.get< double >();
if ( type_conversion )
{
// string -> double
if ( v.is< std::string >() )
{
std::stringstream s;
s << v.get< std::string >();
double out;
s >> out;
if ( s.fail() )
throw std::runtime_error( "cannot convert to a number type from std::string type." );
return out;
}
}
// note: picojson による cast 失敗で適当な std::runtime_error が発行される
return v.get< double >();
}
template < >
inline auto get_value_as< std::string >
( const value_type& source
, const std::string& dot_separated_path
, const bool type_conversion
) -> std::string
{
const auto& v = get_value( source, dot_separated_path );
if ( v.is< std::string >() )
return v.get< std::string >();
if ( type_conversion )
{
// value_type の operator<< で string にして返す
std::stringstream s;
s << v;
return s.str();
}
// note: picojson による cast 失敗で適当な std::runtime_error が発行される
return v.get< std::string >();
}
template < >
inline auto get_value_as< object_type >
( const value_type& source
, const std::string& dot_separated_path
, const bool type_conversion
) -> object_type
{
const auto& v = get_value( source, dot_separated_path );
// note: picojson による cast 失敗で適当な std::runtime_error が発行される
return v.get< object_type >();
}
template < >
inline auto get_value_as< array_type >
( const value_type& source
, const std::string& dot_separated_path
, const bool type_conversion
) -> array_type
{
const auto& v = get_value( source, dot_separated_path );
// note: picojson による cast 失敗で適当な std::runtime_error が発行される
return v.get< array_type >();
}
/// @brief get_value_as が out_of_range や runtime_error など例外で失敗する場合に optional で例外の送出をカバーする版
template < typename T >
static inline auto get_value_as_optional
( const value_type& source
, const std::string& dot_separated_path
, const bool type_conversion = true
) noexcept -> boost::optional< T >
{
try
{ return get_value_as< T >( source, dot_separated_path, type_conversion ); }
catch ( ... )
{ return { }; }
}
#include <usagi/json/picojson/get_value.hxx>
#include <iostream>
#include <iomanip>
auto main() -> int
{
using namespace std;
picojson::value v;
if ( const auto& e = picojson::parse
( v
, "{ 'aaa':"
" { 'bbb':"
" { 'ccc': 1.23"
" , 'ddd': '345.6789012345678e+9'"
" }"
"}"
)
)
throw runtime_error( e.what() );
cout << v << '\n';
using namespace usagi::json::picojson;
cout << get_value_as< double >( v, "aaa.bbb.ccc" ) << '\n';
cout << get_value_as< float >( v, "aaa.bbb.ccc" ) << '\n';
cout << get_value_as< int >( v, "aaa.bbb.ccc" ) << '\n';
cout << get_value_as< uint16_t >( v, "aaa.bbb.ccc" ) << '\n';
cout << to_string( get_value_as< uint8_t >( v, "aaa.bbb.ccc" ) ) << '\n';
cout << get_value_as< string >( v, "aaa.bbb.ccc" ) << '\n';
cout << get_value_as< picojson::object >( v, "aaa.bbb" ).at( "ccc" ) << '\n';
cout << setprecision( 10 ) << get_value_as< float >( v, "aaa.bbb.ddd" ) << '\n';
}
{"aaa":{"bbb":{"ccc":1.23,"ddd":2.3399999141693115,"eee":"345.6789012345678e+9"}}}
1.23
1.23
1
1
1
1.23
1.23
3.456789053e+011
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment