Как печатать assert динамически генерируемый отражением структурный интерфейс - PullRequest
0 голосов
/ 01 февраля 2020

Я новичок в Go, поэтому, пожалуйста, потерпите меня, если это тривиальная проблема. Я использую самодельный «реестр типов» для сопоставления имен типов с их типом, чтобы генерировать их динамически на основе вариантов использования, которые указывают на различные имена типов (в основном я пытаюсь найти простое решение для polymorphi c Агрегация JSON структур ответа в Elasticsearch, но, конечно, это может относиться ко многим другим динамическим / полиморфным c ситуациям). Я использую решение, предоставленное dolmen в этом вопросе: есть ли способ создать экземпляр структуры из строки? :

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

func registerType(typedNil interface{}) {
    t := reflect.TypeOf(typedNil).Elem()
    typeRegistry[t.Name()] = t
}

func init() {
    registerType((*playlistIDAggregation)(nil))
    registerType((*srcIDAggregation)(nil))
    registerType((*assetIDAggregation)(nil))
}

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

Затем я хочу использовать свой динамически генерируемая структура в качестве цели для JSON демаршаллинга узла Aggregations в моем ответе ES:

playlistIDAgg := makeInstance("playlistIDAggregation")
err = json.Unmarshal(esResponse.Aggregations, &playlistIDAgg)

Это не работает так, как я хочу, так как Unmarshal пытается разобрать в пустой интерфейс вместо базового типа структуры. он помещает данные в узлы «data» в переменной playlistIDAgg, и эти поля данных, конечно, map[string]interface{}. Я просто пропускаю способ ввода assert моего playlistIDAgg интерфейса или есть лучший способ сделать это?

EDIT --- Вопросы в комментариях заставили меня понять, что редактирование этого вопроса давно назрело , В моем конкретном случае определенные мной структуры для привязки к моим агрегатам Bucket, возвращаемым Elasticsearch, имеют похожую структуру и отличаются только своим тегом root JSON, который ES использует для именования агрегата и строгого его ввода. Например,

type <name>Aggregation struct {
    Agg BucketAggregationWithCamIDCardinality `json:"<name>"`
}

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

Кроме того, более тяжелый, но более надежный вариант - использовать клиентскую библиотеку Elasticsearch Go Оливера Эйларда, называемую Elasti c, которая имеет встроенную поддержку для всех структур отклика агрегации ES: https://github.com/olivere/elastic/

1 Ответ

1 голос
/ 01 февраля 2020

Я изменил функцию makeInstance, получив адрес элемента и добавив целевые структуры с открытыми полями.

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

Вот рабочий код

package main

import (
    "encoding/json"
    "fmt"
    "reflect"
)

type playlistIDAggregation struct {
    PlaylistID string
}

type srcIDAggregation struct {
    SrcID string
}

type assetIDAggregation struct {
    AssetID string
}

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

func registerType(typedNil interface{}) {
    t := reflect.TypeOf(typedNil).Elem()
    typeRegistry[t.Name()] = t
}

func init() {
    registerType((*playlistIDAggregation)(nil))
    registerType((*srcIDAggregation)(nil))
    registerType((*assetIDAggregation)(nil))
}

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

func main() {
    playlistIDAgg := makeInstance("playlistIDAggregation")
    fmt.Printf("Type = %[1]T => %#[1]v\n", playlistIDAgg)
    err := json.Unmarshal([]byte(`{"PlayListID": "dummy-id"}`), &playlistIDAgg)
    if err != nil {
        panic(err)
    }
    fmt.Printf("Type = %[1]T => %#[1]v\n", playlistIDAgg)
}

https://play.golang.org/p/dn19_iG5Xjz

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