解组成Go中运行时确定的结构数组 [英] Unmarshal into array of structs determined at runtime in Go

查看:91
本文介绍了解组成Go中运行时确定的结构数组的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图动态地创建一个结构片段,我可以将数据编入数据,但是在运行时基于某些未知的结构类型进行。我这样做是因为我想拥有一个可以解组任何实现特定接口的代码的普通位。

例如。 (伪代码)
$ b $ pre $ func unmarshalMyData(myInterface,MyInterface,jsonData [] byte){
targetSlice:= myInterface.EmptySlice ()
json.Unmarshal(jsonData,& targetSlice)
}

我尝试了几种不同的选择。看起来最有前途的是使用 reflect 包来构建切片。不幸的是,在解组之后,动态创建的slice具有 [] interface {} 类型。如果我在解组之前打印出动态创建的切片的类型,则会打印 [] * main.myObj 。谁能解释为什么?请参阅游乐场链接: https://play.golang.org/p/vvf1leuQeY



是否有其他方法可以动态创建解构正确的结构片段?

我知道 json.RawMessage ,但有几个原因导致我无法使用它。 ..我的JSON中没有type字段。此外,我正在解组的代码没有编译时知道它正在解组的结构。它只知道该结构实现了一个特定的接口。



一些让我感到困惑的代码为什么动态创建的slice在unmarshalling后不能保持它的类型:

<$ p





$ f





$ b func main(){
input:=`[{myField:one},{myField:two}]`
myObjSlice:= [ ] * myObj {}
fmt.Printf(解组之前myObjSlice的类型%T'\
,myObjSlice)
err:= json.Unmarshal([] byte(input)& myObjSlice)
if err!= nil {
panic(err)
}
fmt.Printf(解组后的myObjSlice的类型%T \ n,myObjSlice)

myConstructedObjSlice:= reflect.MakeSlice(reflect.SliceOf(reflect.TypeOf(& myObj {})),0,0).Interface()
fmt.Printf(解组之前myConstructedObjSlice的类型% T \ n,myConstructedObjSlice)
err = json.Unmarshal([] byte(input),& myConstructedObjSlice)
if err!= nil {
panic(err)
}
fmt.Printf(解组后的myConstructedObjSlice类型%T \ n,myConstructedObjSlice)
}

类型myObj struct { b $ b myField字符串
}

输出:

 解组之前myObjSlice的类型[] * main.myObj 
解组之后myObjSlice的类型[] * main.myObj
之前的myConstructedObjSlice类型unmarshalling [] * main.myObj
解组后的myConstructedObjSlice类型[] interface {}


解决方案

使用reflect构建 [] * myObj ,然后传递一个 *接口{} json.Unmarshal 。 json包将指针的目标视为类型 interface {} ,因此使用其默认类型解组。您需要创建一个指向您创建的切片类型的指针,因此对 Interface()方法的调用将返回您想要解组的类型,即<$ c
$ b $ pre code> sliceType:= reflect.SliceOf(reflect.TypeOf (& myObj {}))
slicePtr:= reflect.New(sliceType)

err = json.Unmarshal([] byte(input),slicePtr.Interface())


I'm trying to dynamically create a slice of structs that I can marshal my data into, but doing it at runtime based on some unknown struct type. I'm doing this because I want to have one common bit of code that can unmarshal anything that implements a specific interface.

e.g. (pseudo code)

func unmarshalMyData(myInterface MyInterface, jsonData []byte) {
   targetSlice := myInterface.EmptySlice()
   json.Unmarshal(jsonData, &targetSlice)
}

I've tried several different alternatives. The one that seems the most promising is using the reflect package to construct the slice. Unfortunately, after unmarshalling, the dynamically created slice has a type of []interface{}. If I print out the type of the dynamically created slice before unmarshalling it prints []*main.myObj. Can anyone explain why? See playground link: https://play.golang.org/p/vvf1leuQeY

Is there some other way to dynamically create a slice of structs that unmarshals correctly?

I'm aware of json.RawMessage, but there are a few reasons I can't use it... there is no "type" field in my json. Also, the code where I am unmarshalling has no compile time knowledge of the struct that it is unmarshalling. It only knows that the struct implements a specific interface.

Some code that baffles me as to why dynamically created slice doesn't maintain its type after unmarshalling:

package main

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

func main() {
    input := `[{"myField":"one"},{"myField":"two"}]`
    myObjSlice := []*myObj{}
    fmt.Printf("Type of myObjSlice before unmarshalling %T\n", myObjSlice)
    err := json.Unmarshal([]byte(input), &myObjSlice)
    if err != nil {
        panic(err)
    }
    fmt.Printf("Type of myObjSlice after unmarshalling %T\n", myObjSlice)

    myConstructedObjSlice := reflect.MakeSlice(reflect.SliceOf(reflect.TypeOf(&myObj{})), 0, 0).Interface()
    fmt.Printf("Type of myConstructedObjSlice before unmarshalling %T\n", myConstructedObjSlice)
    err = json.Unmarshal([]byte(input), &myConstructedObjSlice)
    if err != nil {
        panic(err)
    }
    fmt.Printf("Type of myConstructedObjSlice after unmarshalling %T\n", myConstructedObjSlice)
}

type myObj struct {
    myField string
}

The output:

Type of myObjSlice before unmarshalling []*main.myObj
Type of myObjSlice after unmarshalling []*main.myObj
Type of myConstructedObjSlice before unmarshalling []*main.myObj
Type of myConstructedObjSlice after unmarshalling []interface {}

解决方案

You're building a []*myObj using reflect, but you're then passing an *interface{} to json.Unmarshal. The json package sees the target of the pointer as the type interface{}, and therefore uses its default types to unmarshal into. You need to create a pointer to the slice type you create, so the call to the Interface() method returns the exactly type you want to unmarshal into, which is *[]*myObj.

sliceType := reflect.SliceOf(reflect.TypeOf(&myObj{}))
slicePtr := reflect.New(sliceType)

err = json.Unmarshal([]byte(input), slicePtr.Interface())

这篇关于解组成Go中运行时确定的结构数组的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