Go言語のjson.Marshal()でゆるふわにjsonを使う。

## 巷のGoのjsonについてのブログはjson.Unmarshal()の話だらけだが・・・。
標準パッケのみでゆるふわに使いたいのであれば`json.Marshal()`も重要なのに、
そういう説明があるブログがあまりないので書いてみました。
使い所としては、サーバサイドでDBの情報を引っ張ってきてjsonを投げ返す時とかクライアントとしてGoでPOSTする時ですかね。
## 今日の主役はこいつら

標準パッケージのencording/json。
https://github.com/golang/go/blob/master/src/encoding/json/encode.go

- `func Marshal(v interface{}) ([]byte, error) `
- `func MarshalIndent(v interface{}, prefix, indent string) ([]byte, error)`

## デモ
```go
package main

import "fmt"
import "encoding/json"

func main() {
  m1 := map[string]interface{}{
    "string":"dayo", 
    "int":1, 
    "friends":true,
    "null":nil,
    "iary":[]int{10, 20, 30, 30, 4000},
    "sary":[]string{"stringAry", "dayo"},
    "inary":[]interface{}{10, false ,nil},
    "map":map[string]interface{}{"int":1},
    "mapmap":map[string]interface{}{"map":map[string]interface{}{"nemui":false}},
    "dup":map[string]interface{}{"stringAry":[]string{"string", "dayo"}},
  }
  
  m2 := map[string]interface{}{
    "orange":1,
    "banana":2,
    "apple":3,
  }
  
  var m2Ary []map[string]interface{}
  m2Ary = append(m2Ary, m2)
  m2Ary = append(m2Ary, m2)
  
  s, _ := json.Marshal(m1)
  fmt.Println("m1:\n")
  fmt.Println(string(s), "\n")
  fmt.Println("m1(indented):\n")
  i, _ := json.MarshalIndent(m1, "", "   ")
  fmt.Println(string(i), "\n")
  fmt.Println("m2array:\n")
  a, _ := json.MarshalIndent(m2Ary, "", "   ")
  fmt.Println(string(a), "\n")
}
```

`map[string]interface{}`にjsonっぽく突っ込むだけの圧倒的『ゆるふわ感』。
・・・これ動くの？と思う人もいるかもしれないが、
侮るなかれ、これが`json.Marshal()`のチカラだ！

