技術向上

プログラミングの学び、気になるテクノロジーやビジネストレンドを発信

json【Go】

JSON形式から変数に、変数からJSON形式に変換する方法を見ていきます。

JSONから変数

まずは、JSON形式から変数への変換です。

type Person struct {    // 各フィールド名の頭文字は、必ず大文字にする
    Name      string
    Age       int
    Nicknames []string
}

func main() {
    var p Person
    b := []byte(`{"name":"mike","age":19,"nicknames":["mike","mic","micky"]}`)    // json形式のbyte配列を生成
    if err := json.Unmarshal(b, &p); err != nil {    // 値を変更するため、変数はポインタで渡す
        fmt.Println(err)
    }
    fmt.Println(p.Name, p.Age, p.Nicknames)    // mike 19 [mike mic micky]
}

Unmarshalする際の格納先の型については注意が必要です。structの中のフィールドは頭文字を大文字にしないと、格納することができません。 Unmarshal先のstructのフィールドは、必ず頭文字を大文字にします。


変数からJSON

続いて、変数からJSON形式への変換です。

type Person struct {
    Name      string
    Age       int
    Nicknames []string
}

func main() {
    var p Person
    b := []byte(`{"name":"mike","age":19,"nicknames":["mike","mic","micky"]}`)    // json形式のbyte配列を生成
    if err := json.Unmarshal(b, &p); err != nil {    // 変数はポインタで渡す
        fmt.Println(err)
    }
    
    v, _ := json.Marshal(p)    // 使用しないため、errは破棄
    fmt.Println(string(v))    // byte配列をstringにcast  {"Name":"mike","Age":19,"Nicknames":["mike","mic","micky"]}
}


デフォルトのままですと、json.Marshal(v interface{})が返す値のフィールド名、型は、
変換対象である変数の定義に沿ったものになります。json出力時の定義を変更したいという場合、
下記の通り、変数の定義を「jsonの時はこうして欲しい」というように記述することができます。

type Person struct {
    Name      string   `json:"name,omitempty"`
    Age       int      `json:"age,string,omitempty"`
    Nicknames []string `json:"nicknames"`
}

""の中はカンマ区切りで記載しますが、スペースを空けてはいけません。
左から順にフィールド名、型、空や数値0の場合の対応です。
フィールド名は省略できないので、変数定義から変更しない場合は、カンマだけを記述します。
omitemptyとすると、空や数値0の場合に返す値に含まれなくなります。

また、type定義したstructの場合、omitemptyを有効にするには、定義の型をポインタにする必要があります。

type T struct {
}

type Person struct {
    T         *T       `json:",omitempty"`
}


変換内容のカスタマイズ

Marshal、Unmarshalそれぞれ次のように、処理内容を書き換えることができます。

func (p *Person) UnmarshalJSON(b []byte) error {    // メソッド(指定した型限定となる)。 中身を変更するため、ポインタレシーバー
    type Person2 struct {    // 新たにローカル型を定義。Personを指定、またはPersonを継承してしまうと、このメソッドを有することになり解決できずstack overflowとなるため
        Name string
    }
    var p2 Person2
    err := json.Unmarshal(b, &p2)
    if err != nil {
        fmt.Println(err)
    }
    p.Name = p2.Name + "!"
    return err
}

func (p Person) MarshalJSON() ([]byte, error) {    // メソッド(指定した型限定となる)。
    v, err := json.Marshal(&struct {    // この関数内でのみ使用する構造体なので、定義と中身の指定を連続で行う
        Name string
    }{
        Name: "Mr." + p.Name,
    })
    return v, err
}

上記の処理内容はあくまで例ですので、返り値の型さえ気をつければ、自由に変更できます。

なお、フィールド名等は「変数からJSON」で紹介した、
type定義で記述したjson変換時の各種指定ではなく、こちらのカスタマイズした内容が反映されます。
そのため、頭文字を小文字にしたい場合には、改めて処理の中に記載します。

func (p Person) MarshalJSON() ([]byte, error) { // メソッド(指定した型限定となる)。
    v, err := json.Marshal(&struct {
        Name string `json:"name"`    // nameと出力するように変更。定義のフィールド名は、レシーバーの型と合わせる必要がある
    }{
        Name: "Mr." + p.Name,
    })
    return v, err
}