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

# 概要

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

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

# 必要な機能

1. ドット区切りのパス文字列を階層ごとに分離する機能: boost::split （†1と同様）
2. 分離されたパスの階層構造を組み立てる機能
3. ユーザーの期待する型で受け入れる機能

# 実装詳細

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

```C++
    /// @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` も使えるとしばしばこれも便利が良い。

```C++
  /// @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 ) ); }
```

# ライブラリー実装例

- https://github.com/usagi/usagi/blob/master/include/usagi/json/picojson/set_value.hxx

## 使用例

```c++
#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;
}
```

### 実行結果

```json
{"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` など通して整形すると:

```json
{
  "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

- †1 : [Qiita - picojson::value に入った picojson::object のネスト構造をドット区切りのパス文字列で引っ張り出すヘルパー](http://qiita.com/usagi/items/da3568d8fa61e4aafede)