英文:
How to unmarshal two json with same internal structure into one single golang struct?
问题
我有两个具有以下结构的JSON文件:
{
"cast": [
{
"url": "carey-mulligan",
"name": "Carey Mulligan",
"role": "Actress"
},
{
"url": "leonardo-dicaprio",
"name": "Leonardo DiCaprio",
"role": "Actor"
},
...
]
}
和
{
"movie": [
{
"url": "carey-mulligan",
"name": "Carey Mulligan",
"role": "Actress"
},
{
"url": "leonardo-dicaprio",
"name": "Leonardo DiCaprio",
"role": "Actor"
},
...
]
}
你可以看到,JSON的内部结构对于"cast"和"movie"是相同的。我想将这些JSON文件解组成相同的Golang结构,但是我不能为同一个结构元素给出两个名称标签("cast"和"movie")。我想要类似这样的结构:
type Detail struct {
Name string `json:"name"`
Url string `json:"url"`
Role string `json:"role"`
}
type Info struct {
Detail []Detail `json:"cast or movie"`
}
在这种情况下,Detail可以解析cast和movie。
以下是我的当前代码:
// RIMAGE project main.go
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
)
const (
website = "https://data.moviebuff.com/"
)
func main() {
fmt.Println("Hello World!")
content, err := ioutil.ReadFile("data/great-getsby")
if err != nil {
panic(err)
}
var info Info
err = json.Unmarshal(content, &info)
if err != nil {
panic(err)
}
fmt.Println(info.Detail)
}
type Detail struct {
Name string `json:"name"`
Url string `json:"url"`
Role string `json:"role"`
}
type Info struct {
Detail []Detail `json:"cast" json:"movie"`
}
但是它只适用于第一个标签"cast",如果JSON包含"movie",则返回nil。
提前感谢。
英文:
I have two json files with following structure
{
"cast": [
{
"url": "carey-mulligan",
"name": "Carey Mulligan",
"role": "Actress"
},
{
"url": "leonardo-dicaprio",
"name": "Leonardo DiCaprio",
"role": "Actor"
},
.
.
.
]
}
and
{
"movie": [
{
"url": "carey-mulligan",
"name": "Carey Mulligan",
"role": "Actress"
},
{
"url": "leonardo-dicaprio",
"name": "Leonardo DiCaprio",
"role": "Actor"
},
.
.
.
]
}
as you can see internal structure of the json is same for cast and movie. I want to unmarshel these json file into the same golang structure. But i am not able to give two name tags (cast and movie) for same struct element. I want something like
type Detail struct {
Name string `json:"name"`
Url string `json:"url"`
Role string `json:"role"`
}
type Info struct {
Detail []Detail `json:"cast or movie"`
}
In which case Detail could parse both cast and movie.
Here is my current code
// RIMAGE project main.go
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
)
const (
website = "https://data.moviebuff.com/"
)
func main() {
fmt.Println("Hello World!")
content, err := ioutil.ReadFile("data/great-getsby")
if err != nil {
panic(err)
}
var info Info
err = json.Unmarshal(content, &info)
if err != nil {
panic(err)
}
fmt.Println(info.Detail)
}
type Detail struct {
Name string `json:"name"`
Url string `json:"url"`
Role string `json:"role"`
}
type Info struct {
Detail []Detail `json:"cast" json:"movie"
}
but it only works for first tag "cast" and gives nill in case json contain the movie.
Thanks in advance.
答案1
得分: 3
你可以使用type Info map[string][]Detail
来替代你的结构体。
在<kbd>Go playground</kbd>上尝试一下。
或者你可以在你的结构体中同时使用这两种类型,并创建一个名为Details()
的方法来返回正确的类型:
type Info struct {
CastDetails []Detail `json:"cast"`
MovieDetails []Detail `json:"movie"`
}
func (i Info) Details() []Detail {
if i.CastDetails == nil {
return i.MovieDetails
}
return i.CastDetails
}
在<kbd>Go playground</kbd>上尝试一下。
英文:
You can use type Info map[string][]Detail
instead of your struct.
Try it on the <kbd>Go playground</kbd>
Or you can use both types in your structure, and make method Details()
which will return right one:
type Info struct {
CastDetails []Detail `json:"cast"`
MovieDetails []Detail `json:"movie"`
}
func (i Info) Details() []Detail {
if i.CastDetails == nil {
return i.MovieDetails
}
return i.CastDetails
}
Try it on the <kbd>Go playground</kbd>
答案2
得分: 0
尝试在结构体中使用匿名字段:
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
)
type Detail struct {
Name string `json:"name"`
Url string `json:"url"`
Role string `json:"role"`
}
type Cast struct {
Detail []Detail `json:"cast"`
}
type Movie struct {
Detail []Detail `json:"movie"`
}
type Info struct {
Cast
Movie
}
func (i *Info) getDetails() []Detail {
if len(i.Cast.Detail) > 0 {
return i.Cast.Detail
}
return i.Movie.Detail
}
func main() {
cast, _ := ioutil.ReadFile("./cast.json")
movie, _ := ioutil.ReadFile("./movie.json")
var cInfo Info
err := json.Unmarshal(cast, &cInfo)
fmt.Printf("cast: %+v\n", &cInfo)
fmt.Printf("err: %v\n", err)
fmt.Printf("details: %v\n", cInfo.getDetails())
var mInfo Info
err = json.Unmarshal(movie, &mInfo)
fmt.Printf("movie: %+v\n", &mInfo)
fmt.Printf("err: %v\n", err)
fmt.Printf("details: %v\n", mInfo.getDetails())
}
需要注意的事项:
- 多了一层间接访问:要访问
Details
字段,首先需要先访问Info
中的Cast
或Movie
字段。 - 最好为
Details
提供一个访问函数(在这个例子中是getDetails
)。
英文:
Try anonymous field in struct:
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
)
type Detail struct {
Name string `json:"name"`
Url string `json:"url"`
Role string `json:"role"`
}
type Cast struct {
Detail []Detail `json:"cast"`
}
type Movie struct {
Detail []Detail `json:"movie"`
}
type Info struct {
Cast
Movie
}
func (i *Info) getDetails() []Detail {
if len(i.Cast.Detail) > 0 {
return i.Cast.Detail
}
return i.Movie.Detail
}
func main() {
cast, _ := ioutil.ReadFile("./cast.json")
movie, _ := ioutil.ReadFile("./movie.json")
var cInfo Info
err := json.Unmarshal(cast, &cInfo)
fmt.Printf("cast: %+v\n", &cInfo)
fmt.Printf("err: %v\n", err)
fmt.Printf("details: %v\n", cInfo.getDetails())
var mInfo Info
err = json.Unmarshal(movie, &mInfo)
fmt.Printf("movie: %+v\n", &mInfo)
fmt.Printf("err: %v\n", err)
fmt.Printf("details: %v\n", mInfo.getDetails())
}
Things to note:
- One more level of indirection: to access 'Details' field, you need to access either 'Cast' or 'Movie' field first in Info first.
- Better provide an access function for 'Details' ('getDetail' in this example)
答案3
得分: -1
如果你深入研究encoding/json
,你会找到https://github.com/golang/go/blob/master/src/encoding/json/encode.go和以下内容:
tag := sf.Tag.Get("json")
if tag == "-" {
continue
}
所以它获取一个json标签并继续执行。
你也可以选择使用RoninDev的解决方案,在完成后将其复制过来。
英文:
If you dig far enough down into encoding/json
you'll get to https://github.com/golang/go/blob/master/src/encoding/json/encode.go and the following:
tag := sf.Tag.Get("json")
if tag == "-" {
continue
}
So it gets one json tag and keeps on going.
You could always go with RoninDev's solution and just copy it over when done.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论