简体   繁体   中英

golang how to print struct value with pointer

package main

import "fmt"

type A struct {
    a int32
    B *B
}
type B struct {
    b int32
}

func main() {
    a := &A{
        a: 1,
        B: &B{
            b: 2,
        },
    }
    fmt.Printf("v ==== %+v \n", a)
}


//ret: v ==== &{a:1 B:0xc42000e204}
//??? how to print B's content but not pointer

Basically, you have to do it yourself. There are two ways to do this. Either just print the thing how you want, or implement the Stringer interface for the struct by adding a func String() string , which gets called when you use the format %v . You could also reference each value in the format which is a struct.

Implementing the Stringer interface is the surest way to always get what you want. And you only have to do it once per struct, instead of per format string when you print.

https://play.golang.org/p/PKLcPFCqOe

package main

import "fmt"

type A struct {
    a int32
    B *B
}

type B struct{ b int32 }

func (aa *A) String() string {
    return fmt.Sprintf("A{a:%d, B:%v}",aa.a,aa.B)
}

func (bb *B) String() string {
    return fmt.Sprintf("B{b:%d}",bb.b)
}

func main() {
    a := &A{a: 1, B: &B{b: 2}}

    // using the Stringer interface
    fmt.Printf("v ==== %v \n", a)

    // or just print it yourself however you want.
    fmt.Printf("v ==== A{a:%d, B:B{b:%d}}\n", a.a, a.B.b)

    // or just reference the values in the struct that are structs themselves
    // but this can get really deep
    fmt.Printf("v ==== A{a:%d, B:%v}", a.a, a.B)
}

When you get into larger structs, it becomes a pain to write a bunch of custom String functions. Goconvey currently uses the following project to show diffs and expected/actual output on structs of any size: https://github.com/luci/go-render/blob/master/render/render.go#L51 . It includes displaying pointer values.

If you need the output to be re-usable as code (like fmt.Printf("%#v", a) but include pointer values), I have a forked version of the above project which will render full nested pointers as re-usable code:

package main

import (
    "fmt"
    "github.com/gdexlab/go-render/render"
)

type A struct {
    a int32
    B *B
}
type B struct {
    b int32
}

func main() {
    a := &A{
        a: 1,
        B: &B{
            b: 2,
        },
    }
    output := render.AsCode(a)
    fmt.Println(output)

}
// outputs: "&A{a:1, B:&B{b:2}}" compared to initial version of "&{a:1 B:0xc42000e204}"

Go Playground Example: https://play.golang.org/p/tcfJYb0NnVf

Another simple solution is to print the struct using marshaling. This works only for exported (public) variables by capitalizing the first char inside the struct.

package main

import (
    "fmt"
    "gopkg.in/yaml.v2"
    "encoding/json"
)

type A struct {
    Aa int32
    B *B
}
type B struct {
    Bb int32
}
func main() {
    a := &A{
        Aa: 1,
        B: &B{
            Bb: 2,
        },
    }
    aJSON, _ := json.Marshal(a)
    fmt.Printf("JSON Print - \n%s\n", string(aJSON))


    aYAML, _ := yaml.Marshal(a)
    fmt.Printf("YAML Print - \n%s\n", string(aYAML))
}

Output :-

JSON Print - 
{"Aa":1,"B":{"Bb":2}}
YAML Print - 
aa: 1
b:
  bb: 2

If you are printing the struct multiple times then implement Stringer interface as follows :-

package main

import (
    "fmt"
    "gopkg.in/yaml.v2"
)

type A struct {
    Aa int32
    B *B
}

func (a A) String() string {
    bytes, _ := yaml.Marshal(a)
    return string(bytes)
}

type B struct {
    Bb int32
}

func main() {
    a := &A{
        Aa: 1,
        B: &B{
            Bb: 2,
        },
    }

    fmt.Printf("YAML Print - \n%+v\n", a)
}

Output -

YAML Print - 
aa: 1
b:
  bb: 2

Use reflect.

package main

import (
    "fmt"
    "reflect"
)

type A struct {
    a int32
    B *B
}
type B struct {
    b int32
}

func main() {
    a := &A{
        a: 1,
        B: &B{
            b: 2,
        },
    }
    fmt.Printf("%s\n", ReflectSprint(a)) // output: &A{a: 1, B: &B{b: 2}}
}

func ReflectSprint(v interface{}) string {
    return reflectSprint(reflect.ValueOf(v))
}
func reflectSprint(v reflect.Value) string {
    vt := v.Type()
    switch vt.Kind() {
    case reflect.Struct:
        out := vt.Name() + "{"
        for i := 0; i < v.NumField(); i++ {
            if i > 0 {
                out += ", "
            }
            fieldValue := v.Field(i)
            fieldName := vt.Field(i).Name
            out += fmt.Sprintf("%s: %s", fieldName, reflectSprint(fieldValue))
        }
        out += "}"
        return out
    case reflect.Interface:
        if v.IsZero() {
            return "nil"
        }
        return "&" + reflectSprint(v.Elem())
    case reflect.Ptr:
        if v.IsZero() {
            return "nil"
        }
        return "&" + reflectSprint(v.Elem())
    default:
        return fmt.Sprintf("%#v", v)
    }
}

Implement Stringer interface to custom type

  • no external packages
  • no need to wrap types in other types

example:

package main

import "fmt"

type A struct {
    B *B `json:"b"`
}

type B int

func (b *B) String() string {
    if b == nil {
        return "nil"
    }

    return fmt.Sprintf("%d", *b)
}

func main() {
    var a A
    fmt.Printf("a: %+v\n", a)
    a.B = B(3)
    fmt.Printf("a: %+v\n", a)
}

output:

a: {B:nil}
a: {B:3}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM