Go言語(プログラミング)入門メモ㉘

## JSONと構造体を相互に変換する。
JSONとはWeb上でデータをやり取りする際によく用いられるデータの記述方式。
encoding/jsonという標準パッケージを用いることで、JSONのデータをGoの構造体として扱い、処理をすることが可能。

## json.Unmarshal関数でJSONを構造体に変換する
encoding/jsonはJSONを扱う標準パッケージ。
ネットワーク越しにJSONで入ってきたデータをPersonという構造体に格納し、再度JSONに変換して送る処理を想定する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3579705/fe0d1a9b-208c-f11d-8464-b276ad3538bc.png)
JSONのデータはbyteで作成し、変数bに代入する。続いて、Person型の変数pを宣言し、json.Unmarshal関数に変数bとポインタの変数pを渡す。
エラーハンドリングをして、エラーがなければ変数pのそれぞれの値を表示してみる。
次のコードを実行すると、JSONのデータが構造体Personに変換されて表示されることが確認できる。
~~~
package main

import (
	"encoding/json"
	"fmt"
)

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

func main() {
	b := []byte(`{"name":"mike","age":20,"nicknames":["a","b","c"]}`)
	var p Person
	if err := json.Unmarshal(b, &p); err != nil {
		fmt.Println(nil)
	}
	fmt.Println(p.Name, p.Age, p.Nicknames)

}
~~~
※コードの意味が分かりにくかったので補足
~~~
type Person struct {
    Name      string
    Age       int
    Nicknames []string
}
~~~

Personという構造体（struct）を定義します。この構造体は、名前（Name）、年齢（Age）、およびニックネームのリスト（Nicknames）を持ちます。
~~~
b := []byte(`{"name":"mike","age":20,"nicknames":["a","b","c"]}`)
~~~
JSON文字列をバイトスライスに変換し、変数bに格納します。これは、後で構造体にデコードするためのデータです。
~~~
if err := json.Unmarshal(b, &p); err != nil {
    fmt.Println(nil)
}
~~~
json.Unmarshal関数を使用して、バイトスライスbからPerson型の変数pにデコードします。エラーが発生した場合は、fmt.Println(nil)を実行します。
~~~
fmt.Println(p.Name, p.Age, p.Nicknames)
~~~
デコードされた構造体の内容を標準出力に表示します。具体的には、名前、年齢、およびニックネームのリストを出力します。
※補足終わり

実行結果は以下。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3579705/4fc297cf-38ff-213a-dad9-51b821580852.png)

構造体Personのフィールドは、パブリックなので頭文字を大文字にしている。ただ、json.Unmarshal関数は大文字か小文字かに関わらず名前が一致すればよいので、問題なく構造体Personに値が代入されている。
JSONのキーの頭文字が大文字でも問題なく値は代入される。ただし、一致しない名前にしてしまうと値が代入されない。次のコードでは、JSONのキーが「age」でなく「ge」になっているので構造体Personのageの値はデフォルト値である0が表示される
~~~
package main

import (
	"encoding/json"
	"fmt"
)

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

func main() {
	b := []byte(`{"name":"mike","ge":20,"nicknames":["a","b","c"]}`)
	var p Person
	if err := json.Unmarshal(b, &p); err != nil {
		fmt.Println(nil)
	}
	fmt.Println(p.Name, p.Age, p.Nicknames)

}
~~~
出力結果は以下。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3579705/112dc23d-f78f-4ada-6852-6bb7cbe917ec.png)
↑ageが0と表示される。

## json.Marshal関数で構造体をJSONに変換する。
構造体のデータをJSONに変換するには、json.Marshal関数を使う。引数に構造体Personの変数pを渡し、エラーは無視して変数vに代入する。
このとき、変数vはbyte配列なので、次のコードのようにstring型で表示すると変数pからJSONに変換されて表示される。
~~~
package main

import (
	"encoding/json"
	"fmt"
)

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

