How to parse an inner field in a nested JSON objec

2020-05-16 19:39发布

问题:

I have a JSON object similar to this one:

{
  "name": "Cain",
  "parents": {
    "mother" : "Eve",
    "father" : "Adam"
  }
}

Now I want to parse "name" and "mother" into this struct:

struct {
  Name String
  Mother String `json:"???"`
}

I want to specify the JSON field name with the json:... struct tag, however I don't know what to use as tag, because it is not the top object I am interested in. I found nothing about this in the encoding/json package docs nor in the popular blog post JSON and Go. I also tested mother, parents/mother and parents.mother.

回答1:

Unfortunately, unlike encoding/xml, the json package doesn't provide a way to access nested values. You'll want to either create a separate Parents struct or assign the type to be map[string]string. For example:

type Person struct {
    Name string
    Parents map[string]string
}

You could then provide a getter for mother as so:

func (p *Person) Mother() string {
    return p.Parents["mother"]
}

This may or may not play into your current codebase and if refactoring the Mother field to a method call is not on the menu, then you may want to create a separate method for decoding and conforming to your current struct.



回答2:

You could use structs so long as your incoming data isn't too dynamic.

http://play.golang.org/p/bUZ8l6WgvL

package main

import (
    "fmt"
    "encoding/json"
    )

type User struct {
    Name string
    Parents struct {
        Mother string
        Father string
    }
}

func main() {
    encoded := `{
        "name": "Cain",
        "parents": {
            "mother": "Eve",
            "father": "Adam"
        }
    }`

    // Decode the json object
    u := &User{}
    err := json.Unmarshal([]byte(encoded), &u)
    if err != nil {
        panic(err)
    }

    // Print out mother and father
    fmt.Printf("Mother: %s\n", u.Parents.Mother)
    fmt.Printf("Father: %s\n", u.Parents.Father)
}


回答3:

Here's some code I baked up real quick in the Go Playground

http://play.golang.org/p/PiWwpUbBqt

package main

import (
    "fmt"
    "encoding/json"
    )

func main() {
    encoded := `{
        "name": "Cain",
        "parents": {
            "mother": "Eve"
            "father": "Adam"
        }
    }`

    // Decode the json object
    var j map[string]interface{}
    err := json.Unmarshal([]byte(encoded), &j)
    if err != nil {
        panic(err)
    }

    // pull out the parents object
    parents := j["parents"].(map[string]interface{})

    // Print out mother and father
    fmt.Printf("Mother: %s\n", parents["mother"].(string))
    fmt.Printf("Father: %s\n", parents["father"].(string))
}

There might be a better way. I'm looking forward to seeing the other answers. :-)



回答4:

More recently, gjson supports selection of nested JSON properties.

name := gjson.Get(json, "name")
mother := gjson.Get(json, "parents.mother")


回答5:

How about using an intermediary struct as the one suggested above for parsing, and then putting the relevant values in your "real" struct?

import (
    "fmt"
    "encoding/json"
    )

type MyObject struct{
  Name string
  Mother string
}

type MyParseObj struct{
   Name string
   Parents struct {
         Mother string
         Father string
   } 
}


func main() {
    encoded := `{
         "name": "Cain",
         "parents": {
             "mother": "Eve",
             "father": "Adam"
         }
    }`

    pj := &MyParseObj{}
    if err := json.Unmarshal([]byte(encoded), pj); err != nil {
        return
    }
    final := &MyObject{Name: pj.Name, Mother: pj.Parents.Mother}
    fmt.Println(final)  
}


标签: json go