Golang实践录:反射reflect的一些研究及代码汇总

369 阅读3分钟

这是我参与11月更文挑战的第12天,活动详情查看:2021最后一次更文挑战

本文汇总一些工程中使用到的和 reflect 有关的代码示例。由于是代码片段,不一定保证完整。但其思想可以参考。

解析json

本节介绍解析json的一些方法。json 可来自文件,也可来自字符串,无论哪种,都是先转换成[]byte,再调用json.Unmarshal解析。 有时候并不关注完整的 json 格式,因为完整的 json 数据可能非常多,而我们只需其中一个字段或部分字段,因此,可使用map[string]interface{}来接收解析结果(注意如果 json 本身是数组,则 map 必须使用数组形式)。

从json文件解析其字段

首先打开文件,再读取其内容,再调用json.Unmarshal即可。

    file, err := os.Open(filename)
    if err != nil {
        fmt.Printf("open file %s error: %s\n", filename, err)
        return
    }
    defer file.Close()
​
    read := io.Reader(file)
​
    data, _ := ioutil.ReadAll(read)
    var v []map[string]interface{}
    err = json.Unmarshal(data, &v)
    if err != nil {
        fmt.Printf("Unmarshal error: %s\n", err)
        return
    }
    
    // 假定filename里面有很多的json数组,一一遍历
    for _, idx := range v {
        fmt.Println(idx["foo"])
        ...
    }

从json字符串解析

一个简单的示例,从字符串组装,到解析,到提取其中某个字段。

func TestJsonSimple(t *testing.T) {
// 原始json字符串不能格式化,必须转换成[]byte
orgJsonString :=
`{"enID":"ID250","exID":"ID251","type":1,"money":250.44,"distance":274050}`
​
    // 虽然不知道具体结构体,但知道json只有一个,不是数组
    var data map[string]interface{}
    // 解析
    err := json.Unmarshal([]byte(orgJsonString), &data)
    if err != nil {
        fmt.Println(err.Error())
        return
    }
    fmt.Printf("%#v\n", data);
    fmt.Println(data["money"]) // 并不关心其它字段,只抽取所需的
    fmt.Println(data["money1"]) // 不存在,返回nil
}

输出如下内容:

map[string]interface {}{"distance":274050, "enID":"ID250", "exID":"ID251", "money":250.44, "type":1}
250.44
<nil>

如果 json 中有嵌套的数组,获取数组内容代码如下:

func showInterface(origin interface{}) {
    switch reflect.TypeOf(origin).Kind() {
    case reflect.Slice, reflect.Array:
        s := reflect.ValueOf(origin)
        for i := 0; i < s.Len(); i++ {
            fmt.Printf("%d: %v\n", i, s.Index(i))
        }
    case reflect.String:
        s := reflect.ValueOf(origin)
        fmt.Printf("only string %v\n", s.String())
    case reflect.Int:
        s := reflect.ValueOf(origin)
        fmt.Printf("only int %v\n", s.Int())
    }
}
​
func TestJsonArray(t *testing.T) {
    orgJsonString :=
`{"enID":"ID500","exID":"ID501","type":2,"money":27.00,"distance":28322,"splitInfo":[{"index":1,"pTag":"11","pMoney":700},{"index":2,"pTag":"12","pMoney":2000}]}`
​
    // 解析
    var data map[string]interface{}
    err := json.Unmarshal([]byte(orgJsonString), &data)
    if err != nil {
        fmt.Println(err.Error())
        return
    }
    fmt.Printf("org json:\n%#v\n\n", data);
    showInterface(data["enID"])
    showInterface(data["splitInfo"])
}

data["splitInfo"]是一个interface,此刻并不知道具体的内容,因此无法再获取内部的字段或长度。可以利用reflect.TypeOf获取类型,如是数组,需要遍历。详见代码。 输出内容如下:

org json:
map[string]interface {}{"distance":28322, "enID":"ID500", "exID":"ID501", "money":27, "splitInfo":[]interface {}{map[string]interface {}{"index":1, "pMoney":700, "pTag":"11"}, map[string]interface {}{"index":2, "pMoney":2000, "pTag":"12"}}, "type":2}
​
only string ID500
0: map[index:1 pMoney:700 pTag:11]
1: map[index:2 pMoney:2000 pTag:12]
​

接上,如果只需要获取data["splitInfo"]的长度。注意,此处已经知道了该字段是数组,只是不知道其内容罢了(或不关注内容),那么可以直接用reflect.ValueOf求出长度,如下:

    mylen := reflect.ValueOf(data["splitInfo"]).Len()
    fmt.Println("splitInfo len: ", mylen)

更多示例,待后续研究。