JSONとGo
JSON and Go by Andrew Gerrand
[2026年8月追記: Go 1.27で新しい encoding/json/v2 パッケージが導入されました。
Goで最新のJSONの扱い方については「チュートリアル: JSONを扱う」を参照してください。]
はじめに
JSON(JavaScript Object Notation)はシンプルなデータ交換フォーマットです。 構文的にはJavaScriptのオブジェクトやリストによく似ています。 Webのバックエンドとブラウザ上で動作するJavaScriptプログラムとの通信に最もよく使われますが、 それ以外にも様々な場面で使われています。 そのホームページである json.org には、この標準について実にわかりやすく簡潔な定義が載っています。
jsonパッケージ を使えば、GoプログラムからJSONデータを読み書きするのはたやすいことです。
エンコード
JSONデータをエンコードするには Marshal 関数を使います。
func Marshal(v interface{}) ([]byte, error)
次のGoのデータ構造 Message があるとします。
type Message struct {
Name string
Body string
Time int64
}
そして Message のインスタンスがあるとします。
m := Message{"Alice", "Hello", 1294706395881547000}
json.Marshal を使って m をJSONエンコードされた形式にマーシャルできます。
b, err := json.Marshal(m)
すべてがうまくいけば、err は nil になり、b は次のJSONデータを含む []byte になります。
b == []byte(`{"Name":"Alice","Body":"Hello","Time":1294706395881547000}`)
妥当なJSONとして表現できるデータ構造のみがエンコードされます。
JSONオブジェクトはキーとして文字列のみをサポートします。Goのmap型をエンコードするには、
map[string]T(Tはjsonパッケージがサポートする任意のGoの型)という形式でなければなりません。チャネル型、複素数型、関数型はエンコードできません。
循環データ構造はサポートされていません。循環データ構造を渡すと
Marshalは無限ループに陥ります。ポインタは、それが指す値としてエンコードされます(ポインタが
nilの場合はnullになります)。
jsonパッケージは構造体型のエクスポートされたフィールド(大文字で始まるフィールド)にのみアクセスします。 したがって、構造体のエクスポートされたフィールドのみがJSON出力に現れます。
デコード
JSONデータをデコードするには Unmarshal 関数を使います。
func Unmarshal(data []byte, v interface{}) error
まず、デコードされたデータを格納する場所を用意する必要があります。
var m Message
そして json.Unmarshal を呼び出し、JSONデータの []byte と m へのポインタを渡します。
err := json.Unmarshal(b, &m)
b に m に収まる妥当なJSONが含まれていれば、呼び出し後 err は nil になり、b のデータは
次のような代入がされたかのように構造体 m に格納されます。
m = Message{
Name: "Alice",
Body: "Hello",
Time: 1294706395881547000,
}
Unmarshal はデコードされたデータをどのフィールドに格納するかをどうやって特定しているのでしょうか?
与えられたJSONキー "Foo" に対して、Unmarshal は次の優先順位で格納先の構造体のフィールドを探します。
タグが
"Foo"であるエクスポートされたフィールド(構造体タグについては Go言語仕様 を参照してください)名前が
"Foo"であるエクスポートされたフィールド、あるいは名前が
"FOO"や"FoO"など、"Foo"と大文字小文字を区別せずにマッチするエクスポートされたフィールド
JSONデータの構造がGoの型と完全には一致しない場合はどうなるでしょうか?
b := []byte(`{"Name":"Bob","Food":"Pickle"}`)
var m Message
err := json.Unmarshal(b, &m)
Unmarshal は格納先の型の中に見つかったフィールドのみをデコードします。この場合、m の Name フィールドのみが
値で埋められ、Food フィールドは無視されます。この振る舞いは、大きなJSONの塊から特定の少数のフィールドだけを
取り出したいときに特に便利です。また、これは格納先の構造体の中にあるエクスポートされていないフィールドが
Unmarshal の影響を受けないことも意味します。
しかし、JSONデータの構造をあらかじめ知らない場合はどうすればよいでしょうか?
インターフェースを使った汎用的なJSON
interface{}(空のインターフェース)型は、メソッドを1つも持たないインターフェースを表します。
すべてのGoの型は少なくともゼロ個のメソッドを実装しているので、空のインターフェースを満たします。
空のインターフェースは汎用的なコンテナ型として機能します。
var i interface{}
i = "a string"
i = 2011
i = 2.777
型アサーションは、その裏にある具体的な型にアクセスします。
r := i.(float64)
fmt.Println("the circle's area", math.Pi*r*r)
あるいは、裏にある型がわからない場合は、型switchを使って型を判定します。
switch v := i.(type) {
case int:
fmt.Println("twice i is", v*2)
case float64:
fmt.Println("the reciprocal of i is", 1/v)
case string:
h := len(v) / 2
fmt.Println("i swapped by halves is", v[h:]+v[:h])
default:
// iは上記のどの型でもない
}
jsonパッケージは、任意のJSONオブジェクトと配列を格納するために map[string]interface{} と
[]interface{} の値を使います。妥当なJSONの塊であれば何でも、単なる interface{} の値に
難なくアンマーシャルできます。デフォルトで対応する具体的なGoの型は次のとおりです。
JSONの真偽値に対しては
bool、JSONの数値に対しては
float64、JSONの文字列に対しては
string、JSONのnullに対しては
nil。
任意のデータのデコード
変数 b に格納された次のJSONデータを考えてみましょう。
b := []byte(`{"Name":"Wednesday","Age":6,"Parents":["Gomez","Morticia"]}`)
このデータの構造を知らなくても、Unmarshal を使って interface{} の値にデコードできます。
var f interface{}
err := json.Unmarshal(b, &f)
この時点で、f の中のGoの値は、キーが文字列で、値自体が空のインターフェースの値として
格納されたマップになります。
f = map[string]interface{}{
"Name": "Wednesday",
"Age": 6,
"Parents": []interface{}{
"Gomez",
"Morticia",
},
}
このデータにアクセスするには、型アサーションを使って f の裏にある map[string]interface{} に
アクセスできます。
m := f.(map[string]interface{})
そして、range文でこのマップを反復処理し、型switchを使ってその値を具体的な型としてアクセスできます。
for k, v := range m {
switch vv := v.(type) {
case string:
fmt.Println(k, "is string", vv)
case float64:
fmt.Println(k, "is float64", vv)
case []interface{}:
fmt.Println(k, "is an array:")
for i, u := range vv {
fmt.Println(i, u)
}
default:
fmt.Println(k, "is of a type I don't know how to handle")
}
}
このようにして、型安全性の恩恵を受けながら未知のJSONデータを扱えます。
参照型
先ほどの例のデータを格納するGoの型を定義してみましょう。
type FamilyMember struct {
Name string
Age int
Parents []string
}
var m FamilyMember
err := json.Unmarshal(b, &m)
このデータを FamilyMember の値にアンマーシャルすると期待どおりに動作しますが、よく見てみると
注目すべきことが起きているのがわかります。var文で FamilyMember 構造体を確保し、その値へのポインタを
Unmarshal に渡していますが、そのとき Parents フィールドは nil のスライス値でした。Parents
フィールドに値を入れるために、Unmarshal は裏側で新しいスライスを確保していたのです。これは、
Unmarshal がサポートしている参照型(ポインタ、スライス、マップ)を扱う際の典型的な動作です。
次のデータ構造にアンマーシャルする場合を考えてみましょう。
type Foo struct {
Bar *Bar
}
もしJSONオブジェクトの中に Bar フィールドがあれば、Unmarshal は新しい Bar を確保し、値を
入れます。なければ、Bar は nil ポインタのままになります。
ここから便利なパターンが生まれます。いくつかの異なるメッセージ型を受け取るアプリケーションがある場合、 次のような「receiver」構造体を定義するとよいでしょう。
type IncomingMessage struct {
Cmd *Command
Msg *Message
}
送信側は、伝えたいメッセージの種類に応じて、トップレベルのJSONオブジェクトの Cmd フィールドや
Msg フィールドに値を入れられます。Unmarshal はJSONを IncomingMessage 構造体に
デコードする際、JSONデータの中に存在するデータ構造だけを確保します。どちらのメッセージを処理すべきかを
知るには、プログラマは単に Cmd か Msg のどちらかが nil でないかを調べるだけで済みます。
ストリーミングエンコーダーとデコーダー
jsonパッケージは、JSONデータのストリームを読み書きするというよくある操作をサポートするために
Decoder 型と Encoder 型を提供しています。NewDecoder 関数と NewEncoder 関数は、それぞれ
io.Reader インターフェース型と io.Writer
インターフェース型をラップします。
func NewDecoder(r io.Reader) *Decoder
func NewEncoder(w io.Writer) *Encoder
次は、標準入力から一連のJSONオブジェクトを読み込み、それぞれのオブジェクトから Name フィールド以外を
すべて削除し、そのオブジェクトを標準出力に書き出すサンプルプログラムです。
package main
import (
"encoding/json"
"log"
"os"
)
func main() {
dec := json.NewDecoder(os.Stdin)
enc := json.NewEncoder(os.Stdout)
for {
var v map[string]interface{}
if err := dec.Decode(&v); err != nil {
log.Println(err)
return
}
for k := range v {
if k != "Name" {
delete(v, k)
}
}
if err := enc.Encode(&v); err != nil {
log.Println(err)
}
}
}
ReaderとWriterはいたるところで使われているので、これらの Encoder 型と Decoder 型は、
HTTP接続やWebSocket、ファイルへの読み書きなど、幅広い場面で利用できます。
参考文献
詳しくは jsonパッケージのドキュメント を参照してください。 jsonの利用例については jsonrpcパッケージ のソースファイルを 参照してください。
By Andrew Gerrand