How to print struct with String() of fields?

后端 未结 1 1691
既然无缘
既然无缘 2020-12-11 17:02

This code:

type A struct {
    t time.Time
}

func main() {
    a := A{time.Now()}
    fmt.Println(a)
    fmt.Println(a.t)
}

prints:

<
相关标签:
1条回答
  • 2020-12-11 17:49

    This is how the fmt package is implemented, so you can't change that.

    But you can write a helper function which uses reflection (reflect package) to iterate over the fields of a struct, and can call the String() method on the fields if they have such a method.

    Example implementation:

    func PrintStruct(s interface{}, names bool) string {
        v := reflect.ValueOf(s)
        t := v.Type()
        // To avoid panic if s is not a struct:
        if t.Kind() != reflect.Struct {
            return fmt.Sprint(s)
        }
    
        b := &bytes.Buffer{}
        b.WriteString("{")
        for i := 0; i < v.NumField(); i++ {
            if i > 0 {
                b.WriteString(" ")
            }
            v2 := v.Field(i)
            if names {
                b.WriteString(t.Field(i).Name)
                b.WriteString(":")
            }
            if v2.CanInterface() {
                if st, ok := v2.Interface().(fmt.Stringer); ok {
                    b.WriteString(st.String())
                    continue
                }
            }
            fmt.Fprint(b, v2)
        }
        b.WriteString("}")
        return b.String()
    }
    

    Now when you want to print a struct, you can do:

    fmt.Println(PrintStruct(a, true))
    

    You may also choose to add a String() method to your struct which just has to call our PrintStruct() function:

    func (a A) String() string {
        return PrintStruct(a, true)
    }
    

    Whenever you change your struct, you don't have to do anything with your String() method as it uses reflection to dynamically walk over all the fields.

    Notes:

    Since we're using reflection, you have to export the t time.Time field for this to work (also added a few extra fields for testing purposes):

    type A struct {
        T          time.Time
        I          int
        unexported string
    }
    

    Testing it:

    a := A{time.Now(), 2, "hi!"}
    fmt.Println(a)
    fmt.Println(PrintStruct(a, true))
    fmt.Println(PrintStruct(a, false))
    fmt.Println(PrintStruct("I'm not a struct", true))
    

    Output (try it on the Go Playground):

    {T:2009-11-10 23:00:00 +0000 UTC I:2 unexported:hi!}
    {T:2009-11-10 23:00:00 +0000 UTC I:2 unexported:hi!}
    {2009-11-10 23:00:00 +0000 UTC 2 hi!}
    I'm not a struct
    
    0 讨论(0)
提交回复
热议问题