func main() {
	b := []byte(`{"name":"mike","ge":20,"nicknames":["a","b","c"]}`)
	var p Person
	if err := json.Unmarshal(b, &p); err != nil {
		fmt.Println(nil)
	}
	fmt.Println(p.Name, p.Age, p.Nicknames)

	v, _ := json.Marshal(p)
	fmt.Println(string(v))

}
~~~
出力結果は以下。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3579705/e21304de-f9be-3e81-a0c2-13310885e9e3.png)

## JSONに変換する際のキー名を指定する
構造体Personのフィールド名はエクスポートしているため、JSONに変換した際もキーの頭文字は大文字になっている。
このとき、構造体のフィールド名がJSONのキーに変換されるときの名前を指定することができる。
構造体Personのフィールドの後ろで「json:"name"」のように名前を指定して再度コードを実行すると、JSONのキーが指定した通り小文字になっていることが分かる。
~~~
package main

import (
	"encoding/json"
	"fmt"
)

type Person struct {
	Name      string `json:"name"` //キー名の指定
	Age       int
	Nicknames []string
}

func main() {
	b := []byte(`{"name":"mike","ge":20,"nicknames":["a","b","c"]}`)
	var p Person
	if err := json.Unmarshal(b, &p); err != nil {
		fmt.Println(nil)
	}
	fmt.Println(p.Name, p.Age, p.Nicknames)

	v, _ := json.Marshal(p)
	fmt.Println(string(v))

}
~~~
実行結果は以下
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3579705/d503c0b2-cc2e-6147-a92d-09dfc6b7d149.png)
↑nameという頭文字が小文字で出力されている。
また、。「json:"nameXXX"」のように指定すると、JSONのキーの名前も変わる。また、json.Unmarshal関数の時も「nameXXX」として扱うので、JSONの中に「nameXXX」という名前のキーが存在しないため、デフォルトとしてから文字列が表示される
~~~
package main

import (
	"encoding/json"
	"fmt"
)

type Person struct {
	Name      string `json:"nameXXX"` //JSONのとき名前を変える
	Age       int
	Nicknames []string
}

func main() {
	b := []byte(`{"name":"mike","age":20,"nicknames":["a","b","c"]}`)
	var p Person
	if err := json.Unmarshal(b, &p); err != nil {
		fmt.Println(nil)
	}
	fmt.Println(p.Name, p.Age, p.Nicknames)

	v, _ := json.Marshal(p)
	fmt.Println(string(v))

}
~~~
実行結果は以下
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3579705/6601c30a-6ecf-2dd5-03d2-feba26c1fcae.png)

## JSONでの型変更
構造体とJSONとの間での変換を行う時に、違う型として扱うこともできる。
「json:"age,string"」とすると、json.Marshal関数で構造体Personに渡すJSONのageの値はstring型とする必要がある。
以下のコードを実行するとjson.Marshal関数でJSONに変換したときのageはstring型で表示されるようになる。
~~~
package main

import (
	"encoding/json"
	"fmt"
)

type Person struct {
	Name      string `json:"name"`
	Age       int    `json:"age,string` //jsonのときはstring型にする。
	Nicknames []string
}

func main() {
	b := []byte(`{"name":"mike","age":20,"nicknames":["a","b","c"]}`)
	var p Person
	if err := json.Unmarshal(b, &p); err != nil {
		fmt.Println(nil)
	}
	fmt.Println(p.Name, p.Age, p.Nicknames)

	v, _ := json.Marshal(p)
	fmt.Println(string(v))

}
~~~
結果は以下
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3579705/2e0d25ca-6aa1-6c14-b80e-eaf1c4a1bc2b.png)

##JSONに表示する際に非表示とする。
構造体の定義の部分で「json:"-"」と指定するとJSON側では無視される。
~~~
package main

import (
	"encoding/json"
	"fmt"
)

type Person struct {
	Name      string `json:"-"`
	Age       int    `json:"age,string` //jsonのときはstring型にする。
	Nicknames []string
}

func main() {
	b := []byte(`{"name":"mike","age":20,"nicknames":["a","b","c"]}`)
	var p Person
	if err := json.Unmarshal(b, &p); err != nil {
		fmt.Println(nil)
	}
	fmt.Println(p.Name, p.Age, p.Nicknames)

	v, _ := json.Marshal(p)
	fmt.Println(string(v))

}
~~~
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3579705/367653d4-b7f8-507e-61fb-bc5a3d42a213.png)
Nameの値がから文字列になり、nameを除いてJSONが表示されるようになる。

## json.Marshal関数をカスタマイズする
json.marshal関数で構造体をJSONに変換する際の処理を、独自にカスタマイズすることも可能。カスタマイズする場合は、独自の処理をしたい型にMarshalJSONメソッドを実装する。
構造体PersonにMarshalJSONメソッドを実装し、処理を記載する。
~~~
package main

import (
	"encoding/json"
	"fmt"
)

type Person struct {
	Name      string `json:"-"`
	Age       int    `json:"age",string` //jsonのときはstring型にする。
	Nicknames []string
}

func (p Person) MarshalJSON() ([]byte, error) { //MarshaJSONメソッドを実装
	v, err := json.Marshal(&struct { //その場で無名構造体を定義
		Name string //無名構造体にNameというフィールドを定義
	}{
		Name: "Mr." + p.Name, //Nameの値を初期化
	})
	return v, err
}

func main() {
	b := []byte(`{"name":"mike","age":20,"nicknames":["a","b","c"]}`)
	var p Person
	if err := json.Unmarshal(b, &p); err != nil {
		fmt.Println(nil)
	}
	fmt.Println(p.Name, p.Age, p.Nicknames)

	v, _ := json.Marshal(p) //Mashal関数でMarshalJSONメソッドが呼び出される
	fmt.Println(string(v))

}
~~~
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3579705/7745e754-0115-d826-18c5-68c5d79d9ff4.png)

もとになるJSONなnameの値が-で隠れているので元に戻して再度実行する
~~~
package main

import (
	"encoding/json"
	"fmt"
)

type Person struct {
	Name      string
	Age       int `json:"age",string` //jsonのときはstring型にする。
	Nicknames []string
}

func (p Person) MarshalJSON() ([]byte, error) { //MarshaJSONメソッドを実装
	v, err := json.Marshal(&struct { //その場で無名構造体を定義
		Name string //無名構造体にNameというフィールドを定義
	}{
		Name: "Mr." + p.Name, //Nameの値を初期化
	})
	return v, err
}

func main() {
	b := []byte(`{"name":"mike","age":20,"nicknames":["a","b","c"]}`)
	var p Person
	if err := json.Unmarshal(b, &p); err != nil {
		fmt.Println(nil)
	}
	fmt.Println(p.Name, p.Age, p.Nicknames)

	v, _ := json.Marshal(p) //Mashal関数でMarshalJSONメソッドが呼び出される
	fmt.Println(string(v))

}
~~~

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3579705/c6cbd3a3-2b95-8345-9c42-30f5b86425d9.png)

## 学習に使用した教材
・『入門】Golang基礎入門 + 各種ライブラリ + 簡単なTodoWebアプリケーション開発(Go言語)』M.A EduTech
https://www.udemy.com/course/golang-webgosql/?utm_medium=udemyads&utm_source=bene-msa&utm_campaign=responsive&utm_content=top-1&utm_term=general&msclkid=81e2f24a32cc185d275d953d60760226&couponCode=NEWYEARCAREERJP

・『シリコンバレー一流プログラマーが教える Goプロフェッショナル大全』酒井 潤 (著)
https://www.amazon.co.jp/%E3%82%B7%E3%83%AA%E3%82%B3%E3%83%B3%E3%83%90%E3%83%AC%E3%83%BC%E4%B8%80%E6%B5%81%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9E%E3%83%BC%E3%81%8C%E6%95%99%E3%81%88%E3%82%8B-Go%E3%83%97%E3%83%AD%E3%83%95%E3%82%A7%E3%83%83%E3%82%B7%E3%83%A7%E3%83%8A%E3%83%AB%E5%A4%A7%E5%85%A8-%E9%85%92%E4%BA%95-%E6%BD%A4/dp/4046070897