Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/go/7.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Json 如何从接口{}转换为结构实例_Json_Go - Fatal编程技术网

Json 如何从接口{}转换为结构实例

Json 如何从接口{}转换为结构实例,json,go,Json,Go,我正在进行Go模块实现,以抽象与其他同行的通信。该模块背后的思想是通过标准消息格式的MQ发送/接收消息,该格式可以携带几乎任何类型的“实体”。但是我发现很难解决Go中的类型转换 下面是我尝试做的一个片段() 在这一行,我得到以下错误: cart = message.Entity.(Cartoon) panic:interface conversion:interface{}是map[string]interface{},而不是main.Cartoon 问题是,由于这是一个用于“通用”实体类型的

我正在进行Go模块实现,以抽象与其他同行的通信。该模块背后的思想是通过标准消息格式的MQ发送/接收消息,该格式可以携带几乎任何类型的“实体”。但是我发现很难解决Go中的类型转换

下面是我尝试做的一个片段()

在这一行,我得到以下错误:

cart = message.Entity.(Cartoon)
panic:interface conversion:interface{}是map[string]interface{},而不是main.Cartoon

问题是,由于这是一个用于“通用”实体类型的模块,所以在将消息传递给消费者应用程序之前,我不知道实体类型(struct)

因此,我需要一种方法将结构实例交付给应用程序,甚至允许使用者应用程序转换为它期望接收的类型(struct)

即使有一个更优雅的方式来做我想要的,即使我必须改变我的消息结构,我也可以考虑解决方案。 解决这个问题的唯一方法是将Message.Entity转换为保存原始json内容的字符串字段,然后消费者应用程序将其解组为所需类型。不那么优雅

原始字符串/json解决方案()

有什么想法吗?

如果您保持struct不变,那么有一个解决方案(它不是那么优雅!)

tmp, ok := message.Entity.(map[string]interface{})
if ok {
    cart.Name = tmp["Name"].(string) // inside bracket depends to the json
    cart.Show = tmp["Show"].(string)
}


您还可以使用reflect。

看起来我可以用一个名为mapstructure的库来解决这个问题

var cart Cartoon
mapstructure.Decode(message.Entity, &cart)
fmt.Println("cartoon:", cart)

我在这里做了一个成功的测试:

问题是如何将
接口{}
转换为Go类型。OPs应用程序的更高级别目标是将JSON消息的变体部分转换为Go类型。这里回答了更高层次的问题

如果JSON解组时已知类型,则将实体字段设置为指向适当类型值的指针:

var cart Cartoon
message := &Message{Entity: &cart}
err := json.Unmarshal(payload, message)

如果直到解码消息的不变部分后才知道类型,则使用捕获实体JSON。一旦知道类型,就对JSON进行解码

var entity json.RawMessage
message := &Message{Entity: &entity}
err := json.Unmarshal(payload, &message)

// ... determine target entity type using message

var cart Cartoon
err = json.Unmarshal(entity, &cart)

另一个选项是创建以下类型的注册表:

var messageTypes = map[string]reflect.Type{
    "cartoon": reflect.TypeOf(&Cartoon{}).Elem(),
}
并从注册表解码为一种类型:

var entity json.RawMessage
message := &Message{Entity: &entity}
err = json.Unmarshal(payload, message)
if err != nil {
    // handle error
}

message.Entity = nil

if t := messageTypes[message.Type]; t != nil {
    v := reflect.New(t).Interface()
    err := json.Unmarshal(entity, v)
    if err != nil {
        // handle error
    }
    message.Entity = v
}

在解组之前将实体字段设置为具体类型的值,或者使用而不是字符串。它的存在正是为了这个用例。目标类型是如何确定的?消息中的字段是否指定了类型,或者由被调用的函数确定?实际上,在我正在处理的适当模块中,有一个字段表示类型名称(字符串)。但只有消费者应用程序才会知道/拥有这种类型。自由党对此一无所知。因此,被调用函数恰好知道它。这与使用json.RawMessage和json.Unmarshal没有什么不同,只是它强制用户依赖,并且用户在其类型中使用非标准标记,如果这些类型是在第三方包中定义的,这是不可能的。
var entity json.RawMessage
message := &Message{Entity: &entity}
err = json.Unmarshal(payload, message)
if err != nil {
    // handle error
}

message.Entity = nil

if t := messageTypes[message.Type]; t != nil {
    v := reflect.New(t).Interface()
    err := json.Unmarshal(entity, v)
    if err != nil {
        // handle error
    }
    message.Entity = v
}