在Go中运行时确定的结构数组解组

时间:2016-06-08 22:04:55

标签: go

我正在尝试动态创建一个结构片段,我可以将我的数据封送到其中,但是在运行时根据一些未知的结构类型进行。我这样做是因为我希望有一个共同的代码可以解组任何实现特定接口的代码。

e.g。 (伪代码)

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

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

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

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

有些代码让我感到困惑,为什么动态创建的切片在解组后不会保持其类型:

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
}

输出:

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 {}

1 个答案:

答案 0 :(得分:5)

您正在使用反射构建[]*myObj,但您正在将*interface{}传递给json.Unmarshal。 json包将指针的目标视为类型interface{},因此使用其默认类型进行解组。您需要创建指向您创建的切片类型的指针,因此对Interface()方法的调用将返回您要解组的确切类型,即*[]*myObj

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

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