概要
先日、「読み込み」については 「picojson::value に入った picojson::object のネスト構造をドット区切りのパス文字列で引っ張り出すヘルパー」 †1 について書いた。
今回は「書き出し」についてコードの実装例を紹介したい。
必要な機能
- ドット区切りのパス文字列を階層ごとに分離する機能: boost::split (†1と同様)
- 分離されたパスの階層構造を組み立てる機能
- ユーザーの期待する型で受け入れる機能
実装詳細
基本: 分離されたパスの階層構造を組み立てる方法
/// @brief object_type に対しドット区切りのパスで object_type の階層を必要なら作成しながら辿り末梢の要素の参照を返す
static inline decltype( auto ) make_object_path
( object_type& root_object
, const std::string& dot_separated_path
)
{
// (1) ドット区切りのパス文字列を階層ごとに分離する機能
std::vector< std::string > path;
boost::split( path, dot_separated_path, boost::is_any_of( "." ) );
// (2) 分離されたパスの階層構造を組み立てる機能
auto peripheral = &root_object;
// 終端のパスを取り出しておく
const auto last_path = std::move( path.back() );
path.pop_back();
// 終端の手前のパスまで object を掘る
for ( const auto& path_part : path )
{
const auto i = peripheral->find( path_part );
// note: 末梢に必要なパスが無いか object ではない場合にのみ object を作成
if ( i == peripheral->cend() or not i->second.is< object_type >() )
(*peripheral)[ path_part ] = value_type( object_type() );
// note: root_object から末梢側へ1段階辿る
peripheral = &(*peripheral)[ path_part ].get< object_type >();
}
// 終端の value& を返す
return (*peripheral)[ last_path ];
}
}
/// @brief element_value が rvalue の場合向けの set_value
static inline auto set_value
( object_type& root_object
, const std::string& dot_separated_path
, value_type&& element_value
)
{ detail::make_object_path( root_object, dot_separated_path ) = std::move( element_value ); }
/// @brief object_type に対しドット区切りのパスで object_type の階層を辿り value_type を放り込む
static inline auto set_value
( object_type& root_object
, const std::string& dot_separated_path
, const value_type& element_value
)
{ detail::make_object_path( root_object, dot_separated_path ) = element_value; }
おまけ: ユーザーの期待する型で受け入れる機能に対応する方法
picojson::array
, picojson::object
, picojson::value
, std::string
, const char*
, bool
, double
に加えて std::uint8_t
, std::uint16_t
, std::uint32_t
, std::uint64_t
, std::int8_t
, std::int16_t
, std::int32_t
, std::int64_t
も set_value
へそのまま放り投げられると便利が良い。
また、 root_value
が picojson::object
を内包した picojson::value
にも対応した set_value
も使えるとしばしばこれも便利が良い。
/// @brief value_type へ可換な T 型の値に対応する set_value の syntax sugar ラッパー
template < typename T = null_type >
static inline auto set_value( object_type& root_object, const std::string& dot_separated_path, const T& element_value = T() )
{ set_value( root_object, dot_separated_path, value_type( static_cast< double >( element_value ) ) ); }
template < >
inline auto set_value< null_type >( object_type& root_object, const std::string& dot_separated_path, const null_type& )
{ set_value( root_object, dot_separated_path, value_type() ); }
template < >
inline auto set_value< array_type >( object_type& root_object, const std::string& dot_separated_path, const array_type& element_value )
{ set_value( root_object, dot_separated_path, value_type( element_value ) ); }
template < >
inline auto set_value< object_type >( object_type& root_object, const std::string& dot_separated_path, const object_type& element_value )
{ set_value( root_object, dot_separated_path, value_type( element_value ) ); }
template < >
inline auto set_value< value_type >( object_type& root_object, const std::string& dot_separated_path, const value_type& element_value )
{ set_value( root_object, dot_separated_path, element_value ); }
template < >
inline auto set_value< std::string >( object_type& root_object, const std::string& dot_separated_path, const std::string& element_value )
{ set_value( root_object, dot_separated_path, value_type( element_value ) ); }
static inline auto set_value( object_type& root_object, const std::string& dot_separated_path, const char* element_value )
{ set_value( root_object, dot_separated_path, value_type( element_value ) ); }
static inline auto set_value( object_type& root_object, const std::string& dot_separated_path, const bool element_value )
{ set_value( root_object, dot_separated_path, value_type( element_value ) ); }
/// root_value が object_type を内包する value_type の場合に対応する set_value の syntax sugar ラッパー
template < typename T = null_type >
static inline auto set_value( value_type& root_value, const std::string& dot_separated_path, T&& element_value = T() )
{ set_value( root_value.get< object_type >(), dot_separated_path, std::move( element_value ) ); }
ライブラリー実装例
使用例
# include <usagi/json/picojson/set_value.hxx>
# include <iostream>
auto main() -> int
{
using namespace std::literals::string_literals;
using namespace usagi::json::picojson;
object_type a, b;
a["hoge"] = value_type( 1.0 );
a["fuga"] = value_type( 2.0 );
value_type va( a );
set_value( va, "aa.bb.xx", 1.23 );
set_value( va, "aa.bb.yy", 1.23f );
set_value( va, "aa.bb.zz", 123ull );
set_value( va, "aa.bb.ww", true );
set_value( va, "aa.cc.ss", "xyz123" );
set_value( va, "aa.cc.tt", "xyz456"s );
set_value( va, "uu.vv" );
set_value( va, "array", array_type() );
set_value( va, "object", object_type() );
std::cout << va;
}
実行結果
{"aa":{"bb":{"ww":true,"xx":1.23,"yy":1.2300000190734863,"zz":123},"cc":{"ss":"xyz123","tt":"xyz456"}},"array":[],"fuga":2,"hoge":1,"object":{},"uu":{"vv":null}}
jq
など通して整形すると:
{
"aa": {
"bb": {
"ww": true,
"xx": 1.23,
"yy": 1.2300000190734863,
"zz": 123
},
"cc": {
"ss": "xyz123",
"tt": "xyz456"
}
},
"array": [],
"fuga": 2,
"hoge": 1,
"object": {},
"uu": {
"vv": null
}
}