最近はスクリプト言語を使う時に何を使うのが良いか迷います。
いや、もともと大して選んでいないんですが、Azure 関連の操作がメインだったので PowerShell をよく使っています。
PowerShell は C#っぽく書けるので悪くないんですが、改めて、OS 関係なく軽快に動いてくれる言語として go に注目しています。
プログラミングのリハビリを兼ねて go で Grafana を扱うコードを書いていたんですが、json を扱うところで苦労したのでノウハウをメモしておこうと思います。
encoding/json
を使用する場合のお話になります。
1. 礼儀正しく扱う
json の構造をすべて把握している場合は、その構造を表現した type を宣言し、json.Unmarshal
で変換された値を突っ込むことができます。
一部定義するだけでも良いようです。
例えば Grafana で GET /api/search/
を実行した結果の json を処理したい場合を考えます。
https://grafana.com/docs/grafana/latest/http_api/folder_dashboard_search/
得られる json には、id、uid、title、・・・といくつかの値が得られますが、例えばその中で title と uid の値だけ拾いたいときは下記のような感じです。
package main
import (
"encoding/json"
"fmt"
"log"
)
type DashboardOverview struct {
Title string `json:"title"`
Uid string `json:"uid"`
}
func main() {
dashboardJson := 【Grafanaからjsonを取得する処理】
var dashboards []DashboardOverview
if err := json.Unmarshal(dashboardJson, &dashboards); err != nil {
log.Fatal(err)
}
fmt.Println(dashboards)
}
こうすると、変数 dashboards にダッシュボードの title と uid が配列で格納されます。
2. 子要素を文字列として扱う
json のある要素以下の内容をごっそり文字列で取り出したいときは、json.RawMessage
という型を使えばよいみたいです。
例えば Grafana で GET /api/dashboards/uid/:uid
を実行した結果の json を処理したい場合を考えます。
https://grafana.com/docs/grafana/latest/http_api/dashboard/#get-dashboard-by-uid
得られるデータの中に大きく dashboard
と metadata
があります。
dashboard
の中の json だけ抽出したいときは下記のような感じです。
package main
import (
"encoding/json"
"fmt"
"log"
)
type DashboardDetail struct {
Meta json.RawMessage `json:"meta"`
Dashboard json.RawMessage `json:"dashboard"`
}
func main() {
detailJson := 【Grafanaからjsonを取得する処理】
var detail DashboardDetail
if err := json.Unmarshal(detailJson, &detail); err != nil {
log.Fatal(err)
}
fmt.Println(string(detail.Dashboard))
}
こうすると、detail.Dashboard
に dashboard
要素の内容が入りますので、string に変換するなどして扱います。
3. ある特定の値だけピンポイントに操作する
ある特定の値だけ扱いたいときは、interface{}
という箱に入れてしまえばよいみたいです。
型アサーションを使用すれば参照、更新できました。
例えばエクスポートした Grafana のダッシュボードの json のインポートを自動化することを考えます。対応する API は POST /api/dashboards/db
ですが、これはダッシュボード定義の id
の値が null なら Create、値が入っていたら Update となります。
https://grafana.com/docs/grafana/latest/http_api/dashboard/#create-update-dashboard
エクスポートした json には id
が入った状態なので、新規環境にダッシュボードを追加する場合は何らかの方法で id
を null にする必要があります。
id
の定義は深い階層にもあるので、正規表現での置換は難しそうです。
この場合は json.Unmarshal
して、一番浅い id
の値を更新する処理を書くことになります。
下記のような感じです。
package main
import (
"encoding/json"
"fmt"
"log"
)
func main() {
detailJson := 【Grafanaからjsonを取得する処理】
var jsonObj interface{}
err := json.Unmarshal(detailJson, &jsonObj)
if err != nil {
log.Fatal(err)
}
jsonObj.(map[string]interface{})["id"] = nil
jsonByteArray, _ := json.Marshal(jsonObj)
fmt.Println(string(jsonByteArray))
}
これで id
を null にできたので、API でダッシュボードを作成することができます。
値の更新が終わったら、json.Marshal
をすれば byte 配列にすることができますよ。
まとめ
これだけ押さえておけば json を自在に扱える気になってきました。
ちなみに標準ライブラリ以外のものを使えばもっと楽なようなので、制約がない場合はいろいろ調べてみると良いと思いました。