英文:
Unmarshaling nested JSON objects
问题
有一些关于这个主题的问题,但似乎没有一个涵盖我的情况,所以我正在创建一个新的问题。
我有以下的JSON数据:
{"foo":{ "bar": "1", "baz": "2" }, "more": "text"}
有没有一种方法可以将嵌套的bar属性解组并直接赋值给一个结构体属性,而不需要创建一个嵌套的结构体?
我目前采用的解决方案如下:
type Foo struct {
More String `json:"more"`
Foo struct {
Bar string `json:"bar"`
Baz string `json:"baz"`
} `json:"foo"`
// FooBar string `json:"foo.bar"`
}
这是一个简化版本,请忽略冗长的部分。正如你所看到的,我希望能够解析并将值赋给
// FooBar string `json:"foo.bar"`
我看到有人使用了一个map,但那不是我的情况。除了一些特定的元素,我基本上不关心foo
的内容(它是一个大对象)。
在这种情况下,正确的方法是什么?我不想要奇怪的技巧,所以如果这是正确的方法,我可以接受。
英文:
There are a few questions on the topic but none of them seem to cover my case, thus I'm creating a new one.
I have JSON like the following:
{"foo":{ "bar": "1", "baz": "2" }, "more": "text"}
Is there a way to unmarshal the nested bar property and assign it directly to a struct property without creating a nested struct?
The solution I'm adopting right now is the following:
type Foo struct {
More String `json:"more"`
Foo struct {
Bar string `json:"bar"`
Baz string `json:"baz"`
} `json:"foo"`
// FooBar string `json:"foo.bar"`
}
This is a simplified version, please ignore the verbosity. As you can see, I'd like to be able to parse and assign the value to
// FooBar string `json:"foo.bar"`
I've seen people using a map, but that's not my case. I basically don't care about the content of foo
(which is a large object), except for a few specific elements.
What is the correct approach in this case? I'm not looking for weird hacks, thus if this is the way to go, I'm fine with that.
答案1
得分: 98
有没有一种方法可以将嵌套的bar属性解组并直接赋值给一个结构体属性,而不需要创建一个嵌套的结构体?
不,encoding/json不能像encoding/xml那样处理“>some>deep>childnode”这样的情况。
嵌套结构体是解决这个问题的方法。
英文:
> Is there a way to unmarshal the nested bar property and assign it directly to a struct property without creating a nested struct?
No, encoding/json cannot do the trick with ">some>deep>childnode" like encoding/xml can do.
Nested structs is the way to go.
答案2
得分: 51
像Volker提到的那样,嵌套结构是一个可行的方法。但是,如果你真的不想使用嵌套结构,你可以重写UnmarshalJSON函数。
type A struct {
FooBar string // 获取foo.bar
FooBaz string // 获取foo.baz
More string
}
func (a *A) UnmarshalJSON(b []byte) error {
var f interface{}
json.Unmarshal(b, &f)
m := f.(map[string]interface{})
foomap := m["foo"]
v := foomap.(map[string]interface{})
a.FooBar = v["bar"].(string)
a.FooBaz = v["baz"].(string)
a.More = m["more"].(string)
return nil
}
请忽略我没有返回正确的错误的事实。为了简单起见,我省略了这部分。
更新:正确获取"more"的值。
英文:
Like what Volker mentioned, nested structs is the way to go. But if you really do not want nested structs, you can override the UnmarshalJSON func.
https://play.golang.org/p/dqn5UdqFfJt
type A struct {
FooBar string // takes foo.bar
FooBaz string // takes foo.baz
More string
}
func (a *A) UnmarshalJSON(b []byte) error {
var f interface{}
json.Unmarshal(b, &f)
m := f.(map[string]interface{})
foomap := m["foo"]
v := foomap.(map[string]interface{})
a.FooBar = v["bar"].(string)
a.FooBaz = v["baz"].(string)
a.More = m["more"].(string)
return nil
}
Please ignore the fact that I'm not returning a proper error. I left that out for simplicity.
UPDATE: Correctly retrieving "more" value.
答案3
得分: 29
这是一个从Safebrowsing v4 API sbserver代理服务器解析JSON响应的示例:https://play.golang.org/p/4rGB5da0Lt
// 这个示例展示了如何从Safebrowsing v4 sbserver解析JSON请求
package main
import (
"fmt"
"log"
"encoding/json"
)
// sbserver POST请求的响应
type Results struct {
Matches []Match
}
// sbserver响应中的嵌套结构
type Match struct {
ThreatType string
PlatformType string
ThreatEntryType string
Threat struct {
URL string
}
}
func main() {
fmt.Println("Hello, playground")
// 示例POST请求
// curl -X POST -H 'Content-Type: application/json'
// -d '{"threatInfo": {"threatEntries": [{"url": "http://testsafebrowsing.appspot.com/apiv4/ANY_PLATFORM/MALWARE/URL/"}]}}'
// http://127.0.0.1:8080/v4/threatMatches:find
// 示例JSON响应
jsonResponse := `{"matches":[{"threatType":"MALWARE","platformType":"ANY_PLATFORM","threatEntryType":"URL","threat":{"url":"http://testsafebrowsing.appspot.com/apiv4/ANY_PLATFORM/MALWARE/URL/"}}]}`
res := &Results{}
err := json.Unmarshal([]byte(jsonResponse), res)
if err != nil {
log.Fatal(err)
}
fmt.Printf("%v\n", res)
fmt.Printf("\t威胁类型: %s\n", res.Matches[0].ThreatType)
fmt.Printf("\t平台类型: %s\n", res.Matches[0].PlatformType)
fmt.Printf("\t威胁条目类型: %s\n", res.Matches[0].ThreatEntryType)
fmt.Printf("\tURL: %s\n", res.Matches[0].Threat.URL)
}
以上是代码的翻译部分。
英文:
This is an example of how to unmarshall JSON responses from the Safebrowsing v4 API sbserver proxy server: https://play.golang.org/p/4rGB5da0Lt
// this example shows how to unmarshall JSON requests from the Safebrowsing v4 sbserver
package main
import (
"fmt"
"log"
"encoding/json"
)
// response from sbserver POST request
type Results struct {
Matches []Match
}
// nested within sbserver response
type Match struct {
ThreatType string
PlatformType string
ThreatEntryType string
Threat struct {
URL string
}
}
func main() {
fmt.Println("Hello, playground")
// sample POST request
// curl -X POST -H 'Content-Type: application/json'
// -d '{"threatInfo": {"threatEntries": [{"url": "http://testsafebrowsing.appspot.com/apiv4/ANY_PLATFORM/MALWARE/URL/"}]}}'
// http://127.0.0.1:8080/v4/threatMatches:find
// sample JSON response
jsonResponse := `{"matches":[{"threatType":"MALWARE","platformType":"ANY_PLATFORM","threatEntryType":"URL","threat":{"url":"http://testsafebrowsing.appspot.com/apiv4/ANY_PLATFORM/MALWARE/URL/"}}]}`
res := &Results{}
err := json.Unmarshal([]byte(jsonResponse), res)
if(err!=nil) {
log.Fatal(err)
}
fmt.Printf("%v\n",res)
fmt.Printf("\tThreat Type: %s\n",res.Matches[0].ThreatType)
fmt.Printf("\tPlatform Type: %s\n",res.Matches[0].PlatformType)
fmt.Printf("\tThreat Entry Type: %s\n",res.Matches[0].ThreatEntryType)
fmt.Printf("\tURL: %s\n",res.Matches[0].Threat.URL)
}
答案4
得分: 28
是的。使用gjson,你现在只需要做以下操作:
bar := gjson.Get(json, "foo.bar")
如果你愿意,bar
可以是一个结构体属性。而且,不需要使用映射。
英文:
Yes. With gjson all you have to do now is:
bar := gjson.Get(json, "foo.bar")
bar
could be a struct property if you like. Also, no maps.
答案5
得分: 9
匿名字段是什么情况?我不确定它是否构成了一个"嵌套结构体",但它比有一个嵌套的结构体声明更清晰。如果你想在其他地方重用嵌套元素怎么办?
type NestedElement struct{
someNumber int `json:"number"`
someString string `json:"string"`
}
type BaseElement struct {
NestedElement `json:"bar"`
}
匿名字段允许将NestedElement
结构体嵌入到BaseElement
结构体中,而无需显式声明字段名称。这样可以在BaseElement
中直接访问NestedElement
的字段和方法。如果你想在其他地方重用NestedElement
,只需将其作为匿名字段嵌入到其他结构体中即可。
英文:
What about anonymous fields? I'm not sure if that will constitute a "nested struct" but it's cleaner than having a nested struct declaration. What if you want to reuse the nested element elsewhere?
type NestedElement struct{
someNumber int `json:"number"`
someString string `json:"string"`
}
type BaseElement struct {
NestedElement `json:"bar"`
}
答案6
得分: 9
将嵌套的json
的值分配给结构体,直到你知道json键的底层类型为止:
package main
import (
"encoding/json"
"fmt"
)
// Object
type Object struct {
Foo map[string]map[string]string `json:"foo"`
More string `json:"more"`
}
func main() {
someJSONString := []byte(`{"foo":{"bar":"1","baz":"2"},"more":"text"}`)
var obj Object
err := json.Unmarshal(someJSONString, &obj)
if err != nil {
fmt.Println(err)
}
fmt.Println("jsonObj", obj)
}
英文:
Assign the values of nested json
to struct until you know the underlying type of json keys:-
package main
import (
"encoding/json"
"fmt"
)
// Object
type Object struct {
Foo map[string]map[string]string `json:"foo"`
More string `json:"more"`
}
func main(){
someJSONString := []byte(`{"foo":{ "bar": "1", "baz": "2" }, "more": "text"}`)
var obj Object
err := json.Unmarshal(someJSONString, &obj)
if err != nil{
fmt.Println(err)
}
fmt.Println("jsonObj", obj)
}
答案7
得分: 0
我正在为您翻译以下内容:
我正在处理类似这样的东西。但是只能处理从proto生成的结构。
在您的proto文件中:
message Msg {
Firstname string = 1 [(gogoproto.jsontag) = "name.firstname"];
PseudoFirstname string = 2 [(gogoproto.jsontag) = "lastname"];
EmbedMsg = 3 [(gogoproto.nullable) = false, (gogoproto.embed) = true];
Lastname string = 4 [(gogoproto.jsontag) = "name.lastname"];
Inside string = 5 [(gogoproto.jsontag) = "name.inside.a.b.c"];
}
message EmbedMsg{
Opt1 string = 1 [(gogoproto.jsontag) = "opt1"];
}
然后,您的输出将是:
{
"lastname": "Three",
"name": {
"firstname": "One",
"inside": {
"a": {
"b": {
"c": "goo"
}
}
},
"lastname": "Two"
},
"opt1": "var"
}
英文:
I was working on something like this. But is working only with structures generated from proto.
https://github.com/flowup-labs/grpc-utils
in your proto
<!-- language: golang-->
message Msg {
Firstname string = 1 [(gogoproto.jsontag) = "name.firstname"];
PseudoFirstname string = 2 [(gogoproto.jsontag) = "lastname"];
EmbedMsg = 3 [(gogoproto.nullable) = false, (gogoproto.embed) = true];
Lastname string = 4 [(gogoproto.jsontag) = "name.lastname"];
Inside string = 5 [(gogoproto.jsontag) = "name.inside.a.b.c"];
}
message EmbedMsg{
Opt1 string = 1 [(gogoproto.jsontag) = "opt1"];
}
Then your output will be
<!-- language: json-->
{
"lastname": "Three",
"name": {
"firstname": "One",
"inside": {
"a": {
"b": {
"c": "goo"
}
}
},
"lastname": "Two"
},
"opt1": "var"
}
答案8
得分: 0
有点晚了,但是我会给出一个关于如何在Golang中使用GitLab
的GraphQL API
的json
响应的示例来帮助你:
假设我们使用以下请求向GitLab发出API请求:
query{
projects{
count,
nodes{
fullPath
}
}
}
这将给我们一个类似以下的响应:
{
"data": {
"projects": {
"count": 4,
"nodes": [
{
"fullPath": "mygroup/proj1"
},
{
"fullPath": "mygroup/proj2"
},
{
"fullPath": "mygroup/proj3"
},
{
"fullPath": "mygroup/proj4"
}
]
}
}
}
因此,在我们的响应中,我们可以分析并将其与Go数据类型进行匹配:
data
:作为所有节点的父节点
projects
:它包含一个map[string]int
类型的count
和一个[]map[string]string
类型的nodes
,即一个切片
有了这些信息,我们可以创建结构体来映射响应:
对于根节点data
,我们可以创建以下结构体:
type projectData struct {
Data struct { //因为data可能包含其他嵌套的内容
projects //表示projects结构的结构体
} `json:"data"` //只是映射名称
}
以及包含来自API响应中projects
节点的实际数据的结构体:
type projects struct {
Projects struct {
Count int `json:"count"`
Nodes []map[string]string `json:"nodes"`
} `json:"projects"`
}
为了测试所有这些,这里是json.Unmarshal
和使用方法:
func projects(resp []byte){
var p projectData // root节点结构体的变量
if err := json.Unmarshal(resp, &p); err != nil {
fmt.Println(err)
}
fmt.Println(p.Data.Projects.Count) // 打印出项目的总数
for _, v := range p.Data.Projects.Nodes { //循环遍历所有项目并打印fullPath字段
fmt.Println(v["fullPath"])
}
}
英文:
A little late but trying to help by giving an example of GitLab
GraphQL API
json
response and how to use it in Golang:
suppose we do an API request to GitLab with this request:
query{
projects{
count,
nodes{
fullPath
}
}
}
Which will give us a response like this:
{
"data": {
"projects": {
"count": 4,
"nodes": [
{
"fullPath": "mygroup/proj1"
},
{
"fullPath": "mygroup/proj2"
},
{
"fullPath": "mygroup/proj3"
},
{
"fullPath": "mygroup/proj4"
}
]
}
}
}
So in our response we can analyze and match the response with Go data types:
data
: as the parent of all (root node)
projects
: which holds a map[string]int
(count)
and a []map[string]string
(nodes)
which is a slice
Given this, we can create structs to map the response:
For the root node data
we can create this struct:
type projectData struct {
Data struct { // because data can consist of other nested stuff
projects //a struct which represents the structure of the projects
} `json:"data"` //just mapping names
}
And the struct which holds the actual data from the projects
node in our API response:
type projects struct {
Projects struct {
Count int `json:"count"`
Nodes []map[string]string `json:"nodes"`
} `json:"projects"`
}
And to test all of this, here is the json.Unmarshal
and the usage:
func projects(resp []byte){
var p projectData // variable of type root node struct
if err := json.Unmarshal(resp &p); err != nil {
fmt.Println(err)
}
fmt.Println(p.Data.Projects.Count) // print out the total count of projects
for _, v := range p.Data.Projects.Nodes { //loop over all projects and print the fullPath field
fmt.Println(v["fullPath"])
}
}
答案9
得分: -1
组合使用map和struct可以解析具有动态键的嵌套JSON对象。
=> map[string]<struct>
例如:stock.json
{
"MU": {
"symbol": "MU",
"title": "微型半导体",
"share": 400,
"purchase_price": 60.5,
"target_price": 70
},
"LSCC":{
"symbol": "LSCC",
"title": "晶格半导体",
"share": 200,
"purchase_price": 20,
"target_price": 30
}
}
Go应用程序
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"log"
"os"
)
type Stock struct {
Symbol string `json:"symbol"`
Title string `json:"title"`
Share int `json:"share"`
PurchasePrice float64 `json:"purchase_price"`
TargetPrice float64 `json:"target_price"`
}
type Account map[string]Stock
func main() {
raw, err := ioutil.ReadFile("stock.json")
if err != nil {
fmt.Println(err.Error())
os.Exit(1)
}
var account Account
log.Println(account)
}
哈希中的动态键由字符串处理,嵌套对象由struct表示。
英文:
Combining map and struct allow unmarshaling nested JSON objects where the key is dynamic.
=> map[string]<struct>
For example: stock.json
{
"MU": {
"symbol": "MU",
"title": "micro semiconductor",
"share": 400,
"purchase_price": 60.5,
"target_price": 70
},
"LSCC":{
"symbol": "LSCC",
"title": "lattice semiconductor",
"share": 200,
"purchase_price": 20,
"target_price": 30
}
}
Go application
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"log"
"os"
)
type Stock struct {
Symbol string `json:"symbol"`
Title string `json:"title"`
Share int `json:"share"`
PurchasePrice float64 `json:"purchase_price"`
TargetPrice float64 `json:"target_price"`
}
type Account map[string]Stock
func main() {
raw, err := ioutil.ReadFile("stock.json")
if err != nil {
fmt.Println(err.Error())
os.Exit(1)
}
var account Account
log.Println(account)
}
The dynamic key in the hash is handle a string, and the nested object is represented by a struct.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论