LoginSignup
9
10

More than 3 years have passed since last update.

json.RawMessageはいつポインターを使うべき?

Last updated at Posted at 2015-01-05

⚠️注意

この問題は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ならうまくいきます。

9
10
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
10