1
0

More than 3 years have passed since last update.

goでyamlを直感的にお手軽に操作できるIF「go-rough-yaml」

Last updated at Posted at 2019-12-08

概要

golangでyamlをラフに扱うパッケージを作りました。

xshoji/go-rough-yaml: go-rough-yaml provide accessor methods to edit yaml.
https://github.com/xshoji/go-rough-yaml

背景とかいいから使いっぷり教えてよ!って方は使い方までどうぞ

背景

yamlをgolangで参照、加工しようとした際、お手軽にyamlのデータを扱えるパッケージが存在しないことに気づきました。
ここでいう「お手軽」とは、例えば特定のキーを参照、削除、あるいは書き換えたりするだけの操作を指します。
goでyamlを扱うためには

go-yaml/yaml: YAML support for the Go language.
https://github.com/go-yaml/yaml

を使うのが一般的です。yamlの内容を構造体にUnmarshalして利用しますが、構造体を定義するのが面倒です。
このとき構造体を定義せずとも、map[interface{}]interface{}という最強の型なしmapにUnmarshalすることで、
ある程度お手軽にyamlのデータを参照、加工できるのですが1点大きな問題があります。それは、

  • yamlに戻したときにmap構造のキー順が保証されない(勝手にソートされる)

ということです。

bbb:
  c: 1
  b: 2
aaa:
  f: 3
  e: 4

というyamlをmapにUnmarshalして、そのままMarshalでyamlに戻すと

aaa:
  e: 4
  f: 3
bbb:
  b: 2
  c: 1

という感じで勝手にキーでソートされてしまいます。これは嫌です。
なぜなら、見やすいように意図的に整理しているyaml内のデータが勝手にソートされたら困りますし、
加工せずMarshalしたyamlであっても一切変更がないのに差分だらけになります。
この問題に対して、yaml.MapSliceという便利な構造体が用意されていて、これは上述の課題を解決するものです。
この構造体を使うことで、map構造の順序を保持したままyamlを加工できます。
が、mapではなくsliceとしてデータを扱うため、mapのキーバリュー構造のように簡単に目的の値にアクセスできません。
また、値の加工もかなり面倒です。

そこで、このyaml.MapSliceの特性(map構造の順序を保持する)を活かしつつ、
キーバリュー構造のようにアクセスできるIFを備えたパッケージを用意しました。

xshoji/go-rough-yaml: go-rough-yaml provide accessor methods to edit yaml.
https://github.com/xshoji/go-rough-yaml

特徴

go-rough-yamlの特徴は以下の3つです。

  • シンプルなインターフェース
  • スキーマレス
  • map構造の順序の保持

以下に使い方と合わせて特徴を詳しく説明します。

使い方

1行(yamlは複数行ですが...)でyamlを読み込めます。

roughYaml := goroughyaml.FromYaml(`
ddd:
  ccc:
    c: value-c
  bbb:
  - 10
aaa:
  zzz: value-zzz
`)

入れ子構造の値の有無を意識せず、メソッドチェーンで目的の値を参照、更新できます。

// get value
roughYaml.
Get("ddd").
  Get("ccc").
    Get("c").Value() // => value-c
roughYaml.
Get("xxx").
  Get("xxx").Value() // => nil

// set value
roughYaml.
  Get("aaa").
    Set("zzz", nil)
roughYaml.
  Get("aaa").
    Get("zzz").Value()) // -> nil

// delete key
roughYaml.Delete("aaa")
roughYaml.Get("aaa").Value()) // -> nil

このように、メソッドチェーンでゆるくyamlを扱えます。つまり、構造体の定義が不要(=スキーマレス)です。
yamlへ戻す場合も1行で済みます。かつ、map構造の順序が保持されます。

// print as yaml
/**
ddd:
  ccc:
    c: value-c
  bbb:
  - 10
 */
roughYaml.ToYaml()

まとめ

go-rough-yamlの実装にあたって、以下の3点についての理解が浅かったため、苦労しました。

  • ポインタ
  • interface{}
  • nil

golangであるあるなトピックなので今更、という感じですが、今回実際に苦しめられてかなり学びがありました。
特にinterface{}nilの話は、過去に他の方がよくまとめてくれていたのでそれらの情報にとても助けられました。先人の知識はとても大切です。
基本的な操作しか実装、テストしていないので、バグや必要な機能がもしあればIssueでご連絡いただければ対応します。
golangでyamlをゆるく取り扱い方に少しでも貢献できていたら嬉しいです。もしかしたらjson版も作るかもです。

1
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
1
0