在Go中递归打印地图中的所有值

Suppose I have a map of this type:

var results map[string]interface{}

The values can be anything, even another map. How would I print all the values? If the value is an array, I want to print each item in the array individually. If it is another map, I want to recursively call the same function on the map.

I shamelessly ripped this from a site some time ago:

import (
    "fmt"
    "reflect"
    "strings"
)

/*
InspectStruct prints the guts of an instantiated struct. Very handy for debugging
usage: InspectStruct(req, 0) -> prints all children
*/

func InspectStructV(val reflect.Value, level int) {
    if val.Kind() == reflect.Interface && !val.IsNil() {
        elm := val.Elem()
        if elm.Kind() == reflect.Ptr && !elm.IsNil() && elm.Elem().Kind() == reflect.Ptr {
            val = elm
        }
    }
    if val.Kind() == reflect.Ptr {
        val = val.Elem()
    }

    for i := 0; i < val.NumField(); i++ {
        valueField := val.Field(i)
        typeField := val.Type().Field(i)
        address := "not-addressable"

        if valueField.Kind() == reflect.Interface && !valueField.IsNil() {
            elm := valueField.Elem()
            if elm.Kind() == reflect.Ptr && !elm.IsNil() && elm.Elem().Kind() == reflect.Ptr {
                valueField = elm
            }
        }

        if valueField.Kind() == reflect.Ptr {
            valueField = valueField.Elem()

        }
        if valueField.CanAddr() {
            address = fmt.Sprintf("0x%X", valueField.Addr().Pointer())
        }

        fmt.Printf("%vField Name: %s,\t Field Value: %v,\t Address: %v\t, Field type: %v\t, Field kind: %v
",
            strings.Repeat("\t", level),
            typeField.Name,
            //valueField.Interface(),
            address,
            typeField.Type,
            valueField.Kind())

        if valueField.Kind() == reflect.Struct {
            InspectStructV(valueField, level+1)
        }
    }
}

func InspectStruct(v interface{}, level int) {
    InspectStructV(reflect.ValueOf(v), level)
}

Using %v on fmt.Printf will traverse the inner maps and print the elements too by calling String() on each. Now if you want to change this default printing on an inner type you just have to implement String() on that type. in the following example i created a version of []int that prints one int per line.

package main

import (
    "fmt"
    "strings"
)

type OnePerLineInt []int

func (ip OnePerLineInt) String() string {
    var ret []string
    for _, a := range ip {
        ret = append(ret, fmt.Sprintf("Item: %v
", a))
    }
    return strings.Join(ret, "")
}

func main() {
    arr := &OnePerLineInt{1, 2, 3}
    arr1 := []int{4, 5 , 6}
    foo := make(map[string]interface{})
    bar := make(map[string]interface{})
    bar["a"] = "foobar1"
    bar["b"] = "foobar2"
    foo["1"] = arr
    foo["2"] = bar
    foo["3"] = arr1
    fmt.Printf("foo contents: %v
", foo)
    fmt.Printf("bar is :%v
", bar)
}

prints:

foo contents: map[1:Item: 1
Item: 2
Item: 3
2:map[a:foobar1 b:foobar2] 3:[4 5 6]]
bar is :map[a:foobar1 b:foobar2]

http://play.golang.org/p/kc6xm7dHwx