【Go】jsonパッケージのMarshalとUnmarshalはなんとなくわかったが、Encode, Decodeって何?

普段のコードでJSON関係の変換をする際にはjsonパッケージのMarshlとUnmarshalを使っているのですが、jsonパッケージの中に同じようにJSONを変換するEncoder型のメソッドEncode及びDecoder型のメソッドDecodeなるものを見つけ、いったいMarhsalとUnmarshalと何が違うのかと疑問に思い、調べてみました。
まずMarhsalとUnmarshalの使い方
以下はMarhsalとUnmarshalの利用例です。
b := []byte(`{"Name":"Alice","Age":6,"Parents":["Bob","Chris"]}`)
var m map[string]any
if err := json.Unmarshal(b, &m); err != nil {
fmt.Println("error")
}
fmt.Println(m) // map[Age:6 Name:Alice Parents:[Bob Chris]]
bb, err := json.Marshal(m)
if err != nil {
fmt.Println("error")
}
fmt.Println(string(bb)) // {"Age":6,"Name":"Alice","Parents":["Bob","Chris"]}
上のコードではJSON形式なデータのバイトスライスをマップに変換し、再度JSON形式のバイトスライスのデータに変換しています。
EncodeとDecodeで同じ処理を書いてみる
EncodeとDecodeを使って上と同じ処理を実装してみます。
b := []byte(`{"Name":"Alice","Age":6,"Parents":["Bob","Chris"]}`)
buf := bytes.NewReader(b)
dec := json.NewDecoder(buf)
var m map[string]any
if err := dec.Decode(&m); err != nil {
fmt.Println("error")
}
fmt.Println(m) // map[Age:6 Name:Alice Parents:[Bob Chris]]
var builder strings.Builder
enc := json.NewEncoder(&builder)
if err := enc.Encode(m); err != nil {
fmt.Println("error")
}
fmt.Println(builder.String()) // {"Age":6,"Name":"Alice","Parents":["Bob","Chris"]}
Decoderにio.Readerインターフェイスを満たす型を渡してDecodeをし、Encoderにはio.Writerインターフェイスを満たす型を渡してEncodeをしています。余計な処理が増えて先ほどのMarshalとUnmarshalを使って書いたコードよりわかりづらくなったように思います。こういった処理には素直にMarshalとUnmarshalを使った方が良さそうです。
それではDecode及びEncodeの使いどころはいったいどこでしょうか。
EncodeとDecodeの使いどころ
EncodeとDecodeの使いどころは、ストリーミングな処理を書きたい場合です。
const jsonStream = `
{"Name":"Alice","Age":6,"Parents":["Bob","Chris"]}
{"Name":"Dean","Age":20,"Parents":["Erick","Fern"]}
{"Name":"Gerald","Age":19,"Parents":["Harry","Irene"]}
{"Name":"Jane","Age":11,"Parents":["Kelvin","Lucy"]}
`
reader := strings.NewReader(jsonStream)
dec := json.NewDecoder(reader)
var builder strings.Builder
enc := json.NewEncoder(&builder)
for {
var m map[string]any
if err := dec.Decode(&m); err == io.EOF {
break
} else if err != nil {
fmt.Println(err)
break
}
delete(m, "Parents")
if err := enc.Encode(m); err != nil {
fmt.Println("error")
}
}
fmt.Println(builder.String()) // 以下のように出力される
/*
{"Age":6,"Name":"Alice"}
{"Age":20,"Name":"Dean"}
{"Age":19,"Name":"Gerald"}
{"Age":11,"Name":"Jane"}
*/
上のコードではJSON形式のデータからJSONオブジェクトを一つ読み込んでmap[string]anyに変換し、そのマップからParentsキーを削除し、再度JSON形式のデータに変換して書き込む、という処理をデータの全てのJSONオブジェクトに行っています。
大量のJSONのデータが書かれているファイルやリクエストをストリーミング処理したい場合に活躍してくれそうです。
さいごに
今回調べてみて、ストリーミング処理を行いたい場合はDecoderのDecode及びEncoderのEncodeメソッドを使い、そうでない場合はMarshal及びUnmarshalを使っていけば良さそうだということがわかりました。
正直ストリーミング処理についてはまだ理解が浅いので調べを進めるのに苦労しましたが、これから理解を深めて今回調べたDecodeやEncodeなどを使ってバリバリにストリーミング処理を書いていきたいです。
この記事を書いた人

- 事業開発部 web application engineer
-
これまで農業、士業と経験し、まったく異業種のエンジニアとしてアーティスに入社。
現在は事業開発部でバックエンドエンジニアとして仕事に従事。可読性の高いコードが書けるよう日々勉強中。趣味は一人旅。
この執筆者の最新記事
関連記事
最新記事
FOLLOW US
最新の情報をお届けします
- facebookでフォロー
- Xでフォロー
- Feedlyでフォロー





