LoginSignup
13
6

More than 5 years have passed since last update.

golangでjsonを編集するの作った

Last updated at Posted at 2017-02-27

バージョンアップしました。
名前が変わってます。filebase -> jsonbase
http://qiita.com/intelf___/items/67099042ec7883ca21ef

URL

https://github.com/intelfike/filebase
練習の為、無駄に英語を使ってます。
メソッドの用途別一覧が載ってるので、ぜひ活用してください。

どういうものか

Firebase RealtimeDatabaseというjson形式のデータベースサービスを基に作ろうとして、結局大幅に変更することになった自作パッケージです。
jsonをいろいろ操作することができます。
firebase ファイアーベース
filebase ファイルベース
の違いに注意。

なぜ作った?

[golang json 編集]みたいに検索してもうまくヒットしない。
英語ならあった。
https://github.com/Jeffail/gabs
けど、Mapの入れ子などがちょっと面倒そう

できること

・jsonのデータの取り出し、型変換
・jsonデータの生成
・jsonのデータの編集
・jsonのデータの削除
・jsonのデータの読み取り、書き出し
・gzipファイル(拡張子.gz)対応

使い方

構造体を取得

次の関数を最も良く使うでしょう。

fb := filebase.MustNew(`{"key":"value"}`)

ですが、ファイルやio.Readerから直接読み取るものが高速です。

fb := filebase.NewByFile("data.json")
fb := filebase.NewByReader(os.Stdin)

値を取得

Child()/ChildPath()などのメソッドで値を参照できます。

fmt.Println(fb.Child("key").ToString()) // [value]

To型名()というメソッドを使用すると、特定の型に変換できます。
fmt.Stringerを実装しているので、変換せずに書くと

fmt.Println(fb.Child("key")) // ["value"]
// もしくは
fmt.Println(fb.Child("key").String()) // ["value"]

string型を表す""に囲まれて表示されます。
この表示では、jsonを整形して見やすい形で表示してくれます。

整形して表示するときにインデントを追加

fb.Indent = " "
これで、fmt.Println(fb)のように整形して出力するときにインデントが追加されます。
出力

{
  "key":"vakue"
}

値を書き換え

fb.Child("key").Set(1)

出力

{
  "key": 1
}

値を追加

fb.Child("key").Push(1)
fb.Child("key").Push(2)

出力

{
  "key": [
    1,
    2
  ]
}

キーや要素を削除

fb.Child("key").Remove() // keyを削除
// もしくは
fb.Empty() // すべて削除して空にする

出力

{}

Mapを作成

fb.Child("key", "a", "b").Set(1)
// もしくは
fb.ChildPath("key/a/b").Set(1)

出力

{
  "key": {
    "a": {
      "b": 1
    }
  }
}

Child()/ChildPath()を使って存在しないキーを指定して、Set()/Push()してください。
※すでにMap以外の値がある場合はMapで上書きし、Mapの場合は要素を追加します。

Arrayを作成

Mapと同じように
fb.Child("a", 1).Set(1)
のようにすることはできません。
なぜならば、巨大な配列を簡単に作れてしまうからです。
fb.Child("a", 10000).Set(1)
としたときに、サイズ1万の配列が作られてしまいますし、そのほとんどが空です。
(これをすると、JSONのノードがArrayの場合はerrorが返り、Mapの場合は"a", "10000"と解釈され、Mapの要素が生成/変更されます。)

fb.Child("a").Push(1)

とすることで、Arrayを初期化、または要素を追加してくれます。
※すでにArray以外の値がある場合は上書きし、Arrayの場合は要素を追加します。

jsonを整形したくない

fb.Indentを設定しなければ整形されません。

fb.Indent = ""
fmt.Println(fb)

ディレクトリをjsonにしたい

例えばこういうディレクトリ構成
./main.go
./lib/module.go
./lib/reg.go
./empty/

これをjsonにしたい時、このパッケージでは

fdlist := []string{
    "./main.go",
    "./lib/module.go",
    "./lib/reg.go",
    "./empty/",
}

