【问题标题】:Unmarshaling nested JSON objects解组嵌套的 JSON 对象
【发布时间】:2014-02-11 15:06:48
【问题描述】:

topic 上有 a few questions 但似乎没有一个涵盖我的情况,因此我正在创建一个新的。

我有如下 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"`

我见过有人使用地图,但我不是这样。我基本上不关心foo(这是一个大对象)的内容,除了一些特定的元素。

在这种情况下,正确的方法是什么?我不是在寻找奇怪的黑客,因此,如果这是要走的路,我可以接受。

【问题讨论】:

    标签: json go


    【解决方案1】:

    有没有办法解组嵌套的 bar 属性并将其直接分配给结构属性而不创建嵌套结构?

    不,encoding/json 不能像 encoding/xml 那样使用 ">some>deep>childnode" 来解决问题。 嵌套结构是要走的路。

    【讨论】:

    • 为什么这与 encoding/xml 不同?
    • @CalebThompson XML 和 JSON 的结构完全不同,即使简单的情况看起来很相似。 XML 标记的内容有点:(子标记或文本的有序映射)和属性的无序映射。 JSON 更像是一个 Go 结构。因此将 JSON 映射到结构要简单得多:只需在 JSON 之后对结构进行建模即可。
    • 在我的例子中,JSON 的结构实际上并没有决定,所以我可以创建一个结构,当我使用 [string]interface{} 的映射解析它时,我遇到了嵌套元素的问题。可以做什么?
    • 但是为什么我们不能解组到结构内部的结构呢?
    【解决方案2】:

    就像 Volker 提到的那样,嵌套结构是要走的路。但是如果你真的不想要嵌套结构,你可以重写 UnmarshalJSON 函数。

    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
    }
    

    请忽略我没有返回正确错误的事实。为了简单起见,我把它省略了。

    更新:正确检索“更多”值。

    【讨论】:

    • 我得到了 &{FooBar:1 FooBaz:2 More:}。缺少“文字”
    • @GuySegev 我继续更新我的答案以解决该问题。感谢您指出这一点。
    【解决方案3】:

    这是一个如何从 Safebrowsing v4 API sbserver 代理服务器解组 JSON 响应的示例: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)
    }
    

    【讨论】:

    • 感谢您展示 json.Unmarshal 可以解组复杂的深层嵌套 json 数据。我的问题是我正在从文件中读取 JSON 并以一些零填充结束。很高兴你分享了这个!
    【解决方案4】:

    是的。有了gjson,您现在要做的就是:

    bar := gjson.Get(json, "foo.bar")

    bar 如果你愿意,可以是一个结构属性。另外,没有地图。

    【讨论】:

    • fastjson 也允许使用相同的技巧:fastjson.GetString(json, "foo", "bar")
    • 正是我想要的,简单易用
    • 很棒的图书馆,感谢您的介绍!
    【解决方案5】:

    匿名字段呢?我不确定这是否会构成“嵌套结构”,但它比嵌套结构声明更干净。如果你想在其他地方重用嵌套元素怎么办?

    type NestedElement struct{
        someNumber int `json:"number"`
        someString string `json:"string"`
    }
    
    type BaseElement struct {
        NestedElement `json:"bar"`
    }
    

    【讨论】:

      【解决方案6】:

      将嵌套json的值赋值给struct,直到你知道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)
      }
      

      【讨论】:

      • 要是这能和kubebuilder一起工作就好了!
      • @ivandov 告诉我你的代码需要什么?
      【解决方案7】:

      我正在做这样的事情。但仅适用于从 proto 生成的结构。 https://github.com/flowup-labs/grpc-utils

      在你的原型中

      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"
      }
      

      【讨论】:

      • 添加几行来解释这如何回答问题。如果 repo 被删除,则答案中没有任何值。
      • 我不认为他会回来,伙计们。
      【解决方案8】:

      结合 map 和 struct 允许解组嵌套的 JSON 对象,其中键是动态的。 => 地图[字符串]

      例如: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 应用程序

      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)
      }
      

      散列中的动态键是句柄字符串,嵌套对象用结构体表示。

      【讨论】:

      • 这似乎不完整。 raw 未使用
      • 在这段代码中最好为 Symbol 引入新类型。它不是任何字符串。这也有助于在帐户映射中维护类型化的关系。 golang type Account map[Symbol]Stock
      猜你喜欢
      • 2013-04-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-05-18
      • 2020-11-15
      • 2018-06-30
      • 1970-01-01
      • 2015-04-23
      相关资源
      最近更新 更多