2012-03-17 34 views
0

我正在嘗試編寫函數,使我可以將簡單結構編組/解組爲字節數組。我已經成功寫作Marshal,在#go-nuts的友好人士的幫助下,但我遇到了編寫Unmarshal的麻煩。使用反射將字節讀入結構中

// Unmarshal unpacks the binary data and stores it in the packet using 
// reflection. 
func Unmarshal(b []byte, t reflect.Type) (pkt interface{}, err error) { 
    buf := bytes.NewBuffer(b) 
    p := reflect.New(t) 
    v := reflect.ValueOf(p) 
    for i := 0; i < t.NumField(); i++ { 
     f := v.Field(i) 
     switch f.Kind() { 
     case reflect.String: 
      // length of string 
      var l int16 
      var e error 
      e = binary.Read(buf, binary.BigEndian, &l) 
      if e != nil { 
       err = e 
       return 
      } 

      // read length-of-string bytes from the buffer 
      raw := make([]byte, l) 
      _, e = buf.Read(raw) 
      if e != nil { 
       err = e 
       return 
      } 

      // convert the bytes to a string 
      f.SetString(bytes.NewBuffer(raw).String()) 
     default: 
      e := binary.Read(buf, binary.BigEndian, f.Addr()) 
      if e != nil { 
       err = e 
       return 
      } 
     } 
    } 

    pkt = p 
    return 
} 

與上面的代碼的問題是,臨近年底調用f.Addr()顯然是想獲得一個不可尋址的地址值。

如果有其他解決方案,我也會很感激。無論哪種方式,任何幫助將不勝感激。

謝謝!

+0

您能否提供樣本數據和結構以便我們可以測試您的代碼? – Mostafa 2012-03-17 08:50:04

+0

更多信息真的有幫助。編組數據的格式是什麼,結構是什麼樣的? (如果結構類型是固定的,則根本不需要反射。)如果結構類型可以變化,那麼您編寫的代碼仍然取決於它們如何變化。例如,如果struct字段類型對於給定的數據字段可能會有所不同,則必須進行一些轉換。如果struct字段與數據字段不完全一致,則必須有某種方式將它們匹配起來。 – Sonia 2012-03-17 23:24:20

回答

1

我認爲你應該使用

v := p.Elem() // Get the value that 'p' points to 

,而不是

v := reflect.ValueOf(p) 
+0

原來這是解決方案。我不得不重新閱讀Go博客文章。 ;)我還必須將f.Addr()更改爲f.Addr()。Interface(),以獲取binary.Read()的實際指針,而不是指針的值。 – 2012-03-18 04:26:09

0

工作,有很多假設,一個簡單的數據格式,例如:

package main 

import (
    "fmt" 
    "reflect" 
    "strconv" 
) 

// example marshalled format. lets say that marshalled data will have 
// four bytes of a formatted floating point number followed by two more 
// printable bytes. 
type m42 []byte 

// example struct we'd like to unmarshal into. 
type packet struct { 
    S string // exported fields required for reflection 
    F float64 
} 

// example usage 
func main() { 
    var p packet 
    if err := Unmarshal(m42("3.14Pi"), &p); err == nil { 
     fmt.Println(p) 
    } else { 
     fmt.Println(err) 
    } 
} 

func Unmarshal(data m42, structPtr interface{}) error { 
    vp := reflect.ValueOf(structPtr) 
    ve := vp.Elem() // settable struct Value 
    vt := ve.Type() // type info for struct 
    nStructFields := ve.NumField() 
    for i := 0; i < nStructFields; i++ { 
     fv := ve.Field(i) // settable field Value 
     sf := vt.Field(i) // StructField type information 
     // struct field name indicates which m42 field to unmarshal. 
     switch sf.Name { 
     case "S": 
      fv.SetString(string(data[4:6])) 
     case "F": 
      s := string(data[0:4]) 
      if n, err := strconv.ParseFloat(s, 64); err == nil { 
       fv.SetFloat(n) 
      } else { 
       return err 
      } 
     } 
    } 
    return nil 
} 

合適的替代解決方案,將很大程度上依賴關於你需要支持的真實數據。

0

我打賭,f.Addr()存在問題,因爲它實際上不可尋址。

反射包類型對象有一個方法告訴你類型是否可尋址,稱爲CanAddr()。假設該字段是可尋址的,如果它不是字符串並不總是正確的。如果結構未作爲指向結構體的指針傳入,那麼它的字段將不可尋址。有關什麼是和不可尋址的更多詳細信息,請參閱:http://weekly.golang.org/pkg/reflect/#Value.CanAddr其中概述了正確的規則。

本質上爲了您的代碼工作,我認爲您需要確保您始終使用指向結構的指針調用它。