66 "errors"
77 "fmt"
88 "io"
9+ "reflect"
910 "sort"
1011 "strings"
1112
@@ -102,11 +103,14 @@ func (e *exportFormat) Fields() []string {
102103 return e .fields
103104}
104105
106+ // Write serializes data into JSON output written to w. If the object passed as data implements exportable,
107+ // or if data is a map or slice of exportable object, ExportData() will be called on each object to obtain
108+ // raw data for serialization.
105109func (e * exportFormat ) Write (w io.Writer , data interface {}, colorEnabled bool ) error {
106110 buf := bytes.Buffer {}
107111 encoder := json .NewEncoder (& buf )
108112 encoder .SetEscapeHTML (false )
109- if err := encoder .Encode (data ); err != nil {
113+ if err := encoder .Encode (e . exportData ( reflect . ValueOf ( data )) ); err != nil {
110114 return err
111115 }
112116
@@ -121,3 +125,44 @@ func (e *exportFormat) Write(w io.Writer, data interface{}, colorEnabled bool) e
121125 _ , err := io .Copy (w , & buf )
122126 return err
123127}
128+
129+ func (e * exportFormat ) exportData (v reflect.Value ) interface {} {
130+ switch v .Kind () {
131+ case reflect .Ptr , reflect .Interface :
132+ if ! v .IsNil () {
133+ return e .exportData (v .Elem ())
134+ }
135+ case reflect .Slice :
136+ a := make ([]interface {}, v .Len ())
137+ for i := 0 ; i < v .Len (); i ++ {
138+ a [i ] = e .exportData (v .Index (i ))
139+ }
140+ return a
141+ case reflect .Map :
142+ t := reflect .MapOf (v .Type ().Key (), emptyInterfaceType )
143+ m := reflect .MakeMapWithSize (t , v .Len ())
144+ iter := v .MapRange ()
145+ for iter .Next () {
146+ ve := reflect .ValueOf (e .exportData (iter .Value ()))
147+ m .SetMapIndex (iter .Key (), ve )
148+ }
149+ return m .Interface ()
150+ case reflect .Struct :
151+ if v .CanAddr () && reflect .PtrTo (v .Type ()).Implements (exportableType ) {
152+ ve := v .Addr ().Interface ().(exportable )
153+ return ve .ExportData (e .fields )
154+ } else if v .Type ().Implements (exportableType ) {
155+ ve := v .Interface ().(exportable )
156+ return ve .ExportData (e .fields )
157+ }
158+ }
159+ return v .Interface ()
160+ }
161+
162+ type exportable interface {
163+ ExportData ([]string ) * map [string ]interface {}
164+ }
165+
166+ var exportableType = reflect .TypeOf ((* exportable )(nil )).Elem ()
167+ var sliceOfEmptyInterface []interface {}
168+ var emptyInterfaceType = reflect .TypeOf (sliceOfEmptyInterface ).Elem ()
0 commit comments