【问题标题】:json.Unmarshal interface pointer with later type assertionjson.Unmarshal 接口指针,带有后面的类型断言
【发布时间】:2019-06-19 17:37:40
【问题描述】:

因为我经常解组http.Response.Body,所以我想我可以编写一个函数来处理读取、关闭和解组到各种不同结构的所有麻烦。这就是为什么我引入了一个函数func unmarhalInterface(closer *io.ReadCloser, v *interface{}) error,然后可以使用t:=i.(T) 断言返回值。

根据this answer,我已经将它包装成一个*interface{}类型的值,但是因为上面的类型是interface{}而不是myStruct,所以json包实现选择map[string]interface{}。之后类型断言失败(当然)。有什么我遗漏的或者需要这个实现“手动”的类型断言,这意味着查找地图中的所有字段并将我想要的那些分配到我的结构中。

下面的代码有一个用 cmets 表示的最小示例。如果我的解释不充分,请尽管问。

package main

import (
    "bytes"
    "encoding/json"
    "fmt"
    "io"
    "io/ioutil"
    "log"
)

type myStruct struct {
    A string `json:"a"`
    B string `json:"b"`
}

func main() {
    jsonBlob := []byte(`{"a":"test","b":"test2"}`)

    var foo = interface{}(myStruct{})
    closer := ioutil.NopCloser(bytes.NewReader(jsonBlob))

    err := unmarshalCloser(&closer, &foo)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println(fmt.Sprintf("%v", foo))

    // That´s what i want:
    foo2 := foo.(myStruct)
    fmt.Println(foo2.A)
}

func unmarshalCloser(closer *io.ReadCloser, v *interface{}) error {
    defer func() { _ = (*closer).Close() }()

    data, err := ioutil.ReadAll(*closer)
    if err != nil {
        return err
    }

    err = json.Unmarshal(data, v)
    if err != nil {
        return err
    }
    return nil
}

Golang Playground

【问题讨论】:

  • 链接的答案说使用*interface{}。唯一需要指向接口的指针是如果您想将一个空接口传递给Unmarshal 本身。另外,为什么你还有*io.ReadCloser 作为参数?几乎没有理由传递指向接口的指针(如果您想通过反射分配给接口,上述例外是少数例外之一)
  • @j 我也试过没有指针,但同样的问题仍然存在。相同的接口转换问题,即使 json.unmarshal 现在获取正确的类型作为输入。 NotPointerCode:play.golang.org/p/edn5opF50SQ
  • 试过什么?如果您删除所有接口指针废话,它会按预期工作:play.golang.org/p/GfJFvhVad3w。这与您想要实现的目标有何不同?

标签: json go unmarshalling


【解决方案1】:

空接口不是实际类型,它基本上可以匹配任何东西。正如 cmets 中所述,指向空接口的指针实际上没有意义,因为指针已经匹配空接口,因为所有内容都匹配空接口。为了使您的代码正常工作,您应该删除结构周围的接口包装器,因为这只会弄乱 json 类型检查,而空接口的全部意义在于您可以将 anything 传递给它。

package main

import (
    "bytes"
    "encoding/json"
    "fmt"
    "io"
    "io/ioutil"
    "log"
)

type myStruct struct {
    A string `json:"a"`
    B string `json:"b"`
}

func main() {
    jsonBlob := []byte(`{"a":"test","b":"test2"}`)

    var foo = &myStruct{} // This need to be a pointer so its attributes can be assigned
    closer := ioutil.NopCloser(bytes.NewReader(jsonBlob))

    err := unmarshalCloser(closer, foo)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println(fmt.Sprintf("%v", foo))

    // That´s what i want:
    fmt.Println(foo.A)
}

// You don't need to declare either of these arguments as pointers since they're both interfaces
func unmarshalCloser(closer io.ReadCloser, v interface{}) error {
    defer closer.Close()
    // v NEEDS to be a pointer or the json stuff will barf

    // Simplified with the decoder
    return json.NewDecoder(closer).Decode(v)
}

【讨论】:

猜你喜欢
  • 2016-08-01
  • 2017-02-13
  • 2016-06-06
  • 2021-12-05
  • 2019-07-18
  • 2023-04-03
  • 1970-01-01
  • 2017-10-08
  • 1970-01-01
相关资源
最近更新 更多