🙄

Go言語で手軽にjsonの項目をパースする

2021/08/01に公開

Go言語の標準ライブラリでjsonを手軽にパースする方法がよくわからなかったので調べた結果のまとめ。

標準ライブラリによるjsonパース

標準ライブラリの encoding/json を用いてjsonをパースしようとすると構造体の定義が必要になる。これが非常に面倒。
json全体を利用するのであれば構造体を定義して操作するのは問題ないが、特定の1項目だけ取得したい場合などわざわざ構造体を定義したくない。
もちろん、 json-to-go を利用すれば、jsonから自動で構造体定義を出力するので作成の手間はないし、
不要な項目は省略しても問題ないのでシンプルに保つことはできるが、できれば構造体定義なしにjsonから値を取得したい。

標準ライブラリでjsonパース
type Data struct {
	Depth1 struct {
		Depth2 struct {
			Depth3 struct {
				Key string
			}
		}
	}
}


func main() {
    var jsonStr =`
{
  "version": "1.0",
  "depth1": {
    "depth2": {
      "depth3": {
        "key": "pure-go-value"
      }
    }
  }
}`

	var data Data
	json.Unmarshal([]byte(jsonStr), &data)
	fmt.Println(data.Depth1.Depth2.Depth3.Key)
}
標準ライブラリ版の実行結果
$ go run pure-go.go
pure-go-value

サードパーティライブラリを利用する

標準ライブラリでは解決できないのでサードパーティライブラリを利用して解決する。
調べてみたらいろいろあった。

antonholmquist/jason

antonholmquist/jasonは他に自分の用途に合ったライブラリがないか調べて一番最初に見付けたやつ。
これでも標準ライブラリに比べて十分シンプルなのでよいかなと思ったが、
追加できちんと調べてみたら実はもっとよさそうなのがたくさんあった(後述)。

jason
func main() {
	var jsonStr =`
{
  "version": "1.0",
  "depth1": {
    "depth2": {
      "depth3": {
        "key": "jason-value"
      }
    }
  }
}`

	v, err := jason.NewObjectFromBytes([]byte(jsonStr))
	if err != nil {
		fmt.Println(os.Stderr, err)
		os.Exit(1)
	}

	key, err := v.GetString("depth1", "depth2", "depth3", "key")
	if err != nil {
		fmt.Println(os.Stderr, err)
		os.Exit(1)
	}
	fmt.Println(key)
}

buger/jsonparser

buger/jsonparser は自分がやりたいことがまさにREADMEに記載されている。

It does not require you to know the structure of the payload (eg. create structs), and allows accessing fields by providing the path to them. I

利用方法も antonholmquist/jason に比べてよりシンプルでよさそう。

jsonparser
func main() {
	var jsonStr =`
{
  "version": "1.0",
  "depth1": {
    "depth2": {
      "depth3": {
        "key": "jsonparser-value"
      }
    }
  }
}`

	key, err := jsonparser.GetString([]byte(jsonStr), "depth1", "depth2", "depth3", "key")
	if err != nil {
		fmt.Println(os.Stderr, err)
		os.Exit(1)
	}
	fmt.Println(key)
}

tidwall/gjson

tidwall/gjsonも似たようなライブラリだが、こちらはドットでパス表現できる。
jsonキーにドットを含む場合はバックスラッシュでエスケープすればよい
こちらの方がシンプルで使いやすいだろうか。
READMEを見る限りでは gjson の方が速くて高機能。
また、スター数や利用者数も多い。

gjson
func main() {
	var jsonStr =
`{
   "version": "1.0",
   "depth1": {
     "depth2": {
       "depth3": {
         "key": "gjson-value"
         "test.data": "dot-value"
       }
     }
   }
 }`

	key := gjson.Get(jsonStr, "depth1.depth2.depth3.key").String()
	dot := gjson.Get(jsonStr, "depth1.depth2.depth3.key.test\\.data").String()
	fmt.Println(key)
	fmt.Println(dot)
}

まとめ

gjsonがよさそう

Discussion