简体   繁体   English

如何键入断言动态反射生成的结构接口

[英]How to type assert a dynamically reflection-generated struct interface

I'm new to Go so please bear with me if this is a trivial problem.我是 Go 的新手,所以如果这是一个微不足道的问题,请多多包涵。 I am using a home grown "type registry" to map type names to their type, so as to generate them dynamically based on use cases that point to the various type names (I'm basically trying for a simple solution to polymorphic Aggregation JSON response structures in Elasticsearch, but of course this could apply to many other dynamic/polymorphic situations).我正在使用自制的“类型注册表”将类型名称映射到它们的类型,以便根据指向各种类型名称的用例动态生成它们(我基本上是在尝试一个简单的解决方案来解决多态聚合 JSON 响应Elasticsearch 中的结构,但当然这可以应用于许多其他动态/多态情况)。 I'm using the solution provided by dolmen in this question: is there a way to create an instance of a struct from a string?我在这个问题中使用了 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()
}

I then want to use my dynamically generated struct as the target for the JSON unmarshalling of the Aggregations node in my ES response:然后,我想使用动态生成的结构作为 ES 响应中聚合节点的 JSON 解组目标:

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

This isn't working like I want it to, as the Unmarshal is trying to unmarshall into an empty interface instead of the underlying struct type.这不像我想要的那样工作,因为 Unmarshal 试图解组到一个空接口而不是底层结构类型。 it's putting the data under "data" nodes in the playlistIDAgg variable, and those data fields are of course map[string]interface{} .它将数据放在 playlistIDAgg 变量中的“数据”节点下,这些数据字段当然是map[string]interface{} Am I just missing the way to type assert my playlistIDAgg interface or is there a better way to do this?我是否只是缺少输入断言我的playlistIDAgg接口的方法,还是有更好的方法来做到这一点?

EDIT--- The questions in the comments made me realize an edit to this question was long overdue.编辑---评论中的问题让我意识到对这个问题的编辑早就应该了。 In my particular case, the structs I defined, to bind to my Bucket aggregations returned by Elasticsearch, have a similar structure and vary only by their root JSON tag, which ES uses to name the aggregation and strongly type it.在我的特殊情况下,我定义的用于绑定到 Elasticsearch 返回的 Bucket 聚合的结构具有相似的结构,并且仅因它们的根 JSON 标记而异,ES 使用该标记命名聚合并对其进行强类型化。 Eg例如

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

So, rather than a type registry, my particular problem could be solved by dynamically setting the JSON tag on the struct based on the particular use case.因此,不是类型注册表,我的特定问题可以通过基于特定用例在结构上动态设置 JSON 标记来解决。

In addition, a heavier but more robust option would be to leverage Oliver Eilhard's Elasticsearch Go client lib, called Elastic, which has built-in support for all the ES aggregation response structures: https://github.com/olivere/elastic/此外,一个更重但更强大的选择是利用 Oliver Eilhard 的 Elasticsearch Go 客户端库,称为 Elastic,它内置了对所有 ES 聚合响应结构的支持: https : //github.com/olivere/elastic/

I changed makeInstance function by getting address of element and add target structs with exposed fields.我通过获取元素的地址并添加具有公开字段的目标结构来更改makeInstance函数。

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

Here is the working code这是工作代码

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 https://play.golang.org/p/dn19_iG5Xjz

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM