C++
JSON
path
library
picojson

文字列のパスから picojson::object による階層構造に picojson::value を放り込む例

More than 1 year has passed since last update.


概要

先日、「読み込み」については 「picojson::value に入った picojson::object のネスト構造をドット区切りのパス文字列で引っ張り出すヘルパー」 †1 について書いた。

今回は「書き出し」についてコードの実装例を紹介したい。


必要な機能


  1. ドット区切りのパス文字列を階層ごとに分離する機能: boost::split (†1と同様)

  2. 分離されたパスの階層構造を組み立てる機能

  3. ユーザーの期待する型で受け入れる機能


実装詳細


基本: 分離されたパスの階層構造を組み立てる方法

    /// @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_tset_value へそのまま放り投げられると便利が良い。

また、 root_valuepicojson::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
}
}


References