Чтение байтов в структуры с использованием отражения - PullRequest
0 голосов
/ 17 марта 2012

Я пытаюсь написать функции, которые позволят мне маршалировать / демаршировать простые структуры в байтовые массивы.Мне удалось написать 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() ближе к концу, по-видимому, пытается получить адрес с неадресуемым значением.

Если есть альтернативное решение, я также был бы признателен за это.В любом случае, любая помощь будет высоко ценится.

Спасибо!

Ответы [ 3 ]

1 голос
/ 17 марта 2012

Я думаю, вы должны использовать

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

вместо

v := reflect.ValueOf(p)
0 голосов
/ 19 марта 2012

Готов поспорить, что причина в f.Addr () состоит в том, что проблема в том, что она на самом деле не адресуема.

у объекта типа отраженного пакета есть метод, который сообщит вам, является ли типадресуемый называется CanAddr ().Предполагая, что поле является адресуемым, если оно не является строкой, это не всегда верно.Если структура не передана как указатель на структуру, то ее поля не будут адресуемыми.Для получения более подробной информации о том, что является и что нельзя адресовать, см. http://weekly.golang.org/pkg/reflect/#Value.CanAddr, в котором изложены правильные правила.

По существу, чтобы ваш код работал, я думаю, вам нужно убедиться, что вы всегда вызываете его с указателемв структуру.

0 голосов
/ 18 марта 2012

Рабочий пример с множеством допущений и тривиальным форматом данных:

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
}

Подходящие альтернативные решения будут сильно зависеть от реальных данных, которые вы должны поддерживать.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...