Установка значений вложенных структурных полей с помощью отражения в Go - PullRequest
0 голосов
/ 17 сентября 2018

Я пытаюсь создать структуру protobuf из

type FlatMessage map[string][]byte

, и я пытаюсь установить значения вложенных полей.

У меня есть метод

func (fm *FlatMessage) Unflatten() (pb.Message, error)

для преобразования плоского сообщения в структурированный protobuf.

У меня также есть вспомогательная функция с именем analyze, которая принимает структуру типа pb.Message и возвращает map[int]*ProtoField где:

type ProtoField struct {
    Name  string
    Type  reflect.Type
}

т.е. analyze traversses pb.Messageрекурсивно и собирает всю необходимую мне информацию об этом.Все идет нормально.

Теперь, когда я иду ключ за ключом через мой FlatMessage, ключ - это кодированный номер поля соответствующего поля protobuf, и я могу установить его, используя отражение, например:

r := reflect.ValueOf(&result).Elem().FieldByName(field.Name)
if r.IsValid() {
    r.Set(scalarValue)
}

, но это работает только тогда, когда рассматриваемый field.Name не относится к вложенному полю, т. Е. Установка OldV1Id работает нормально, но попытка установить Profile.Id или, скажем, Destination.Address.TypeOfNumber, приводит к:

panic: reflect: call of reflect.Value.Set on zero Value [recovered]
    panic: reflect: call of reflect.Value.Set on zero Value

Я понимаю, что это связано с тем, что мой r становится <invalid Value> при вызове во вложенном поле, но как я могу обойти это?

Мой r, конечно, не valid, не адресуемый и не устанавливаемый.Я могу сделать из него reflect.New, но я не могу понять, как установить этот новый reflect.Value таким образом, чтобы поле моих исходных структур изменилось.Независимо от того, что я делаю, моя функция не изменяет поля с вложенными именами с помощью этого подхода.

Еще одно решение, которое я попробовал, - это добавить значение Reflect.Value в мою структуру ProtoField и изменить analyze, добавив reflect.ValueOf(s).Field(i), где s - это структура верхнего уровня interface{}и i - это его i-е поле.Затем, когда я сталкиваюсь с вложенным полем, я вызываю r := reflect.ValueOf(field.Value), но тогда проблема в том, что я не могу вызвать r.Set(scalarValue) из-за несовместимых типов.

Любая помощь или понимание очень ценится.

1 Ответ

0 голосов
/ 17 сентября 2018

Проблема решена. ThunderCat был прав в своем комментарии: хитрость заключается в использовании FieldByIndex.Я не замечал подписи этого метода раньше, я думал, что он принимает целое число в качестве аргумента, но на самом деле он принимает срез целых чисел.Тогда мне было легко модифицировать мою analyze функцию, которая рекурсивно обходит структуру, а теперь также строит часть индексов по мере необходимости.Теперь я могу просто r := reflect.ValueOf(&result).Elem().FieldByIndex(field.Idx) и я смеюсь.

Извлеченный здесь урок: если вы хотите задать значения вложенных struct полей с помощью отражения, не называйте их по имени;называть их по индексу.

...