はじめに
Go言語の学習中に、JSONを扱うのにencoding/json
パッケージのjson.Marshal
, json.Unmarshal
,json.NewDecoder
,json.NewEncoder
といった関数を使うけど、これらの関数の使い分けについて気になりました。そこで、この記事ではそれぞれの関数の特性と適した使用場面について学んだことを記述しています。
json.Marshal
, json.Unmarshal
について
まず、json.Marshal
とjson.Unmarshal
ですが、これらの関数は主にメモリ内のデータをJSON形式にシリアライズ(変換)したり、JSONをデシリアライズ(変換)してデータを読み出したりするのに使用されます。以下にその使用例を示します。
▼ json.Marshal
の使用例
package main
import (
"encoding/json"
"fmt"
"log"
)
type Person struct {
Name string
Age int
}
func main() {
p := Person{Name: "Alice", Age: 20}
fmt.Println(p)
// 出力: {Alice 20}
bytes, err := json.Marshal(p)
if err != nil {
log.Fatal(err)
}
fmt.Println(bytes)
// 出力: [123 34 78 97 109 101 34 58 34 65 108 105 99 101 34 44 34 65 103 101 34 58 50 48 125]
// これはJSON形式の文字列をASCIIコードに対応する整数のスライスとして表現したものです。
fmt.Println(string(bytes))
// 出力: {"Name":"Alice","Age":20}
}
▼ json.Unmarshal
の使用例
package main
import (
"encoding/json"
"fmt"
"log"
)
type Person struct {
Name string
Age int
}
func main() {
jsonBytes := []byte(`{"Name":"Alice","Age":20}`)
fmt.Println(jsonBytes)
// 出力: [123 34 78 97 109 101 34 58 34 65 108 105 99 101 34 44 34 65 103 101 34 58 50 48 125]
// これはJSON形式の文字列をASCIIコードに対応する整数のスライスとして表現したものです。
fmt.Println(string(jsonBytes))
// 出力: {"Name":"Alice","Age":20}
var p Person
err := json.Unmarshal(jsonBytes, &p)
if err != nil {
log.Fatal(err)
}
fmt.Println(p)
// 出力: {Alice 20}
}
json.NewDecoder
, json.NewEncoder
について
次に、json.NewDecoder
とjson.NewEncoder
です。これらの関数はストリーム形式のデータを扱う際に使用します。例えば、ネットワーク経由で送受信するデータや、大きなファイルから読み出すデータなどです。以下にその使用例を示します。
▼ json.NewDecoder
package main
import (
"encoding/json"
"fmt"
"log"
"strings"
)
type Person struct {
Name string
Age int
}
func main() {
jsonReader := strings.NewReader(`{"Name":"Alice","Age":20}`)
fmt.Printf("%#v\n", jsonReader)
// 出力: &strings.Reader{...}
// jsonReaderはstrings.Readerの構造体のポインタであり、内部的な状態を表現するフィールドを含んでいます。
jsonDecoder := json.NewDecoder(jsonReader)
fmt.Printf("%#v\n", jsonDecoder)
// 出力: &json.Decoder{...}
// jsonDecoderはjson.Decoderの構造体のポインタであり、内部的な状態を表現するフィールドを含んでいます。
var p Person
err := jsonDecoder.Decode(&p)
if err != nil {
log.Fatal(err)
}
fmt.Println(p)
// 出力: {Alice 20}
}
▼ json.NewEncoder
package main
import (
"bytes"
"encoding/json"
"fmt"
"log"
)
type Person struct {
Name string
Age int
}
func main() {
p := Person{Name: "Alice", Age: 20}
fmt.Printf("%#v\n", p)
// 出力: main.Person{Name:"Alice", Age:20}
buffer := &bytes.Buffer{}
fmt.Printf("%#v\n", buffer)
// 出力: &bytes.Buffer{...}
// bufferはbytes.Bufferの構造体のポインタであり、内部的な状態を表現するフィールドを含んでいます。
jsonEncoder := json.NewEncoder(buffer)
fmt.Printf("%#v\n", jsonEncoder)
// 出力: &json.Encoder{...}
// jsonEncoderはjson.Encoderの構造体のポインタであり、内部的な状態を表現するフィールドを含んでいます。
err := jsonEncoder.Encode(p)
if err != nil {
log.Fatal(err)
}
fmt.Println(buffer.String())
// 出力: {"Name":"Alice","Age":20}
}
両者の比較
json.Marshal
とjson.Unmarshal
は一度に全てのデータをメモリにロードし、その全てをJSON形式にシリアライズ、またはJSON形式からデシリアライズします。一方、json.NewEncoder
とjson.NewDecoder
はストリーム形式のデータを扱います。つまり、データは順次読み込まれ、必要に応じてエンコードまたはデコードされます。この特性から、大量のデータを扱う際や、ネットワーク経由でデータを送受信する際には、json.NewEncoder
とjson.NewDecoder
を使うべきです。
一方で、メモリに十分に収まる量のデータを扱う際や、簡潔さが求められる場合には、json.Marshal
とjson.Unmarshal
を用いると良いでしょう。
さいごに
以上、json.Marshal
, json.Unmarshal
, json.NewDecoder
, json.NewEncoder
の使い分けについて解説しました。それぞれの関数がどのようなシチュエーションで最適であるかを理解することで、より効率的なコーディングが可能になります。