Goで双方向リストをJSONに変換する際のつまずき

# 何があったか
Goでコーディング中、双方向リストっぽいものを自作＆JSONに変換する必要性があった。方針としては `encoding/json` を使っていくつもりが、ごちゃごちゃ詰まってしまった、という話。

# 要するに
* structに自分自身がいるときはちゃんとポインタにしましょう
* JSONに変換するとき、そいつが循環する可能性があるなら、`json:"-"` で無視させて、阻止しておこうね

# structに自分自身がいると invalid recursive type

当たり前っちゃ当たり前の話ですが。

## 失敗編

```golang
package main

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

type Person struct {
	Name   string
	Parent Person
	Child  Person
}

func main() {
	sora := Person{}
	sora.Name = "Sora Amamiya"
	j, err := json.Marshal(sora)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(string(j))
}
```

こう怒られます。

```console
$ go run main.go
# command-line-arguments
./main.go:9:6: invalid recursive type Person
```

そりゃそうじゃ、という感じ。相互再帰でも怒ってくれるらしいです。優秀。

## 成功編

ポインタに直して解決。

```golang
package main

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

type Person struct {
	Name   string
	Parent *Person
	Child  *Person
}

func main() {
	sora := Person{}
	sora.Name = "Sora Amamiya"
	j, err := json.Marshal(sora)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(string(j))
}
```
```
$ go run main.go
{"Name":"Sora Amamiya","Parent":null,"Child":null}
```

# JSON循環阻止

本題です。
そもそも双方向リストをJSONに変えたいと思うことがどれだけあるのかが疑問ではありますが。

`Person` の構造は先程と同じです。こんな感じに小さめの双方向リストを組みます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/191019/69ad2fd5-1adc-5cea-7057-2003c6b7b12e.png)


## 失敗編

```golang
package main

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

type Person struct {
	Name   string  `json:"name"`
	Parent *Person 
	Child  *Person `json:"child"`
}


func main() {
	m := Person{}
	n := Person{}
	m.Name = "Momo Asakura"
	m.Child = &n
	n.Name = "Shiina Natsukawa"
	n.Parent = &m

	j, err := json.Marshal([]Person{m, n})
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(string(j))
}
```

出力はこんな感じで文字通り無限に怒られます。再帰エラーですね。

```
runtime: goroutine stack exceeds 1000000000-byte limit
fatal error: stack overflow

runtime stack:
runtime.throw(0x4f30b2, 0xe)
        /usr/local/go/src/runtime/panic.go:774 +0x72
runtime.newstack()
        /usr/local/go/src/runtime/stack.go:1046 +0x6e9
runtime.morestack()
        /usr/local/go/src/runtime/asm_amd64.s:449 +0x8f

goroutine 1 [running]:
encoding/json.stringEncoder(0xc00007c000, 0x4c8f80, 0xc00000c080, 0x198, 0x100)
        /usr/local/go/src/encoding/json/encode.go:589 +0x389 fp=0xc02009a380 sp=0xc02009a378 pc=0x4a94c9
encoding/json.structEncoder.encode(0xc000082000, 0x3, 0x4, 0xc00005e2a0, 0xc00007c000, 0x4dcee0, 0xc00000c
```

そりゃまあ当たり前ですね。「ParentにJSONタグつけなければワンチャン無視してくれないかな？」とかいう浅はかな考えをしたのがダメ。デフォルトでフィールド名をそのまま使って変換してくれます。非常に優秀なライブラリ。

## 成功編

つまり私がやりたかったことは「JSONライブラリで任意のフィールドを無視する」ということ。
そのためにはタグを `json:"-"` とします。

```golang
package main

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

type Person struct {
	Name   string  `json:"name"`
	Parent *Person `json:"-"`
	Child  *Person `json:"child"`
}

func main() {
	m := Person{}
	n := Person{}
	m.Name = "Momo Asakura"
	m.Child = &n
	n.Name = "Shiina Natsukawa"
	n.Parent = &m

	j, err := json.Marshal([]Person{m, n})
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(string(j))
}
```

出力結果はこちら。ちゃんとChildだけ持ってってくれてます。Rootから引っ張るならParentは別に出す必要ないですからね。

```
$ go run main.go
[{"name":"Momo Asakura","child":{"name":"Shiina Natsukawa","child":null}},{"name":"Shiina Natsukawa","chil
d":null}]
```

# まとめ
* Go、書きやすくて素敵。
* JSONに変えやすくて素敵。
* ポインタ意識しなくて良い言語を使ってる人だけはポインタ学ぶなり思い出すなりしようね。
* みんなGoやろう、楽しいよ（初心者並感）。