⚠️注意
この問題は2016年に修正されたらしいです。Goのバージョンが古かなければ気にしなくて良いです。
元記事
Playground: http://play.golang.org/p/DvuH_KondU
生JSON扱うのに便利な型、json.RawMessage
。よくお世話になるが、ポインター使わないことによってハマることがあります。
調査
NG: json.RawMessage
[]byte
として認識され、Base64エンコードされてしまいます。
import (
"encoding/json"
"os"
)
func main() {
raw := json.RawMessage(`{"hello": "world"}`) // 生JSON
out := json.NewEncoder(os.Stdout)
// NG: そのまま渡して
out.Encode(raw) // "eyJoZWxsbyI6ICJ3b3JsZCJ9"
}
OK: *json.RawMessage
しかし、ポインターを渡すと想像通りに動きます。
// OK: ポインター
out.Encode(&raw) // {"hello":"world"}
OK: []json.RawMessage
sliceはポインターを渡さなくても大丈夫
// OK: slice
slice := []json.RawMessage{raw}
out.Encode(slice)
NG: map[string]interface{}
の中にjson.RawMessage
mapはポインターが必要です。
// map[string]interface needs a pointer
msi := map[string]interface{}{
"NoPointer": raw, // NG
"WithPointer": &raw, // OK
}
out.Encode(&msi)
// {"NoPointer":"eyJoZWxsbyI6ICJ3b3JsZCJ9","WithPointer":{"hello":"world"}}
NG: map[string]json.RawMessage
完全にアウトだ。
whoops := map[string]json.RawMessage{
"NoPointer": raw,
}
out.Encode(&whoops)
// {"NoPointer":"eyJoZWxsbyI6ICJ3b3JsZCJ9"}
OK: map[string]*json.RawMessage
msgmap := map[string]*json.RawMessage{
"WithPointer": &raw,
}
out.Encode(msgmap)
// {"WithPointer":{"hello":"world"}}
OK: ポインターで渡すstruct
obj := struct {
NoPointer json.RawMessage
WithPointer *json.RawMessage
}{raw, &raw}
out.Encode(&obj)
// {"NoPointer":{"hello":"world"},"WithPointer":{"hello":"world"}}
// これはNG:
out.Encode(obj)
// {"NoPointer":"eyJoZWxsbyI6ICJ3b3JsZCJ9","WithPointer":{"hello":"world"}}
結論
json.RawMessage
を使うときは要注意です。特にinterface{}
として使ってはダメですよ。*json.RawMessage
ならうまくいきます。