dpj997991 2019-06-19 17:37
浏览 53
已采纳

json.Unmarshal接口指针,带有更高类型的断言

Because I often unmarshal http.Response.Body, I thought I could write a function which handles all the hassle of reading, closing and unmarshaling into various different structs. That's why I introduced a function func unmarhalInterface(closer *io.ReadCloser, v *interface{}) error and can then assert the return value with t:=i.(T).

According to this answer I already wrapped it into a value of type *interface{}, but because the overlying type is interface{}and not myStruct, the json package implementation chooses map[string]interface{}. After that a type assertion fails (of course). Is there anything i am missing or requires this implementation a type assertion "by hand", that means look for all fields in the map and assign those that I want into my struct.

Code below has minimal example with notation in comments. If my explanation is not sufficient, please ask away.

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

  • 写回答

1条回答 默认 最新

  • doujiang6944 2019-06-19 22:44
    关注

    An empty interface isn't an actual type, it's basically something that matches anything. As stated in the comments, a pointer to an empty interface doesn't really make sense as a pointer already matches an empty interface since everything matches an empty interface. To make your code work, you should remove the interface wrapper around your struct, since that's just messing up the json type checking, and the whole point of an empty interface is that you can pass anything to it.

    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)
    }
    
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 求差集那个函数有问题,有无佬可以解决
  • ¥15 MATLAB动图问题
  • ¥15 【提问】基于Invest的水源涵养
  • ¥20 微信网友居然可以通过vx号找到我绑的手机号
  • ¥15 寻一个支付宝扫码远程授权登录的软件助手app
  • ¥15 解riccati方程组
  • ¥15 display:none;样式在嵌套结构中的已设置了display样式的元素上不起作用?
  • ¥15 使用rabbitMQ 消息队列作为url源进行多线程爬取时,总有几个url没有处理的问题。
  • ¥15 Ubuntu在安装序列比对软件STAR时出现报错如何解决
  • ¥50 树莓派安卓APK系统签名