【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
などを使ってバリバリにストリーミング処理を書いていきたいです。
この記事を書いた人
-
これまで農業、士業と経験し、まったく異業種のエンジニアとしてアーティスに入社。
現在は事業開発部でバックエンドエンジニアとして仕事に従事。可読性の高いコードが書けるよう日々勉強中。趣味は一人旅。
関連記事
最新記事
FOLLOW US
最新の情報をお届けします
- facebookでフォロー
- Twitterでフォロー
- Feedlyでフォロー