Можно ли динамически установить вывод json.Unmarshal? - PullRequest
0 голосов
/ 24 октября 2018

У меня есть следующий код Go:

var typeRegistry = make(map[string]reflect.Type)

func init() {
    typeRegistry["User"] = reflect.TypeOf(User{})
}

func makeInstance(name string) interface{} {
    v := reflect.New(typeRegistry[name]).Elem()
    return v.Interface()
}

func Invoke(any interface{}, name string, body []byte, signature Signature) {
    args := signature.Args
    data := makeInstance(signature.Args[0])
    json.Unmarshal(body, &data)
    inputs := make([]reflect.Value, len(args))
    for i, _ := range signature.Args {
        log.Println(reflect.TypeOf(data))
        log.Println(reflect.ValueOf(data))
        inputs[i] = reflect.ValueOf(data)
    }
    reflect.ValueOf(any).MethodByName(name).Call(inputs)
}

Я пытаюсь передать некоторый JSON и строку, обозначающую, какой тип JSON должен быть сопоставлен.Я пытаюсь использовать рефлексию, чтобы снова соединить их вместе и передать в метод с именем метода.

Я вроде все заработал, однако, когда я использую указатель un json.Unmarshal, он, похоже, снова теряет ссылку на назначенный ему тип, и по умолчанию возвращается к map[string]interface{}, что не соответствуетметод, который я вызываю.В этом случае ожидается тип main.User.Если я удаляю указатель из json.Unmarshal(body, data), типы совпадают правильно, но, очевидно, данные больше не устанавливаются для data.

Я знаю, что я убиваю систему типов Го и, возможно, использую язык способами, которые не предлагаются, но я пытаюсь сделать что-то более академическое, чем полезное, я думаю.

1 Ответ

0 голосов
/ 24 октября 2018

Вы должны передать указатель на json.Unmarshal(), иначе он не может изменить (заостренные) данные, только их копию.Это то, что происходит, когда вы передаете data: так как он не может изменить значение (значение без указателя типа User, заключенное в interface{}), поэтому он создает новое значение, в которое он может разобрать.И он выберет все, что сочтет нужным (но это будет не User, а скорее map[string]interface{}, как вы испытали).

Так что да, вы должны передать указатель на него, но &data не будет тем, что вы хотите.&data будет указателем на интерфейс типа *interface{}, поскольку makeInstance() возвращает значение типа interface{}, поэтому data будет иметь этот выведенный тип.

Решение состоит в том, чтобы изменить makeInstance() чтобы вернуть указатель, который может быть заключен в значение interface{}, это нормально.И тогда вы можете просто передать data в json.Unmarshal(), потому что значение интерфейса data будет содержать значение типа *User.

Так же:

func makeInstance(name string) interface{} {
    v := reflect.New(typeRegistry[name]) // Don't call Elem() here
    return v.Interface()
}

Ив Invoke():

data := makeInstance(signature.Args[0])
json.Unmarshal(body, data)

См. связанный вопрос, который был опубликован всего несколько часов назад:

Хранить информацию / справку о структуре

Вы можете увидеть рабочую демонстрацию здесь: Go Playground .

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