はじめに
JSON (JavaScript Object Notation) は、シンプルなデータ交換フォーマットです。構文的には、JavaScript のオブジェクトやリストに似ています。主にウェブバックエンドとブラウザで実行される 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}
m の JSON エンコード版を json.Marshal
を使用してマシャルできます:
b, err := json.Marshal(m)
すべてが正常であれば、err
は nil
になり、b
はこの JSON データを含む []byte
になります:
b == []byte(`{"Name":"Alice","Body":"Hello","Time":1294706395881547000}`)
有効な JSON として表現できるデータ構造のみがエンコードされます:
- JSON オブジェクトは、キーとして文字列のみをサポートします。Go のマップ型をエンコードするには、
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 spec を参照)、"Foo"
という名前のエクスポートされたフィールド、または"FOO"
または"FoO"
という名前のエクスポートされたフィールド、または"Foo"
のケースインセンシティブな一致のいずれか。
JSON データの構造が Go 型と正確に一致しない場合はどうなりますか?
b := []byte(`{"Name":"Bob","Food":"Pickle"}`)
var m Message
err := json.Unmarshal(b, &m)
Unmarshal
は、宛先型で見つけられるフィールドのみをデコードします。この場合、m の Name フィールドのみが populated され、Food フィールドは無視されます。この動作は、大きな JSON ブロブから特定のフィールドのみを選択したい場合に特に便利です。また、宛先構造体の未エクスポートフィールドは Unmarshal
によって影響を受けません。
しかし、事前に JSON データの構造がわからない場合はどうなりますか?
インターフェースによる汎用 JSON
interface{}
(空のインターフェース)型は、メソッドがゼロのインターフェースを説明します。すべての 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 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 isn't one of the types above
}
json パッケージは、map[string]interface{}
および []interface{}
の値を使用して任意の JSON オブジェクトや配列を格納します。有効な JSON ブロブをプレーンな interface{}
値にデシリアライズすることができます。デフォルトの具体的な Go 型は次のとおりです:
任意のデータのデコーディング
この JSON データを考えてみましょう。変数 b
に格納されています:
b := []byte(`{"Name":"Wednesday","Age":6,"Parents":["Gomez","Morticia"]}`)
このデータの構造を知らなくても、interface{}
値に Unmarshal
を使用してデコードできます:
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{})
次に、範囲文を使用してマップを反復処理し、型スイッチを使用してその値に具体的な型としてアクセスできます:
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
フィールドを populated するために、Unmarshal
は裏で新しいスライスを割り当てました。これは、Unmarshal
がサポートされている参照型(ポインタ、スライス、マップ)でどのように機能するかの典型的な例です。
このデータ構造にアンマーシャルすることを考えてみましょう:
type Foo struct {
Bar *Bar
}
JSON オブジェクトに Bar
フィールドがある場合、Unmarshal
は新しい Bar
を割り当てて populated します。そうでない場合、Bar
は nil
ポインタのままになります。
ここから有用なパターンが生まれます:いくつかの異なるメッセージ型を受信するアプリケーションがある場合、次のような「受信者」構造体を定義することができます。
type IncomingMessage struct {
Cmd *Command
Msg *Message
}
送信者は、伝えたいメッセージの型に応じて、トップレベルの JSON オブジェクトの Cmd
フィールドおよび/または Msg
フィールドを populated できます。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 パッケージ のソースファイルを参照してください。