グローバルナビゲーションへ

本文へ

フッターへ

お役立ち情報Blog



【Go言語】jsonデータをstreamで扱うEncoder、Decoder型を試してみる

goのjsonパッケージには、jsonデータをstreamで扱うEncoder、Decoder型があります。
今回はこのEncoder、Decoderについて紹介します。

Encoderの定義

Encoderの構造体は以下のように定義されています

// An Encoder writes JSON values to an output stream.
type Encoder struct {
	w          io.Writer
	err        error
	escapeHTML bool

	indentBuf    *bytes.Buffer
	indentPrefix string
	indentValue  string
}

Encoderを作成するには NewEncoder 関数にio.Writerインターフェース型を渡します。
これによりstreamでの処理が可能になります。

// NewEncoder returns a new encoder that writes to w.
func NewEncoder(w io.Writer) *Encoder {
	return &Encoder{w: w, escapeHTML: true}
}

Encoderの使い方

io.Writerを受け付けるのでstreamでファイルに書き出せます。

package main

import (
	"encoding/json"
	"fmt"
	"os"
	"strings"
)

func main() {
	// jsonにencodeするデータ構造を定義します
	m := map[string]interface{}{
		"name": "hoge",
		"age":  30,
		"html": "<!DOCTYPE html><html><head><meta charset=\"utf-8\"><title>吾輩は猫である</title></head><body><p>吾輩わがはいは猫である。名前はまだ無い。</p><p>どこで生れたかとんと見当けんとうがつかぬ。</p></body></html>",
	}

	// *File型はio.Writerインターフェース型を満たしています
	w, err := os.OpenFile("encode.json", os.O_CREATE|os.O_WRONLY, 0664)
	if err != nil {
		fmt.Println(err)
		return
	}
	defer w.Close()

	e := json.NewEncoder(w)

	// html文字をエスケープします
	e.SetEscapeHTML(true)

	// インデントを指定します
    // 第一引数の prefixは行の先頭で文字を追加します
    // 第二引数はインデント文字を指定します。
	e.SetIndent("", strings.Repeat(" ", 4))

    // jsonをencodeしてファイルにstreamで書き出します
	if err := e.Encode(m); err != nil {
		fmt.Println(err)
		return
	}
}

実行するとencode.jsonが作成されます。
htmlがエスケープされ、インデントが半角スペース4つになっているのがわかります。

$ cat encode.json
{
    "age": 30,
    "html": "\u003c!DOCTYPE html\u003e\u003chtml\u003e\u003chead\u003e\u003cmeta charset=\"utf-8\"\u003e\u003ctitle\u003e吾輩は猫である\u003c/title\u003e\u003c/head\u003e\u003cbody\u003e\u003cp\u003e吾輩わがはいは猫である。名前はまだ無い。\u003c/p\u003e\u003cp\u003eどこで生れたかとんと見当けんとうがつかぬ。\u003c/p\u003e\u003c/body\u003e\u003c/html\u003e",
    "name": "hoge"
}

Decoderの定義

Decoderの構造体は以下のように定義されています。

// A Decoder reads and decodes JSON values from an input stream.
type Decoder struct {
	r       io.Reader
	buf     []byte
	d       decodeState
	scanp   int   // start of unread data in buf
	scanned int64 // amount of data already scanned
	scan    scanner
	err     error

	tokenState int
	tokenStack []int
}

Decoderを作成するには NewDecoder 関数にio.Readerインターフェース型を渡します。
これによりstreamでの処理が可能になります。

// NewDecoder returns a new decoder that reads from r.
//
// The decoder introduces its own buffering and may
// read data from r beyond the JSON values requested.
func NewDecoder(r io.Reader) *Decoder {
	return &Decoder{r: r}
}

Decoderの使い方

io.Readerを受け付けるのでapiのレスポンスをそのままstreamで扱えます。
今回はサンプルAPIのレスポンスを構造体にマッピングしてみます。

package main

import (
	"encoding/json"
	"fmt"
	"net/http"
)

// マッピング用の構造体を用意
type Coffees []struct {
	Title       string   `json:"title"`
	Description string   `json:"description"`
	Ingredients []string `json:"ingredients"`
	ID          int      `json:"id"`
}

func main() {
	// サンプルAPIにアクセス
	resp, err := http.Get("https://api.sampleapis.com/coffee/hot")
	if err != nil {
		fmt.Println(err)
		return
	}

	var d Coffees
	// io.ReadCloserを満たすresp.Bodyを渡して、構造体にDecode
	if err := json.NewDecoder(resp.Body).Decode(&d); err != nil {
		fmt.Println(err)
		return
	}

	fmt.Printf("%#v\n", d)
}

実行するとこのように構造体にjsonデータがマッピングされています。

$ go run decode.go
main.Coffees{struct { Title string "json:\"title\""; Description string "json:\"description\""; Ingredients []string "json:\"ingredients\""; ID int "json:\"id\"" }{Title:"Black", Description:"Black coffee is as simple as it gets with ground coffee beans steeped in hot water, served warm. And if you want to sound fancy, you can call black coffee by its proper name: cafe noir.", Ingredients:[]string{"Coffee"}, ID:1}, struct { Title string "json:\"title\""; Description string "json:\"description\""; Ingredients []string "json:\"ingredients\""; ID int "json:\"id\"" }{Title:"Latte", Description:"As the most popular coffee drink out there, the latte is comprised of a shot of espresso and steamed milk with just a touch of foam. It can be ordered plain or with a flavor shot of anything from vanilla to pumpkin spice.", Ingredients:[]string{"Espresso", "Steamed Milk"}, ID:2}, ・・・・

さいごに

いかがだったでしょうか。
Encode、Decodeの使い方を学ぶことで、Marshal、Unmarshalと併せて効率良くjsonを扱えるようになります。
goはjsonを扱う事が多いのでぜひ活用してみてください。

この記事を書いた人

アーティス
アーティス
創造性を最大限に発揮するとともに、インターネットに代表されるITを活用し、みんなの生活が便利で、豊かで、楽しいものになるようなサービスやコンテンツを考え、創り出し提供しています。
この記事のカテゴリ

FOLLOW US

最新の情報をお届けします