2015-10-15 15 views
8

Questo codice:Come stampare struct con String() dei campi?

type A struct { 
    t time.Time 
} 

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

stampe:

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

A non implementa String(), quindi non è un fmt.Stringer e stampa la sua rappresentazione nativa. Ma è molto noioso implementare String() per ogni singola struttura che voglio stampare. Peggio ancora, devo aggiornare i String() se aggiungo o rimuovo alcuni campi. C'è un modo più semplice per stampare una struttura, con i suoi campi 'String() s?

risposta

4

Questo è il modo in cui il pacchetto fmt è implementato, quindi non è possibile cambiarlo.

Ma è possibile scrivere una funzione di supporto che utilizza la riflessione (pacchetto) per iterare sui campi di una struttura e può chiamare il metodo String() sui campi se hanno un tale metodo. implementazione

Esempio:

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

Ora, quando si desidera stampare un struct, si può fare:

fmt.Println(PrintStruct(a, true)) 

Si può anche scegliere di aggiungere un metodo String() alla tua struct che ha appena deve chiama la nostra funzione PrintStruct():

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

Ogni volta che si modifica la struttura, non è necessario fare nulla con il metodo String() poiché utilizza la riflessione per camminare dinamicamente su tutti i campi.

Note:

Dal momento che stiamo utilizzando la riflessione, è necessario esportare il campo t time.Time per far funzionare tutto (anche aggiunto alcuni campi aggiuntivi per scopi di test):

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

uscita (provate sul Go Playground):

0.123.
{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

Questo è esattamente quello che cerco di evitare. Voglio usare 'time.Time''s' String() ', invece di esporre la sua implementazione interna. –

+0

@ LaiYu-Hsuan Hai ragione. Vedi la risposta modificata. – icza

Problemi correlati