如何将具有相同内部结构的两个 JSON 解组为一个单独的 Golang 结构体?

huangapple go评论95阅读模式
英文:

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:&quot;cast&quot;`
	MovieDetails []Detail `json:&quot;movie&quot;`
}

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 中的 CastMovie 字段。
  • 最好为 Details 提供一个访问函数(在这个例子中是 getDetails)。
英文:

Try anonymous field in struct:

package main

import (
	&quot;encoding/json&quot;
	&quot;fmt&quot;
	&quot;io/ioutil&quot;
)

type Detail struct {
	Name string `json:&quot;name&quot;`
	Url  string `json:&quot;url&quot;`
	Role string `json:&quot;role&quot;`
}

type Cast struct {
	Detail []Detail `json:&quot;cast&quot;`
}

type Movie struct {
	Detail []Detail `json:&quot;movie&quot;`
}

type Info struct {
	Cast
	Movie
}

func (i *Info) getDetails() []Detail {
	if len(i.Cast.Detail) &gt; 0 {
		return i.Cast.Detail
	}
	return i.Movie.Detail
}

func main() {
	cast, _ := ioutil.ReadFile(&quot;./cast.json&quot;)
	movie, _ := ioutil.ReadFile(&quot;./movie.json&quot;)
    
	var cInfo Info
	err := json.Unmarshal(cast, &amp;cInfo)
	fmt.Printf(&quot;cast: %+v\n&quot;, &amp;cInfo)
	fmt.Printf(&quot;err: %v\n&quot;, err)
	fmt.Printf(&quot;details: %v\n&quot;, cInfo.getDetails())

	var mInfo Info
	err = json.Unmarshal(movie, &amp;mInfo)
	fmt.Printf(&quot;movie: %+v\n&quot;, &amp;mInfo)
	fmt.Printf(&quot;err: %v\n&quot;, err)
	fmt.Printf(&quot;details: %v\n&quot;, 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(&quot;json&quot;)
		if tag == &quot;-&quot; {
			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.

huangapple
  • 本文由 发表于 2015年7月30日 20:02:09
  • 转载请务必保留本文链接:https://go.coder-hub.com/31723274.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定