LoginSignup
159
159

More than 5 years have passed since last update.

golangでのjsonのデコード

Posted at

golang最近やっているので、いろいろぐぐったりJSON and Goなどを読みながらやってみました。

{
  "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を使ってこんな感じに書けます。

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)
}
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に渡してあげればおkっぽいです。ただしkeyの名前がjson側と違う場合は、json:"key_name"みたいにして違いを吸収してあげる必要があります。

JSON and Goの後ろの方に、jsonの構造がわからない場合のやり方も載っていて、interfaceを使いなよ!的なことが書いてあります。interfaceよくわかってないので調べつつやったのが以下です。

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()
}
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のType switchの例まんまなのですが、型アサーションの結果によって処理を振り分けることができます。ここの説明によるとDecode結果は、bool、float64、string、[]interface{}、map[string]interface{}、nilのどれかになるはずなので、一旦interface{}で受けてから、それぞれに対応した処理に振り分けます。

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

159
159
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
159
159