Go言語でのJSON操作: json.Marshal, json.Unmarshal, json.NewDecoder, json.NewEncoderの使い分け

## はじめに

Go言語の学習中に、JSONを扱うのに`encoding/json`パッケージの`json.Marshal`, `json.Unmarshal`,`json.NewDecoder`,`json.NewEncoder`といった関数を使うけど、これらの関数の使い分けについて気になりました。そこで、この記事ではそれぞれの関数の特性と適した使用場面について学んだことを記述しています。

## `json.Marshal`, `json.Unmarshal`について

まず、`json.Marshal`と`json.Unmarshal`ですが、これらの関数は主にメモリ内のデータをJSON形式にシリアライズ（変換）したり、JSONをデシリアライズ（変換）してデータを読み出したりするのに使用されます。以下にその使用例を示します。

▼ `json.Marshal`の使用例

```diff_go
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`の使用例

```diff_go
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`

```diff_go
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`

```diff_go
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`の使い分けについて解説しました。それぞれの関数がどのようなシチュエーションで最適であるかを理解することで、より効率的なコーディングが可能になります。