どうもてぃ。
前回のつづきで備忘録。
mapと構造体を準備
type Addr struct { PostalCode int Country string } type User struct { Name string Age int Address Addr Email string } user := map[string]interface{}{ "Name": "motty", "Age": 30, "Address": map[string]interface{}{ "PostalCode": 1234567, "Country": "japan", }, "Email": "test@gmail.com", }
構造体で定義した型に合わせてmapを作成してます。
map側の型と構造体の型が違った場合どうなるか…というのは後ほどやっていきます。
mapをjsonへ変換
encoding/json
のjson.Marshal
を使用。
// mapをjsonへ変換 byte, err := json.Marshal(user) if err != nil { fmt.Println(err) return } fmt.Println(string(byte))
こんな感じ。string(byte)
の結果は以下。
{"Address":{"Country":"japan","PostalCode":1234567},"Age":30,"Email":"test@gmail.com","Name":"motty"}
json.Marshal
の定義元を確認すると、
func Marshal(v interface{}) ([]byte, error) { e := newEncodeState() err := e.marshal(v, encOpts{escapeHTML: true}) if err != nil { return nil, err } buf := append([]byte(nil), e.Bytes()...) encodeStatePool.Put(e) return buf, nil }
と、最終的には[]byte
とerror
が返ってるので
byte, err := json.Marshal(user)
のようにerrも受け取って上げる必要がある。
とりあえず検証するだけ、エラーハンドリング不要であれば
byte, _ := json.Marshal(user)
として省くことも出来きます。
jsonを構造体へ
encoding/json
のjson.Unmarshal
を使用。
// jsonを構造体へ割当 var u User if err := json.Unmarshal(byte, &u); err != nil { fmt.Println(err) return } fmt.Println(u)
json.Unmarshal
の定義元を確認。
func Unmarshal(data []byte, v interface{}) error { // Check for well-formedness. // Avoids filling out half a data structure // before discovering a JSON syntax error. var d decodeState err := checkValid(data, &d.scan) if err != nil { return err } d.init(data) return d.unmarshal(v) }
Unmarshalの場合errorのみ返ってきて、第二引数に渡したポインタアドレスに第一引数で渡した値がスキャニングされてるっぽい(辿っていったけど詳しくはわからん)。
全部合わせたやつ
package main import ( "encoding/json" "fmt" ) type Addr struct { PostalCode int Country string } type User struct { Name string Age int Address Addr Email string } func main() { user := map[string]interface{}{ "Name": "motty", "Age": 30, "Address": map[string]interface{}{ "PostalCode": 1234567, "Country": "japan", }, "Email": "test@gmail.com", } // mapをjsonへ変換 byte, err := json.Marshal(user) if err != nil { fmt.Println(err) return } fmt.Println(string(byte)) // jsonをstructへ割当 var u User if err := json.Unmarshal(byte, &u); err != nil { fmt.Println(err) return } fmt.Println(u) }
実行結果
{"Address":{"Country":"japan","PostalCode":1234567},"Age":30,"Email":"test@gmail.com","Name":"motty"} map[Address:map[Country:japan PostalCode:1234567] Age:30 Email:test@gmail.com Name:motty]
これをある程度理解していれば、簡単なwebサーバーは作れる。
サーバーを作るのはまた次回にするとして…
mapの型と構造体の型が異なる場合
たとえば以下のように構造体UserのAge
がint型であるにもかかわらず、map側がstringになっていた場合。
type Addr struct { PostalCode int Country string } type User struct { Name string Age int Address Addr Email string } user := map[string]interface{}{ "Name": "motty", "Age": "30", "Address": map[string]interface{}{ "PostalCode": 1234567, "Country": "japan", }, "Email": "test@gmail.com", }
何が起きるか、実行してみると
{"Address":{"Country":"japan","PostalCode":1234567},"Age":"30","Email":"test@gmail.com","Name":"motty"} json: cannot unmarshal string into Go struct field User.Age of type int
jsonを構造体へ割当
の部分、つまりjson.Unmarshal
部分でエラーが返ってきます(しっかりしてんな)。
やってくるリクエスト(map)に対して構造体をしっかり用意してあげないと、今回のようにエラーが返ってくる…といった感じです。
mapと構造体でkey, valueの数が異なる場合
構造体で用意してある型とmapの型は一致しているが、mapの中身の要素数が異なる時どうなるか。
例えば、mapに"Test": "test"
というkey, valueが入っていたとき
type Addr struct { PostalCode int Country string } type User struct { Name string Age int Address Addr Email string } user := map[string]interface{}{ "Name": "motty", "Age": 30, "Address": map[string]interface{}{ "PostalCode": 1234567, "Country": "japan", }, "Email": "test@gmail.com", "Test": "test", }
こういう場合、最終的にはエラーは出ず構造体側が優先されます。
json.Unmarshal
されたとき、構造体にないものは割り当てられないという認識でいると良いです。
もし"Test"
を受け取りたいなら、構造体側にちゃんと型を定義してあげましょう。
最後に
Goは楽しいですよ。
次回は構造体→mapを書こうと思います。