LoginSignup
0
0

More than 5 years have passed since last update.

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

Posted at

概要

先日、「読み込み」については 「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

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0