golangでのjsonのデコード

golang最近やっているので、いろいろぐぐったり[JSON and Go](http://blog.golang.org/json-and-go)などを読みながらやってみました。

```json
{
  "period": "yy",
  "exec_period": {
    "start": {
      "month": 1,
      "week": 2,
      "day": 3,
      "hour": 4,
      "minute": 5
    },
    "end": {
      "month": 6,
      "week": 7,
      "day": 8,
      "hour": 9,
      "minute": 10
    }
  },
  "backup": [
    {
      "local_dir": "directoryLo1",
      "server_dir":  "directoryLo2",
      "server_host": "domaineName"
    },
    {
      "local_dir": "directoryLo1",
      "server_dir":  "directorySe2",
      "server_host": "domaineName"
    }
  ],
  "incremental_save": "1Y2M"
}
```
こんな感じのjsonがあるとします。

これを読み込むには標準ライブラリの[encoding/json](http://golang.org/pkg/encoding/json/)を使ってこんな感じに書けます。

```go
package main

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

type time struct {
	Month  int
	Week   int
	Day    int
	Hour   int
	Minute int
}

type execPeriod struct {
	Start time
	End   time
}

type directories struct {
	LocalDir   string `json:"local_dir"`
	ServerDir  string `json:"server_dir"`
	ServerHost string `json:"server_host"`
}

type data struct {
	Period          string
	ExecPeriod      execPeriod `json:"exec_period"`
	Backup          []directories
	IncrementalSave string `json:"incremental_save"`
}

func main() {
	dec := json.NewDecoder(os.Stdin)
	var d data
	dec.Decode(&d)
	fmt.Printf("%+v\n", d)
}
```

```bash:result
$ ./json < test.json
{Period:yy ExecPeriod:{Start:{Month:1 Week:2 Day:3 Hour:4 Minute:5} End:{Month:6 Week:7 Day:8 Hour:9 Minute:10}} Backup:[{LocalDir:directoryLo1 ServerDir:directoryLo2 ServerHost:domaineName} {LocalDir:directoryLo1 ServerDir:directorySe2 ServerHost:domaineName}] IncrementalSave:1Y2M}
```

json側と同じようにstructを定義して、json.Decoderに渡してあげればおｋっぽいです。ただしkeyの名前がjson側と違う場合は、`json:"key_name"`みたいにして違いを吸収してあげる必要があります。


[JSON and Go](http://blog.golang.org/json-and-go)の後ろの方に、jsonの構造がわからない場合のやり方も載っていて、interfaceを使いなよ！的なことが書いてあります。interfaceよくわかってないので調べつつやったのが以下です。

```go
package main

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

func assert(data interface{}) {
	switch data.(type) {
	case string:
		fmt.Print(data.(string))
	case float64:
		fmt.Print(data.(float64))
	case bool:
		fmt.Print(data.(bool))
	case nil:
		fmt.Print("null")
	case []interface{}:
		fmt.Print("[")
		for _, v := range data.([]interface{}) {
			assert(v)
			fmt.Print(" ")
		}
		fmt.Print("]")
	case map[string]interface{}:
		fmt.Print("{")
		for k, v := range data.(map[string]interface{}) {
			fmt.Print(k + ":")
			assert(v)
			fmt.Print(" ")
		}
		fmt.Print("}")
	default:
	}
}

func main() {
	var data interface{}
	dec := json.NewDecoder(os.Stdin)
	dec.Decode(&data)
	assert(data)
	fmt.Println()
}
```

```bash:result
$ ./json < test.json
{period:yy exec_period:{start:{month:1 week:2 day:3 hour:4 minute:5 } end:{month:6 week:7 day:8 hour:9 minute:10 } } backup:[{local_dir:directoryLo1 server_dir:directoryLo2 server_host:domaineName } {local_dir:directoryLo1 server_dir:directorySe2 server_host:domaineName } ] incremental_save:1Y2M nulltest:null }
```

interfaceすげー
[Effective Go](http://golang.org/doc/effective_go.html)のType switchの例まんまなのですが、型アサーションの結果によって処理を振り分けることができます。[ここの説明](http://golang.org/pkg/encoding/json/#Unmarshal)によるとDecode結果は、bool、float64、string、[]interface{}、map[string]interface{}、nilのどれかになるはずなので、一旦interface{}で受けてから、それぞれに対応した処理に振り分けます。

実際使うときはstruct定義した方があとあと使いやすいのでいい気がしますが、golangのinterfaceはこんなの以外にもいろいろ出来て面白そうですね。