Указатель интерфейса json.Unmarshal с последующим утверждением типа - PullRequest
0 голосов
/ 19 июня 2019

Поскольку я часто отменял маршализацию http.Response.Body, я подумал, что мог бы написать функцию, которая обрабатывает все трудности чтения, закрытия и демаршалирования в различные структуры.Вот почему я ввел функцию func unmarhalInterface(closer *io.ReadCloser, v *interface{}) error и затем могу утверждать возвращаемое значение с помощью t:=i.(T).

Согласно этому ответу я уже обернул его в значение типа *interface{},но поскольку перекрывающим типом является interface{}, а не myStruct, реализация пакета json выбирает map[string]interface{}.После этого утверждение типа не выполняется (конечно).Есть ли что-то, чего я пропускаю, или требует, чтобы в этой реализации было утверждение типа «вручную», это означает, что ищите все поля на карте и присваивайте те, которые мне нужны, в моей структуре.

В приведенном ниже коде есть минимальный пример с нотациейв комментариях.Если моего объяснения недостаточно, пожалуйста, спросите.

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
}

Детская площадка Голанга

1 Ответ

1 голос
/ 20 июня 2019

Пустой интерфейс не является реальным типом, это в основном то, что соответствует чему-либо.Как указано в комментариях, указатель на пустой интерфейс на самом деле не имеет смысла, так как указатель уже соответствует пустому интерфейсу, поскольку все соответствует пустому интерфейсу.Чтобы заставить ваш код работать, вы должны удалить интерфейсную оболочку вокруг своей структуры, так как это просто испортило проверку типов json, и весь смысл пустого интерфейса в том, что вы можете передать ему что угодно .

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)
}
...