今天尝试了一下使用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来执行,比如最后我的代码就变成了如下这个样子:
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 attribute
for 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 children
for 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(""))
writer.Write([]byte(field.Name))
writer.Write([]byte(">"))
default:
writer.Write([]byte(asString(child.Interface())))
}
}
//compose end
writer.Write([]byte(""))
writer.Write([]byte(reflect.Typeof(val).Name()))
writer.Write([]byte(">"))
default:
panic("invalid type: only array, slice, struct and primitive type supported")
}
default:
panic("invalid type, only struct supported")
}
}
有疑问加站长微信联系(非本文作者)