Saya mencoba membuat metode umum di Go yang akan mengisi struct
menggunakan data dari a map[string]interface{}
. Misalnya, tanda tangan metode dan penggunaannya mungkin terlihat seperti:
func FillStruct(data map[string]interface{}, result interface{}) {
...
}
type MyStruct struct {
Name string
Age int64
}
myData := make(map[string]interface{})
myData["Name"] = "Tony"
myData["Age"] = 23
result := &MyStruct{}
FillStruct(myData, result)
// result now has Name set to "Tony" and Age set to 23
Saya tahu ini bisa dilakukan dengan menggunakan JSON sebagai perantara; apakah ada cara lain yang lebih efisien untuk melakukan ini?
encoding/json
paket stdlib untuk melakukan langkah menengah itu .. Dapatkah Anda memberikan contoh peta dan contoh struct dimana metode ini dapat digunakan?unsafe
paketnya .. tapi aku tidak berani mencobanya. Selain itu .. Diperlukan refleksi, karena Anda harus dapat mengkueri metadata yang terkait dengan suatu tipe untuk menempatkan data ke propertinya. Ini akan sangat mudah untuk membungkus ini dalamjson.Marshal
+json.Decode
panggilan .. tapi itu menggandakan refleksi.Jawaban:
Cara termudah adalah menggunakan https://github.com/mitchellh/mapstructure
import "github.com/mitchellh/mapstructure" mapstructure.Decode(myData, &result)
Jika Anda ingin melakukannya sendiri, Anda dapat melakukan sesuatu seperti ini:
http://play.golang.org/p/tN8mxT_V9h
func SetField(obj interface{}, name string, value interface{}) error { structValue := reflect.ValueOf(obj).Elem() structFieldValue := structValue.FieldByName(name) if !structFieldValue.IsValid() { return fmt.Errorf("No such field: %s in obj", name) } if !structFieldValue.CanSet() { return fmt.Errorf("Cannot set %s field value", name) } structFieldType := structFieldValue.Type() val := reflect.ValueOf(value) if structFieldType != val.Type() { return errors.New("Provided value type didn't match obj field type") } structFieldValue.Set(val) return nil } type MyStruct struct { Name string Age int64 } func (s *MyStruct) FillStruct(m map[string]interface{}) error { for k, v := range m { err := SetField(s, k, v) if err != nil { return err } } return nil } func main() { myData := make(map[string]interface{}) myData["Name"] = "Tony" myData["Age"] = int64(23) result := &MyStruct{} err := result.FillStruct(myData) if err != nil { fmt.Println(err) } fmt.Println(result) }
sumber
func (s MyStr...) FillStruct ...
untuk setiap orang. Apakah mungkin untuk mendefinisikan FillStruct untuk sebuah struktur dasar kemudian memiliki semua struct saya yang lain 'mewarisi' perilaku itu? Dalam paradigma di atas itu tidak mungkin karena hanya struktur dasar ... dalam hal ini "MyStruct" akan benar-benar memiliki bidangnyaHttps://github.com/mitchellh/mapstructure library Hashicorp melakukan ini di luar kotak:
import "github.com/mitchellh/mapstructure" mapstructure.Decode(myData, &result)
result
Parameter kedua harus berupa alamat struct.sumber
user_name
dan struct diajukanUserName
?encoding/json
pakethanya sebagai contoh:
package main import ( "fmt" "encoding/json" ) type MyAddress struct { House string School string } type Student struct { Id int64 Name string Scores float32 Address MyAddress Labels []string } func Test() { dict := make(map[string]interface{}) dict["id"] = 201902181425 // int dict["name"] = "jackytse" // string dict["scores"] = 123.456 // float dict["address"] = map[string]string{"house":"my house", "school":"my school"} // map dict["labels"] = []string{"aries", "warmhearted", "frank"} // slice jsonbody, err := json.Marshal(dict) if err != nil { // do error check fmt.Println(err) return } student := Student{} if err := json.Unmarshal(jsonbody, &student); err != nil { // do error check fmt.Println(err) return } fmt.Printf("%#v\n", student) } func main() { Test() }
sumber
Anda bisa melakukannya ... mungkin akan sedikit jelek dan Anda akan dihadapkan pada beberapa trial and error dalam hal jenis pemetaan .. tapi inilah inti dasarnya:
func FillStruct(data map[string]interface{}, result interface{}) { t := reflect.ValueOf(result).Elem() for k, v := range data { val := t.FieldByName(k) val.Set(reflect.ValueOf(v)) } }
Contoh kerja: http://play.golang.org/p/PYHz63sbvL
sumber
reflect: call of reflect.Value.Set on zero Value
Saya mengadaptasi jawaban Dave, dan menambahkan fitur rekursif. Saya masih mengerjakan versi yang lebih ramah pengguna. Misalnya, string angka di peta harus bisa diubah menjadi int di struct.
package main import ( "fmt" "reflect" ) func SetField(obj interface{}, name string, value interface{}) error { structValue := reflect.ValueOf(obj).Elem() fieldVal := structValue.FieldByName(name) if !fieldVal.IsValid() { return fmt.Errorf("No such field: %s in obj", name) } if !fieldVal.CanSet() { return fmt.Errorf("Cannot set %s field value", name) } val := reflect.ValueOf(value) if fieldVal.Type() != val.Type() { if m,ok := value.(map[string]interface{}); ok { // if field value is struct if fieldVal.Kind() == reflect.Struct { return FillStruct(m, fieldVal.Addr().Interface()) } // if field value is a pointer to struct if fieldVal.Kind()==reflect.Ptr && fieldVal.Type().Elem().Kind() == reflect.Struct { if fieldVal.IsNil() { fieldVal.Set(reflect.New(fieldVal.Type().Elem())) } // fmt.Printf("recursive: %v %v\n", m,fieldVal.Interface()) return FillStruct(m, fieldVal.Interface()) } } return fmt.Errorf("Provided value type didn't match obj field type") } fieldVal.Set(val) return nil } func FillStruct(m map[string]interface{}, s interface{}) error { for k, v := range m { err := SetField(s, k, v) if err != nil { return err } } return nil } type OtherStruct struct { Name string Age int64 } type MyStruct struct { Name string Age int64 OtherStruct *OtherStruct } func main() { myData := make(map[string]interface{}) myData["Name"] = "Tony" myData["Age"] = int64(23) OtherStruct := make(map[string]interface{}) myData["OtherStruct"] = OtherStruct OtherStruct["Name"] = "roxma" OtherStruct["Age"] = int64(23) result := &MyStruct{} err := FillStruct(myData,result) fmt.Println(err) fmt.Printf("%v %v\n",result,result.OtherStruct) }
sumber
Ada dua langkah:
Berikut ini contohnya:
sumber