> Source: https://www.ymotongpoo.com/works/goblog-ja/json/


# JSONとGo

[JSON and Go](https://go.dev/blog/json) by Andrew Gerrand

[*2026年8月追記: Go 1.27で新しい `encoding/json/v2` パッケージが導入されました。
Goで最新のJSONの扱い方については「[チュートリアル: JSONを扱う](https://go.dev/doc/tutorial/json)」を参照してください。*]

## はじめに

JSON（JavaScript Object Notation）はシンプルなデータ交換フォーマットです。
構文的にはJavaScriptのオブジェクトやリストによく似ています。
Webのバックエンドとブラウザ上で動作するJavaScriptプログラムとの通信に最もよく使われますが、
それ以外にも様々な場面で使われています。
そのホームページである [json.org](http://json.org) には、この標準について実にわかりやすく簡潔な定義が載っています。

[jsonパッケージ](https://go.dev/pkg/encoding/json/) を使えば、GoプログラムからJSONデータを読み書きするのはたやすいことです。

## エンコード

JSONデータをエンコードするには [Marshal](https://go.dev/pkg/encoding/json/#Marshal) 関数を使います。

```go
func Marshal(v interface{}) ([]byte, error)
```

次のGoのデータ構造 `Message` があるとします。

```go
type Message struct {
    Name string
    Body string
    Time int64
}
```

そして `Message` のインスタンスがあるとします。

```go
m := Message{"Alice", "Hello", 1294706395881547000}
```

`json.Marshal` を使って `m` をJSONエンコードされた形式にマーシャルできます。

```go
b, err := json.Marshal(m)
```

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

```go
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](https://go.dev/pkg/encoding/json/#Unmarshal) 関数を使います。

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

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

```go
var m Message
```

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

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

`b` に `m` に収まる妥当なJSONが含まれていれば、呼び出し後 `err` は `nil` になり、`b` のデータは
次のような代入がされたかのように構造体 `m` に格納されます。

```go
m = Message{
    Name: "Alice",
    Body: "Hello",
    Time: 1294706395881547000,
}
```

`Unmarshal` はデコードされたデータをどのフィールドに格納するかをどうやって特定しているのでしょうか？
与えられたJSONキー `"Foo"` に対して、`Unmarshal` は次の優先順位で格納先の構造体のフィールドを探します。

* タグが `"Foo"` であるエクスポートされたフィールド（構造体タグについては
  [Go言語仕様](https://go.dev/ref/spec#Struct_types) を参照してください）

* 名前が `"Foo"` であるエクスポートされたフィールド、あるいは

* 名前が `"FOO"` や `"FoO"` など、`"Foo"` と大文字小文字を区別せずにマッチするエクスポートされたフィールド

JSONデータの構造がGoの型と完全には一致しない場合はどうなるでしょうか？

```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の型は少なくともゼロ個のメソッドを実装しているので、空のインターフェースを満たします。

空のインターフェースは汎用的なコンテナ型として機能します。

```go
var i interface{}
i = "a string"
i = 2011
i = 2.777
```

型アサーションは、その裏にある具体的な型にアクセスします。

```go
r := i.(float64)
fmt.Println("the circle's area", math.Pi*r*r)
```

あるいは、裏にある型がわからない場合は、型switchを使って型を判定します。

```go
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データを考えてみましょう。

```go
b := []byte(`{"Name":"Wednesday","Age":6,"Parents":["Gomez","Morticia"]}`)
```

このデータの構造を知らなくても、`Unmarshal` を使って `interface{}` の値にデコードできます。

```go
var f interface{}
err := json.Unmarshal(b, &f)
```

この時点で、`f` の中のGoの値は、キーが文字列で、値自体が空のインターフェースの値として
格納されたマップになります。

```go
f = map[string]interface{}{
    "Name": "Wednesday",
    "Age":  6,
    "Parents": []interface{}{
        "Gomez",
        "Morticia",
    },
}
```

このデータにアクセスするには、型アサーションを使って `f` の裏にある `map[string]interface{}` に
アクセスできます。

```go
m := f.(map[string]interface{})
```

そして、range文でこのマップを反復処理し、型switchを使ってその値を具体的な型としてアクセスできます。

```go
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の型を定義してみましょう。

```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` がサポートしている参照型（ポインタ、スライス、マップ）を扱う際の典型的な動作です。

次のデータ構造にアンマーシャルする場合を考えてみましょう。

```go
type Foo struct {
    Bar *Bar
}
```

もしJSONオブジェクトの中に `Bar` フィールドがあれば、`Unmarshal` は新しい `Bar` を確保し、値を
入れます。なければ、`Bar` は `nil` ポインタのままになります。

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

```go
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](https://go.dev/pkg/io/#Reader) インターフェース型と [io.Writer](https://go.dev/pkg/io/#Writer)
インターフェース型をラップします。

```go
func NewDecoder(r io.Reader) *Decoder
func NewEncoder(w io.Writer) *Encoder
```

次は、標準入力から一連のJSONオブジェクトを読み込み、それぞれのオブジェクトから `Name` フィールド以外を
すべて削除し、そのオブジェクトを標準出力に書き出すサンプルプログラムです。

```go
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パッケージのドキュメント](https://go.dev/pkg/encoding/json/) を参照してください。
jsonの利用例については [jsonrpcパッケージ](https://go.dev/pkg/net/rpc/jsonrpc/) のソースファイルを
参照してください。

By Andrew Gerrand

