使用Golang进行JSON解码

最近我在学习Golang,一边通过搜索了解相关知识,一边尝试阅读JSON和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)
}
$ ./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一样定义结构体,并将其传递给json.Decoder,就可以了解情况。但是,如果键的名称与json不同,则需要用json:”key_name”这样的方式来弥补差异。

在JSON和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()
}
$ ./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 }

界面太厉害了。
该示例与《Effective Go》中的类型切换完全一样,可以根据类型断言的结果来分配处理。根据这里的解释,Decode的结果应该是bool、float64、string、[]interface{}、map[string]interface{}、nil中的一种,因此先用interface{}接收,然后根据各自的情况来进行相应的处理。

实际使用时,我觉得定义struct会更方便后续使用,但是golang的interface除了这个,还有很多其他的功能,看起来很有趣。