golangの日記

Go言語を中心にプログラミングについてのブログ

Go言語(golang) JSON Marshal, Unmarshal, NewDecoder, NewEncoder の使い方

golang.png


Go言語での JSON のパース/変換 Marshal, Unmarshal, NewDecoder, NewEncoder の使い方。
構造体のタグの指定方法については Go言語の構造体とJSON に書きましたのでそちらをどうぞ。

ドキュメント



目次



Go言語のデータ型(struct,map,slice) から JSON に変換する - Marshal

package main

import (
    "encoding/json"
    "fmt"
)

func main() {
    m := map[string]interface{}{
        "name": "Tanaka",
        "age":  30,
    }

    data, err := json.Marshal(m)
    if err != nil {
        fmt.Println(err)
        return
    }

    fmt.Println(string(data)) // {"age":30,"name":"Tanaka"}
}


インデントをつける - MarshalIndent

package main

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

func main() {
    m := map[string]interface{}{
        "name": "Tanaka",
        "age":  30,
    }

    data, err := json.MarshalIndent(m, "", strings.Repeat(" ", 2))
    if err != nil {
        fmt.Println(err)
        return
    }

    fmt.Println(string(data))
    // {
    //      "age": 30,
    //      "name": "Tanaka"
    // }
}


JSON から Go言語のデータ型に変換する - Unmarshal

package main

import (
    "encoding/json"
    "fmt"
)

func main() {
    data := `{
  "name": "Tanaka",
  "age": 30
}`

    var m map[string]interface{}

    err := json.Unmarshal([]byte(data), &m)
    if err != nil {
        fmt.Println(err)
        return
    }

    fmt.Printf("%v\n", m) // map[name:Tanaka age:30]
}


Go言語のデータ型から JSON に変換する - NewEncoder

Encoder を使えば *os.File を引数にして書き出せる

package main

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

func main() {
    m := map[string]interface{}{
        "name": "Tanaka",
        "age":  30,
        "html": "<p>hello</p>",
    }

    fp, err := os.OpenFile("data.json", os.O_CREATE|os.O_WRONLY, 0664)
    if err != nil {
        fmt.Println(err)
        return
    }
    defer fp.Close()

    e := json.NewEncoder(fp)

    // HTML をエスケープするかどうか。たぶんデフォルトで true
    e.SetEscapeHTML(true)

    // インデントする。第一引数の prefix は何に使うんだろ?不明です
    e.SetIndent("", strings.Repeat(" ", 2))

    if err := e.Encode(m); err != nil {
        fmt.Println(err)
        return
    }

    // {
    //     "age": 30,
    //     "html": "\u003cp\u003ehello\u003c/p\u003e",
    //     "name": "Tanaka"
    // }

}


JSON から Go言語のデータ型に変換する - NewDecoder

Decoder を使えば *os.File を引数にできるので、
ioutil.ReadFile などを使ってファイルの中身をすべて読み出す必要がない

package main

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

func main() {
    data := `{
  "name": "Tanaka",
  "age": 30
}`

    var user struct {
        Name string `json:"name"`
        Age  int    `json:"age"`
    }

    // ファイルから読み込む場合は *os.File を指定する
    d := json.NewDecoder(strings.NewReader(data))

    // 構造体の場合に、JSONのキー名がその構造体に無い場合はエラーにする strict モード
    // yaml.v2 と違って名前の重複は許されるようです。
    d.DisallowUnknownFields() // エラーの場合 json: unknown field "JSONのフィールド名"

    if err := d.Decode(&user); err != nil && err != io.EOF {
        fmt.Println(err)
        return
    }

    fmt.Println(user) // {Tanaka 30}
}


トークン - NewDecoder - Token

package main

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

func main() {
    data := `{
  "name": "Tanaka",
  "age": 30,
  "score": [10, 20, 30]
}`

    d := json.NewDecoder(strings.NewReader(data))

    for {
        t, err := d.Token()
        if err == io.EOF {
            break
        }

        if err != nil {
            fmt.Println(err)
            break
        }

        switch t.(type) {
        case json.Delim:
            // type が Delim `{}` とか `[]`
            fmt.Printf("%-11T: %v", t, t)
        default:
            // 続きがあるかどうかを真偽値で返す
            if d.More() {
                fmt.Printf("%-11T:   %-8v < 続く", t, t)
            } else {
                fmt.Printf("%-11T:   %-8v < 終わり", t, t)
            }
        }
        fmt.Printf("\n")
    }

    // 実行結果
    //     json.Delim : {
    //     string     :   name     < 続く
    //     string     :   Tanaka   < 続く
    //     string     :   age      < 続く
    //     float64    :   30       < 続く
    //     string     :   score    < 続く
    //     json.Delim : [
    //     float64    :   10       < 続く
    //     float64    :   20       < 続く
    //     float64    :   30       < 終わり
    //     json.Delim : ]
    //     json.Delim : }

}


その他の関数

package main

import (
    "bytes"
    "encoding/json"
    "fmt"
)

func main() {

    { // インデントを取り除く - Compact

        data := `{
  "name": "Tanaka",
  "age":  30
}`

        var b bytes.Buffer
        err := json.Compact(&b, []byte(data))
        if err != nil {
            fmt.Println(err)
            return
        }
        fmt.Println(b.String()) // {"name": "Tanaka", "age": 30}
    }

    { // HTML をエスケープする - HTMLEscape

        data := `["<p>hello</p>"]`

        var b bytes.Buffer
        json.HTMLEscape(&b, []byte(data))
        fmt.Println(b.String()) // ["\u003cp\u003ehello\u003c/p\u003e"]
    }

    { // JSON文字列にインデントをつける - Indent

        data := `{"name": "Tanaka", "age":  30}`

        var b bytes.Buffer
        err := json.Indent(&b, []byte(data), "", "  ")
        if err != nil {
            fmt.Println(err)
            return
        }
        fmt.Println(b.String())
        // {
        //     "name": "Tanaka",
        //     "age": 30
        // }
    }

    { // JSONとして有効な文字列かどうか確認する - Valid

        data := `{"name": "Tanaka", "age":  30}`
        ok := json.Valid([]byte(data))
        if !ok {
            fmt.Println("Invalid as string of JSON")
        }
    }
}


エラーの判別

package main

import (
    "encoding/json"
    "fmt"
)

func main() { // エラーの種類を確認
    if err != nil {
        switch err.(type) {
        case *json.SyntaxError:
            fmt.Printf("SyntaxError: %v\n", err)
        case *json.InvalidUTF8Error:
            // Windows で使えそう
            fmt.Printf("InvalidUTF8Error: %v\n", err)
        case *json.InvalidUnmarshalError:
            fmt.Printf("InvalidUnmarshalError: %v\n", err)
        case *json.UnmarshalFieldError:
            fmt.Printf("UnmarshalFieldError: %v\n", err)
        case *json.UnmarshalTypeError:
            fmt.Printf("UnmarshalTypeError: %v\n", err)
        case *json.MarshalerError:
            fmt.Printf("MarshalerError: %v\n", err)
        case *json.UnsupportedTypeError:
            fmt.Printf("UnsupportedTypeError: %v\n", err)
        case *json.UnsupportedValueError:
            fmt.Printf("UnsupportedValueError: %v\n", err)
        }
    }
}