```bash
$ go run main.go
m1:

{"dup":{"stringAry":["string","dayo"]},"friends":true,"iary":[10,20,30,30,4000],"inary":[10,false,null],"int":1,"map":{"int":1},"mapmap":{"map":{"nemui":false}},"null":null,"sary":["stringAry","dayo"],"string":"dayo"} 

m1(indented):

{
   "dup": {
      "stringAry": [
         "string",
         "dayo"
      ]
   },
   "friends": true,
   "iary": [
      10,
      20,
      30,
      30,
      4000
   ],
   "inary": [
      10,
      false,
      null
   ],
   "int": 1,
   "map": {
      "int": 1
   },
   "mapmap": {
      "map": {
         "nemui": false
      }
   },
   "null": null,
   "sary": [
      "stringAry",
      "dayo"
   ],
   "string": "dayo"
} 

m2array:

[
   {
      "apple": 3,
      "banana": 2,
      "orange": 1
   },
   {
      "apple": 3,
      "banana": 2,
      "orange": 1
   }
] 

```
\キャーステキー！/
驚くことに、ちゃんと`nil`もjsonに併せて`null`になってるとか、
mapのvalueを[]interface{}型でぶっこんでも、
[](map[string]interface{})としてぶっこんでも動くとか芸が細かい。
ちなみに、どうやらjsonのkeyをa-zでソートしているみたい。でもこれは(ry

まぁ何にせよ、知っておくべき使い方かと。
## 余談：引数に何を入れれば良いか。
どう考えても`map[string]interaface{}`なのだけれど。

まったくもってゆるふわな話ではないし、
興味ない人は見出しを見よ、結論はそれだ！

`json.Marshal`関数の引数は本来`interface{}`だけど何でも入るのか？
（本当はソースをだらだら解説書いてたけど、encoderの動きが奇々怪々な動きをしていて流石に全部追ってらんないので略しました）

これとか見ると`json.Marshal`関数が何をやっているか一目瞭然のはず。

```go
package main

import "fmt"
import "encoding/json"

func main() {
  test1, err := json.Marshal(1)
  fmt.Println(string(test1), err)  //->1 <nil>
  test2, err := json.Marshal("hello")
  fmt.Println(string(test2), err)  //->"hello" <nil>
  test3, err := json.Marshal(100.00000)
  fmt.Println(string(test3), err)  //->100 <nil>
  test4, err := json.Marshal(nil)
  fmt.Println(string(test4), err)  //->null <nil>
  test5, err := json.Marshal([]uint32{200, 200, 400})
  fmt.Println(string(test5), err)  //->[200,200,400] <nil>
}
```
なんと、非参照型や配列のみも引数としてOK笑
ソースを見れば分かるのだけどmap等の参照型だった場合再帰処理をかますだけなので、map以外の非参照型を入れた場合エラーではなくjsonのvalueに入るべきjson形式の文字列にパースするという働きをする分かる(nilがnullに、helloも"hello"に)。

まぁなかなかAPIとしてこの使い方はしないとは思う笑

しかしやはり`map`のエンコーダーは違った動きをする。

```go
package main

import "fmt"
import "encoding/json"

func main() {
  test1, err := json.Marshal(map[string]string{"hello":"world"})
  fmt.Println("test1: ", string(test1), err)
  test2, err := json.Marshal(map[string]interface{}{"hello":"world"})
  fmt.Println("test2: ", string(test2), err)
  test3, err := json.Marshal(map[int]string{10:"world"})
  fmt.Println("test3: ", string(test3), err)
  test4, err := json.Marshal(map[uint32]string{65000:"world"})
  fmt.Println("test4: ", string(test4), err)
  test5, err := json.Marshal(map[bool]string{false:"world"})
  fmt.Println("test5: ", string(test5), err)
  test6, err := json.Marshal(map[interface{}]string{nil:"world"})
  fmt.Println("test6: ", string(test6), err)
}
```

```bash
go run main.go
test1:  {"hello":"world"} <nil>
test2:  {"hello":"world"} <nil>
test3:  {"10":"world"} <nil>
test4:  {"65000":"world"} <nil>
test5:   json: unsupported type: map[bool]string
test6:   json: unsupported type: map[interface {}]string
```
見れば分かる通り、keyに何を入れようがjson形式でのstring型になっているのが分かる。まぁなんか当然だよなといった感じ。
さらに、keyにboolとかinterface{}とか意味がわからないものを入れるとエラーを吐く仕組み。この時点でもう引数はどう考えても`map[string]interaface{}`でしょうが、まぁ一応私も**gopher**を名乗るからにはソースにて確認する。
https://github.com/golang/go/blob/master/src/encoding/json/encode.go

```go
func newMapEncoder(t reflect.Type) encoderFunc {
	switch t.Key().Kind() {
	case reflect.String,
		reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
		reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
	default:
		if !t.Key().Implements(textMarshalerType) {
			return unsupportedTypeEncoder
		}
	}
	me := &mapEncoder{typeEncoder(t.Elem())}
	return me.encode
}
```

```go
type mapEncoder struct {
	elemEnc encoderFunc
}

func (me *mapEncoder) encode(e *encodeState, v reflect.Value, opts encOpts) {
	if v.IsNil() {
		e.WriteString("null")
		return
	}
	e.WriteByte('{')

	// Extract and sort the keys.
	keys := v.MapKeys()
	sv := make([]reflectWithString, len(keys))
	for i, v := range keys {
		sv[i].v = v
		if err := sv[i].resolve(); err != nil {
			e.error(&MarshalerError{v.Type(), err})
		}
	}
	sort.Slice(sv, func(i, j int) bool { return sv[i].s < sv[j].s })

	for i, kv := range sv {
		if i > 0 {
			e.WriteByte(',')
		}
		e.string(kv.s, opts.escapeHTML)
		e.WriteByte(':')
		me.elemEnc(e, v.MapIndex(kv.v), opts)
	}
	e.WriteByte('}')
}
```
まぁ結論`func newMapEncoder(t reflect.Type) encoderFunc`でふるいにかけて、
`func (me *mapEncoder) encode(e *encodeState, v reflect.Value, opts encOpts)`でjson形式の文字列にパースしてるってオチ。

さっきの私が書いたスニペットでエラーが出たやつらは、`t.Key().Kind()`の結果、

```bash
reflect.String,
reflect.Int, 
reflect.Int8, 
reflect.Int16, 
reflect.Int32, 
reflect.Int64,
reflect.Uint, 
reflect.Uint8,
reflect.Uint16, 
reflect.Uint32, 
reflect.Uint64, 
reflect.Uintptr
```
のいずれかの型じゃなかったので、エラーとなったみたいですね。
**(えっuintptr型OKなのかよｗｗ)**
なのでこうなる。

```go
package main

import "fmt"
import "reflect"
import "encoding/json"

func main() {
  var a1 interface{}
  a1 = map[string]string{"hello":"world"}
  b1 := reflect.ValueOf(a1)
  c1 := b1.Type()
  fmt.Println(c1.Key().Kind())  //->string

  var a2 interface{}
  prept := "hello"
  pt := reflect.ValueOf(&prept).Pointer()
  a2  = map[uintptr]string{pt:"world"}
  b2 := reflect.ValueOf(a2)
  c2 := b2.Type()
  fmt.Println(c2.Key().Kind())  //->uintptr
  
  var a3 interface{}
  a3 = map[float64]string{100.0000000000:"world"}
  b3 := reflect.ValueOf(a3)
  c3 := b3.Type()
  fmt.Println(c3.Key().Kind())  //->float64
    
  test1, err := json.Marshal(a1)
  fmt.Println(string(test1), err)  //->{"hello":"world"} <nil>
  
  test2, err := json.Marshal(a2)
  fmt.Println(string(test2), err)  //->{"842350502608":"world"} <nil>
  
  test3, err := json.Marshal(a3)
  fmt.Println(string(test3), err)  //-> json: unsupported type: map[float64]string
}
```
うむ、まぁ間違いなさそう。

###結論、引数は`map[string]interface{}`がオススメです。