猿问

我可以将 JSON 解组为接口的实现者吗?

我有一个声明方法的接口和一些实现该接口的结构。现在我想将一些 JSON 解组到这些结构的实例中。以机智:


package main


import (

    "encoding/json"

    "fmt"

)


type Animal interface {

    makeNoise() string

}


type Dog struct {

    Name string

}


func (d Dog) makeNoise() string {

    return "woof"

}


type Fish struct {

    NumScales int

}


func (f Fish) makeNoise() string {

    return "glub glub glub"

}


type Zoo struct {

    Animals []Animal

}


func main() {

    animals := `{"Animals": [{"Name": "Fido"}, {"NumScales": 123}]}`

    animalBytes := []byte(animals)

    var zoo Zoo

    er := json.Unmarshal(animalBytes, &zoo)

    if er != nil {

        panic(er)

    } else {

        fmt.Println(zoo)

    }

}

但是当我运行它时,我得到“panic: json: cannot unmarshal object into Go value of type main.Animal”。我可以换一个动物园,它的动物是一只名叫 Fido 的狗和一条 123 鳞片的鱼吗?


德玛西亚99
浏览 194回答 2
2回答

繁星coding

根据您提供给我们的当前条件,没有直接的方法可以实现您想要的目标。@eduncan911 提供了一个非常通用的方法,但是,如果您能够JSON稍微调整输入,则可以使用以下方法实现它。核心思想是json.RawMessage用作缓冲区来延迟解组,直到它知道要解组的类型。首先,将JSON输入调整为如下所示:{    "Animals": [{        "Type": "dog",        "Property": {            "Name": "Fido"        }    },{        "Type": "fish",        "Property": {            "NumScales": 123        }    }]}据我所知,这个调整并没有让 JSON 变得更糟,但实际上在可读性方面让它变得更好。然后,创建一个新结构,例如AnimalCard:type AnimalCard struct {    Type string    Property json.RawMessage    Animal Animal}并修改你Zoo的type Zoo struct {    Animals []*AnimalCard}现在将您的 json 解组到 zoo,您将获得一个*AnimalCard. 现在您可以遍历动物园数组并根据类型对其进行解组:for _, card := range zoo.Animals {    if card.Type == "dog" {        dog := Dog{}        _ = json.Unmarshal(card.Property, &dog)        card.Animal = dog    } else if card.Type == "fish" {        fish := Fish{}        _ = json.Unmarshal(card.Property, &fish)        card.Animal = fish    }}游乐场示例在这里。如果动物园里的动物越来越多怎么办?好问题 :) 上述解决方案给出的问题不会那么可扩展。如果我们有 20 只动物,而不仅仅是 2 只呢?如果是200呢?2000?我们需要一种更通用的方法来做到这一点。这次的核心思想是使用reflect.首先,我们可以维护一个映射,它将类型名称映射到接口实现:mapper map[string]Animal{}然后我们放入我们的动物指针:mapper["dog"] = &Dog{}mapper["fish"] = &Fish{}现在,在我们将 JSON 解组AnimalCard并开始迭代之后,我们使用反射来初始化一个新的实例指针并将其解组:for _, card := range zoo.Animals {    // get the animal type pointer    animal := mapper[card.Type]    // get the pointer's type    animalType := reflect.TypeOf(animal)    // create a new instance pointer of the same type    newInstancePtr := reflect.New(animalType.Elem()).Interface().(Animal)    // unmarshal to the pointer    _ = json.Unmarshal(card.Property, newInstancePtr)    // assign the pointer back    card.Animal = newInstancePtr}游乐场示例在这里。

Qyouu

使用json.Unmarshaler接口创建自定义UnmarshalJSON方法。然后在该方法中,测试类型转换以查看哪种类型有效,分配并返回它。这篇文章末尾的好总结:http://attilaolah.eu/2013/11/29/json-decoding-in-go/
随时随地看视频慕课网APP

相关分类

Go
我要回答