這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
今天嘗試了一下使用go語言中的反射來將struct類型轉換成xml,結果相當糾結。首先去看了一下go的reflect包的實現,根據go的規則,首先應該去看一個NewXXX的方法,結果發現了一個叫NewValue的方法,通過這個方法我們能夠得到一個Value介面。另外我們還應該注意到,go的反映實現中將Type和Value分開了,於是還有另外一個介面Type.
type Value interface { // Type returns the value's type. Type() Type // Interface returns the value as an interface{}. Interface() interface{} // CanSet returns whether the value can be changed. // Values obtained by the use of non-exported struct fields // can be used in Get but not Set. // If CanSet() returns false, calling the type-specific Set // will cause a crash. CanSet() bool // SetValue assigns v to the value; v must have the same type as the value. SetValue(v Value) // Addr returns a pointer to the underlying data. // It is for advanced clients that also // import the "unsafe" package. Addr() uintptr // Method returns a FuncValue corresponding to the value's i'th method. // The arguments to a Call on the returned FuncValue // should not include a receiver; the FuncValue will use // the value as the receiver. Method(i int) *FuncValue // contains unexported methods}type Type interface { // PkgPath returns the type's package path. // The package path is a full package import path like "container/vector". // PkgPath returns an empty string for unnamed types. PkgPath() string // Name returns the type's name within its package. // Name returns an empty string for unnamed types. Name() string // String returns a string representation of the type. // The string representation may use shortened package names // (e.g., vector instead of "container/vector") and is not // guaranteed to be unique among types. To test for equality, // compare the Types directly. String() string // Size returns the number of bytes needed to store // a value of the given type; it is analogous to unsafe.Sizeof. Size() uintptr // Bits returns the size of the type in bits. // It is intended for use with numeric types and may overflow // when used for composite types. Bits() int // Align returns the alignment of a value of this type // when allocated in memory. Align() int // FieldAlign returns the alignment of a value of this type // when used as a field in a struct. FieldAlign() int // Kind returns the specific kind of this type. Kind() Kind // For non-interface types, Method returns the i'th method with receiver T. // For interface types, Method returns the i'th method in the interface. // NumMethod returns the number of such methods. Method(int) Method NumMethod() int // contains unexported methods}
然後包裡還根據每種資料類型都定義了相應的XXXType和XXXValue結構體,那麼我們如何得到這些結構體的執行個體呢?因為只有得到這些構體的執行個體才能利用反映來操縱這些資料。於是很醜陋的地方來了,你需要使用.(type)來得到當前這個變數的類型,而這個使用必須在switch中,請看下面的例子
func test(data interface{}) { switch rvalue := reflect.NewValue(data).(type) { default: println(reflect.Typeof(rvalue).String()) } switch rtype := reflect.Typeof(data).(type) { default: println(reflect.Typeof(rtype).String()) } switch rvalue := data.(type) { default: println(reflect.Typeof(rvalue).String()) } }
NewValue建立了一個data的Value介面,但是這個介面是誰實現的呢?通過.(type)就可以得到。
Typeof建立了一個data的Type介面,同樣的這個介面也可以通過.(type)得到它的實現。
而如果不使用reflect裡的任何方法直接對變數data使用.(type),那麼就會得到這個介面的實現。
因此從這裡我們可以看到go語言中使用反映需要兩個條件,一個是通過type得到它的實現變數,一個是通過reflect裡提供的介面來進行操作。
但是很噁心的是,每次操作都必須通過switch case來執行,比如最後My Code就變成了如下這個樣子:
func asString(val interface{}) string {switch value := reflect.NewValue(val).(type) {case *reflect.StringValue:return value.Get()case *reflect.BoolValue:return strconv.Btoa(value.Get())case *reflect.IntValue:return strconv.Itoa64(value.Get())default:panic("invalid type for xml generator")}return ""}func toXml(val interface{}, writer io.Writer) {switch value := reflect.NewValue(val).(type) {case *reflect.StructValue:writer.Write([]byte("<"))writer.Write([]byte(reflect.Typeof(val).Name()))switch vtype := reflect.NewValue(val).Type().(type) {case *reflect.StructType://compose attributefor i := 0; i < value.NumField(); i++ {field := vtype.Field(i)if field.Tag != "attr" {continue}writer.Write([]byte(" "))writer.Write([]byte(field.Name))writer.Write([]byte("=/""))writer.Write([]byte(asString(value.Field(i).Interface())))writer.Write([]byte("/""))}writer.Write([]byte(">"))//compose childrenfor i := 0; i < value.NumField(); i++ {field := vtype.Field(i)if field.Tag == "attr" {continue}switch child := value.Field(i).(type) {case *reflect.StructValue:toXml(child.Interface(), writer)case *reflect.ArrayValue:case *reflect.SliceValue:writer.Write([]byte("<"))writer.Write([]byte(field.Name))writer.Write([]byte(">"))for i := 0; i < child.Len(); i++ {switch elem := child.Elem(i).(type) {case *reflect.StructValue:toXml(elem.Interface(), writer)default:panic("invalid type for array, only struct supported")}}writer.Write([]byte(""))default:writer.Write([]byte(asString(child.Interface())))}}//compose endwriter.Write([]byte(""))default:panic("invalid type: only array, slice, struct and primitive type supported")}default:panic("invalid type, only struct supported")}}