LoginSignup
26
12

More than 5 years have passed since last update.

GoでJSONから構造体に変換するときに気をつけること

Last updated at Posted at 2018-11-06

JSONライブラリにGoの構造体を投げ入れるとき、
* 構造体のエイリアスやフィールド名を大文字にしたらいいんだっけ、小文字にしたらいいんだっけ、
* jsonタグとフィールド名のどっちが優先的にマッピングの対象になるんだっけ、

と悩んだので、いろいろ実験してみました。

普通(?)

投げ入れるjsonのプロパティ名と受け取る構造体のフィールド名が完全に一致している場合は当然、期待通りマッピングできる

package main

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

type User struct {
    Id   int
    Name string
}

func main() {
    src := `{"Id":3,"Name":"nao"}`

    u := new(User)
    err := json.Unmarshal([]byte(src), u)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("%+v\n", u)
}

// 出力結果:&{Id:3 Name:nao}

jsonのkey値が小文字の場合

投げ入れるjsonのプロパティ名が小文字で受け取る構造体のフィールド名が大文字のときどうなるんでしょう。
頭文字の大小が違ってもマッピングできてますね!


package main

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

type User struct {
    Id   int
    Name string
}

func main() {
    src := `{"id":3,"name":"nao"}`

    u := new(User)
    err := json.Unmarshal([]byte(src), u)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("%+v\n", u)
}

// 出力結果:&{Id:3 Name:nao}

構造体フィールド名小文字にした場合

jsonのプロパティ名も小文字、構造体のフィールド名も小文字の場合はどうでしょう。
マッピングできてませんね。

フィールド名が小文字だとプライベートなフィールドとして認識されてしまいます。Userという構造体のフィールドid, nameはpackage mainの中でしか触ることができません。

package jsonに構造体のフィールドを渡すにはフィールド名を大文字始まりにしないといけません。
Goの基本文法ですが忘れてるとハマりがちですね。

package main

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

type User struct {
    id   int
    name string
}

func main() {
    src := `{"id":3,"name":"nao"}`

    u := new(User)
    err := json.Unmarshal([]byte(src), u)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("%+v\n", u)
}

// 出力結果:&{id:0 name:}

構造体フィールド名とJSONキー名が不一致+jsonタグをつける

投げ入れられるjson名をそのままGoの世界で扱いたくないということはよくあります。

e.g. json名をGoの世界でそのまま使いたくないとき
* プロジェクトの命名規則と異なる。
* すでにnameという変数は使われていて名前がバッティングする。
* nameってなんの名前のこと指してるのかわからない。わかりやすいフィールド名にしたい。

そんなときはjsonタグをつけてやるとよいです。
投げ入れられるjsonのプロパティ名とjsonタグが一致していれば、構造体のフィールド名は好きに設定できます

package main

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

type User struct {
    UserId   int `json:"id"`
    UserName string `json:"name"`
}

func main() {
    src := `{"id":3,"name":"nao"}`

    u := new(User)
    err := json.Unmarshal([]byte(src), u)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("%+v\n", u)
}

// 出力結果:&{UserId:3 UserName:nao}

構造体名を小文字

フィールド名は大文字始まりじゃないといけないみたいですが、構造体名自体は小文字始まりでよいみたいです。。。

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

type user struct {
    UserId   int `json:"id"`
    UserName string `json:"name"`
}

func main() {
    src := `{"id":3,"name":"nao"}`

    u := new(user)
    err := json.Unmarshal([]byte(src), u)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("%+v\n", u)
}

// 出力結果:&{UserId:3 UserName:nao}

jsonタグ名とJSONキー名の大文字小文字は適当でいい

わざわざそんなことする必要もないですが。。。

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

type user struct {
    UserId   int `json:"Id"`
    UserName string `json:"nAme"`
}

func main() {
    src := `{"id":3,"NamE":"nao"}`

    u := new(user)
    err := json.Unmarshal([]byte(src), u)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("%+v\n", u)
}

// 出力結果:&{UserId:3 UserName:nao}

そもそも構造体のエイリアスを設定する必要はない

手軽にJSONからデータを取り出したいなら

package main

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

func main() {
    src := `{"id":3,"name":"nao"}`

    u := &struct{UserName string `json:"name"`}{}
    err := json.Unmarshal([]byte(src), u)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("%+v\n", u)
}

// 出力結果:&{UserName:nao}
26
12
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
26
12