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

报告相同问题?