2 回答

TA贡献1797条经验 获得超4个赞
根据您提供给我们的当前条件,没有直接的方法可以实现您想要的目标。@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
}
游乐场示例在这里。

TA贡献1786条经验 获得超11个赞
使用json.Unmarshaler
接口创建自定义UnmarshalJSON
方法。然后在该方法中,测试类型转换以查看哪种类型有效,分配并返回它。
这篇文章末尾的好总结:
http://attilaolah.eu/2013/11/29/json-decoding-in-go/
- 2 回答
- 0 关注
- 193 浏览
添加回答
举报