fb := filebase.MustNew("{}")
fb.Indent = "  "
for _, v := range fdlist {
    d, f := filepath.Split(v) // ディレクトリ・ファイルの分割
    d = strings.Trim(d, "/") // 余分なスラッシュを削除
    fb.ChildPath(d).Push(f) // 追加
}
fmt.Println(fb)

こう書きます。
出力

{
  ".": {
    "empty": [
      ""
    ],
    "lib": [
      "module.go",
      "reg.go"
    ]
  }
}

ディレクトリ名をキーに、ファイル名を値にしました。

こういった複雑な操作も簡単にできるように工夫をしております。

文字列ベースで操作がしたい

文字列ばかりを使ってjsonを生成、編集することもできます。パフォーマンスは良くないでしょう。

fb := filebase.MustNew(`{"key":"value"}`)
fb.Indent = "  "
fb.Child("key").SetStr(`{"key2":"value2"}`)
fb.Child("key", "key2").SetPrint(`{"`, 130, `":"eee"}`)

n := 300
fb.Child("key", "key2", "130").SetPrintf(`{"%d":"%b"}`, n, n)

出力

{
  "key": {
    "key2": {
      "130": {
        "300": "100101100"
      }
    }
  }
}

mapの要素の追加はこのようにもできますが、デコードをしないのでChild()/ChildPath()を推奨します。

panicを回避したい

設計上panicはほとんど発生しません。
ToBool()などで変換できない型を指定するなどでpanic()が発生します。
ですが、これらはChecker func(冒頭URL参照)で回避できます。

fb := filebase.MustNew(`[{"key":"value"}, {"key2":"value2"}]`)
fb.Indent = "  "
if fb.IsBool(){ // 値がboolに変換できるかチェック
  fmt.Println(fb.ToBool()) // 実行されない
}

出力なし

テンプレートを使いたい

テンプレートを作るためのメソッドとして、参照を切り離すClone()や、参照を切り離して値をセットするSetFB()/PushFB()があります。

b := filebase.MustNew(`{"top":null}`)
fb.Indent = "  "
template := filebase.MustNew(`{"key":"value", "id":0}`)
for n := 0; n < 3; n++ {
    fb.Child("top").PushFB(template) // テンプレートを追加
    fb.Child("top", n, "id").Set(n + 1) // 新しく追加したテンプレートのidを書き換え
}

出力

{
  "top": [
    {
      "id": 1,
      "key": "value"
    },
    {
      "id": 2,
      "key": "value"
    },
    {
      "id": 3,
      "key": "value"
    }
  ]
}

テンプレートを使うことで、毎回SetStr()などでstringをjsonデータに変換するよりは高速になります。
とはいえ、小規模なプログラムであればあまり気にする速度差ではありません。

その他よく使うメソッド

fb.Exists() Child()などで参照した値が存在するか確かめます。
fb.IsNull() 値がnullであるか確かめます。値が存在しない場合はfalseです。
fb.Each(func(f *Filebase){/*書きたい処理*/}) ArrayでもMapでも同じようにループできます。ただArrayの要素番号やMapのキーは取得できないので、取得したい場合はfb.ToArray()fb.ToMap()を使いましょう。
fb.WriteTo()/fb.WriteToFile() 高速にデータを書き出すことができます。Fileの方は拡張子で.gzを指定するとgzip圧縮します。(NewByFile()も同様)

管理

個人で管理しているので、こうしたほうが良い、こんな不具合があるなどお知らせ頂ければ直ちに修正、コミットできます。
逆に、簡単に破壊的変更が簡単にできちゃう。けど、go getでは更新されないから多分大丈夫。

変更履歴などはこちら

(記事公開時var1.0)
https://github.com/intelfike/filebase/blob/master/change_history.txt
ver1.1は機能の追加・制限の緩和が主体なので、1.0と変わらない使用ができます。
ver1.2も同様です。Mapからの値取得のバグ修正、機能の追加をしました。変わらず使用可能です。

13
6
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
13
6