Go言語のJSON処理|メモリとストリームの違い・使い分け・実装例まで

Go言語で JSON を扱う際には、標準パッケージ encoding/json を使用するのが一般的です。
このパッケージには、メモリ上で一括変換を行う方法と、ストリームを通じて順次変換する方法の2通りがあります。

まずはじめに、それぞれの違いを簡単にまとめておきましょう。


メモリ処理とストリーム処理の違い(概要)

特徴 メモリ処理 ストリーム処理
処理方法 データを一括で読み書きする データを少しずつ読み書きする
使用関数 json.Marshal / json.Unmarshal json.Encoder / json.Decoder
対象データ []byte(メモリ上の全データ) io.Reader / io.Writer(データの流れ)
適する場面 小〜中規模のデータ、簡単な変換 大規模データ、リアルタイム・逐次処理

このあと、それぞれの方法を具体的に見ていきます。


エンコードとデコードとは?

GoでJSONを扱う際によく出てくる「エンコード」と「デコード」は、次のような意味です:

用語 意味 関数例
エンコード Goの構造体などのデータをJSON形式の文字列に変換する処理 json.Marshal / Encode
デコード JSON形式の文字列をGoの構造体などのデータに戻す処理 json.Unmarshal / Decode

たとえば、次のような処理がエンコードとデコードの典型です:

jsonBytes, _ := json.Marshal(myStruct) // ← エンコード(Go → JSON)
json.Unmarshal(jsonBytes, &myStruct)   // ← デコード(JSON → Go)


メモリで JSON を処理する

json.Marshal / json.Unmarshal の実装例

package main

import (
	"encoding/json"
	"fmt"
)

type Person struct {
	Name string `json:"name"`
	Age  int    `json:"age"`
}

func main() {
	// 構造体からJSONへ(エンコード)
	p := Person{Name: "Alice", Age: 30}
	jsonBytes, err := json.Marshal(p)
	if err != nil {
		panic(err)
	}
	fmt.Println("JSON出力:", string(jsonBytes))

	// JSONから構造体へ(デコード)
	var decoded Person
	err = json.Unmarshal(jsonBytes, &decoded)
	if err != nil {
		panic(err)
	}
	fmt.Printf("構造体に戻した結果: %+v\n", decoded)
}

メモリ処理の特徴

  • 小〜中規模のデータに適している
  • データ全体を保持してから処理
  • 簡潔で直感的なコードが書ける

  • ストリームで JSON を処理する

    json.NewDecoder / json.NewEncoder の実装例

    package main
    
    import (
    	"encoding/json"
    	"fmt"
    	"os"
    )
    
    type Product struct {
    	ID    int     `json:"id"`
    	Name  string  `json:"name"`
    	Price float64 `json:"price"`
    }
    
    func main() {
    	// ファイルにJSONを書き出す(エンコード)
    	f, err := os.Create("product.json")
    	if err != nil {
    		panic(err)
    	}
    	defer f.Close()
    
    	encoder := json.NewEncoder(f)
    	product := Product{ID: 1, Name: "Notebook", Price: 1299.99}
    	if err := encoder.Encode(product); err != nil {
    		panic(err)
    	}
    	fmt.Println("ファイルにJSONを書き出しました")
    
    	// ファイルからJSONを読み込む(デコード)
    	f2, err := os.Open("product.json")
    	if err != nil {
    		panic(err)
    	}
    	defer f2.Close()
    
    	decoder := json.NewDecoder(f2)
    	var p Product
    	if err := decoder.Decode(&p); err != nil {
    		panic(err)
    	}
    	fmt.Printf("読み込んだ構造体: %+v\n", p)
    }

    ストリーム処理の特徴

  • データサイズが大きい場合や継続的に処理したい場合に有効
  • io.Reader / io.Writer を活用
  • メモリ消費を抑えつつ処理可能

  • メモリ処理とストリーム処理の比較

    項目 メモリ処理 (Marshal / Unmarshal) ストリーム処理 (Encoder / Decoder)
    対象データ 一括データ([]byte 順次データ(io.Reader / Writer
    適するデータ規模 小〜中規模 大規模・連続データ
    メリット 簡単・高速 柔軟・低メモリ

    まとめ

  • メモリ処理json.Marshal / json.Unmarshal は、データを一括で処理するのに適しており、簡潔なコードが書けます。
  • ストリーム処理json.Encoder / json.Decoder は、データを少しずつ処理したい場合に便利で、大規模データやリアルタイム処理に向いています。
  • エンコードは「Go構造体 → JSON文字列」、デコードは「JSON文字列 → Go構造体」と覚えておきましょう。
  • 処理するデータの大きさや性質に応じて、最適な方法を選ぶことが、Goでの効率的なJSON処理のポイントです。

    よかったらシェアしてね!
    • URLをコピーしました!

    この記事を書いた人

    はじめまして、「知識を愛する者」愛知郎です。

    五反田でエンジニアとして活動してます。

    こちらでは、私が最近学んだこと発信しています。

    目次