Как распечатать struct со строкой () полей?

этот код:

type A struct {
    t time.Time
}

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

принты:

{{63393490800 0 0x206da0}}
2009-11-10 23:00:00 +0000 UTC

A не выполнять String(), Так что это не fmt.Stringer и печатает свое собственное представление. Но очень утомительно реализовывать String() для каждой структуры, которую я хочу напечатать. Хуже того, я должен обновить String()s, Если я добавляю или удаляю некоторые поля. Есть ли более простой способ распечатать структуру с ее полями'String()s?

1 ответов


это как fmt пакет реализован, поэтому вы не можете изменить это.

но вы можете написать вспомогательную функцию, которая использует отражение (reflect package) для итерации по полям структуры и может вызывать String() метод на полях, Если у них есть такой метод.

пример реализации:

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()
}

теперь, когда вы хотите напечатать struct, вы можете сделать:

fmt.Println(PrintStruct(a, true))

вы также можете добавить String() метод для вашей структуры, которая просто должна вызвать наш :

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

всякий раз, когда вы меняете свою структуру, вам не нужно ничего делать с вашим String() метод, поскольку он использует отражение для динамического обхода всех полей.

Примечания:

так как мы используем отражение, вы должны экспортировать t time.Time поле для этой работы (также добавили несколько дополнительных полей для целей тестирования):

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

тестирование это:

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))

вывод (попробуйте на 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