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)

すべてがうまくいけば、errnil になり、b は次のJSONデータを含む []byte になります。

b == []byte(`{"Name":"Alice","Body":"Hello","Time":1294706395881547000}`)

妥当なJSONとして表現できるデータ構造のみがエンコードされます。

  • JSONオブジェクトはキーとして文字列のみをサポートします。Goのmap型をエンコードするには、 map[string]TT はjsonパッケージがサポートする任意のGoの型)という形式でなければなりません。

  • チャネル型、複素数型、関数型はエンコードできません。

  • 循環データ構造はサポートされていません。循環データ構造を渡すと Marshal は無限ループに陥ります。

  • ポインタは、それが指す値としてエンコードされます(ポインタが nil の場合は null になります)。

jsonパッケージは構造体型のエクスポートされたフィールド(大文字で始まるフィールド)にのみアクセスします。 したがって、構造体のエクスポートされたフィールドのみがJSON出力に現れます。

デコード

JSONデータをデコードするには Unmarshal 関数を使います。

func Unmarshal(data []byte, v interface{}) error

まず、デコードされたデータを格納する場所を用意する必要があります。

var m Message

そして json.Unmarshal を呼び出し、JSONデータの []bytem へのポインタを渡します。

err := json.Unmarshal(b, &m)

bm に収まる妥当なJSONが含まれていれば、呼び出し後 errnil になり、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 は格納先の型の中に見つかったフィールドのみをデコードします。この場合、mName フィールドのみが 値で埋められ、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 を確保し、値を 入れます。なければ、Barnil ポインタのままになります。

ここから便利なパターンが生まれます。いくつかの異なるメッセージ型を受け取るアプリケーションがある場合、 次のような「receiver」構造体を定義するとよいでしょう。

type IncomingMessage struct {
    Cmd *Command
    Msg *Message
}

送信側は、伝えたいメッセージの種類に応じて、トップレベルのJSONオブジェクトの Cmd フィールドや Msg フィールドに値を入れられます。Unmarshal はJSONを IncomingMessage 構造体に デコードする際、JSONデータの中に存在するデータ構造だけを確保します。どちらのメッセージを処理すべきかを 知るには、プログラマは単に CmdMsg のどちらかが 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