解组嵌套的JSON对象

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

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) = &quot;name.firstname&quot;];
  PseudoFirstname string = 2 [(gogoproto.jsontag) = &quot;lastname&quot;];
  EmbedMsg = 3  [(gogoproto.nullable) = false, (gogoproto.embed) = true];
  Lastname string = 4 [(gogoproto.jsontag) = &quot;name.lastname&quot;];
  Inside string  = 5 [(gogoproto.jsontag) = &quot;name.inside.a.b.c&quot;];
}

message EmbedMsg{
   Opt1 string = 1 [(gogoproto.jsontag) = &quot;opt1&quot;];
}

Then your output will be

<!-- language: json-->

{
&quot;lastname&quot;: &quot;Three&quot;,
&quot;name&quot;: {
	&quot;firstname&quot;: &quot;One&quot;,
	&quot;inside&quot;: {
		&quot;a&quot;: {
			&quot;b&quot;: {
				&quot;c&quot;: &quot;goo&quot;
			}
		}
	},
	&quot;lastname&quot;: &quot;Two&quot;
},
&quot;opt1&quot;: &quot;var&quot;
}

答案8

得分: 0

有点晚了,但是我会给出一个关于如何在Golang中使用GitLabGraphQL APIjson响应的示例来帮助你:

假设我们使用以下请求向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:

{
  &quot;data&quot;: {
    &quot;projects&quot;: {
      &quot;count&quot;: 4,
      &quot;nodes&quot;: [
        {
          &quot;fullPath&quot;: &quot;mygroup/proj1&quot;
        },
        {
          &quot;fullPath&quot;: &quot;mygroup/proj2&quot;
        },
        {
          &quot;fullPath&quot;: &quot;mygroup/proj3&quot;
        },
        {
          &quot;fullPath&quot;: &quot;mygroup/proj4&quot;
        }
      ]
    }
  }
}

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:&quot;data&quot;` //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:&quot;count&quot;`
		Nodes []map[string]string `json:&quot;nodes&quot;`
	} `json:&quot;projects&quot;`
}

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 &amp;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[&quot;fullPath&quot;])

 }
}

答案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

{
  &quot;MU&quot;: {
    &quot;symbol&quot;: &quot;MU&quot;,
    &quot;title&quot;: &quot;micro semiconductor&quot;,
    &quot;share&quot;: 400,
    &quot;purchase_price&quot;: 60.5,
    &quot;target_price&quot;: 70
  },
  &quot;LSCC&quot;:{
    &quot;symbol&quot;: &quot;LSCC&quot;,
    &quot;title&quot;: &quot;lattice semiconductor&quot;,
    &quot;share&quot;: 200,
    &quot;purchase_price&quot;: 20,
    &quot;target_price&quot;: 30
  }
}

Go application

package main

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

type Stock struct {
	Symbol        string  `json:&quot;symbol&quot;`
	Title         string  `json:&quot;title&quot;`
	Share         int     `json:&quot;share&quot;`
	PurchasePrice float64 `json:&quot;purchase_price&quot;`
	TargetPrice   float64 `json:&quot;target_price&quot;`
}
type Account map[string]Stock

func main() {
	raw, err := ioutil.ReadFile(&quot;stock.json&quot;)
	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.

huangapple
  • 本文由 发表于 2014年1月22日 04:05:58
  • 转载请务必保留本文链接:https://go.coder-hub.com/21268000.html
匿名

发表评论

匿名网友

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

确定