1

Team, new to Programming. I have data available after unmarshaling the Json as shown below, which has nested Key values. flat key values I am able to access, how do I access nested key values. Here is the byte slice data shown below after unmarshaling —>

tables:[map[name:basic__snatpool_members] map[name:net__snatpool_members] map[name:optimizations__hosts] map[columnNames:[name] name:pool__hosts rows:[map[row:[ry.hj.com]]]] traffic_group:/Common/traffic-group-1

Flat key values I am able to access by using the following code

p.TrafficGroup = m[“traffic_group”].(string)

here is the complete function

func dataToIapp(name string, d *schema.ResourceData) bigip.Iapp {
        var p bigip.Iapp

        var obj interface{}

        jsonblob := []byte(d.Get("jsonfile").(string))
        err := json.Unmarshal(jsonblob, &obj)
        if err != nil {
                fmt.Println("error", err)
        }
        m := obj.(map[string]interface{}) // Important: to access property
        p.Name = m[“name”].(string)
        p.Partition = m[“partition”].(string)

        p.InheritedDevicegroup = m[“inherited_devicegroup”].(string)

}
1
  • 1
    Please show the raw JSON, and the definition of Iapp. If you know the JSON structure in advance, it's more common to unmarshal directly into a structure. See here. Commented Jan 22, 2018 at 19:46

3 Answers 3

0

Note: This may not work with your JSON structure. I inferred what it would be based on your question but without the actual structure, I cannot guarantee this to work without modification.

If you want to access them in a map, you need to assert that the interface pulled from the first map is actually a map. So you would need to do this:

tmp := m["tables"]
tables, ok := tmp.(map[string]string)
if !ok {
    //error handling here
}

r.Name = tables["name"].(string)

But instead of accessing the unmarshaled JSON as a map[string]interface{}, why don't you create structs that match your JSON output?

type JSONRoot struct {
    Name string `json:"name"`
    Partition string `json:"partition"`
    InheritedDevicegroup string `json:"inherited_devicegroup"`
    Tables map[string]string `json:"tables"` //Ideally, this would be a map of structs
}

Then in your code:

func dataToIapp(name string, d *schema.ResourceData) bigip.Iapp {
    var p bigip.Iapp

    var obj &JSONRoot{}

    jsonblob := []byte(d.Get("jsonfile").(string))
    err := json.Unmarshal(jsonblob, &obj)
    if err != nil {
            fmt.Println("error", err)
    }

    p.Name = obj.Name
    p.Partition = obj.Partition

    p.InheritedDevicegroup = obj.InheritedDevicegroup

    p.Name = obj.Tables["name"]
}
Sign up to request clarification or add additional context in comments.

Comments

0

JSON objects are unmarshaled into map[string]interface{}, JSON arrays into []interface{}, same applies for nested objects/arrays.

So for example if a key/index maps to a nested object you need to type assert the value to map[string]interface{} and if the key/index maps to an array of objects you first need to assert the value to []interface{} and then each element to map[string]interface{}.

e.g. (for brevity this code is not guarding against panic)

tables := obj.(map[string]interface{})["tables"]
table1 := tables.([]interface{})[0]
name := table1.(map[string]interface{})["name"]
namestr := name.(string)

However, if it's the case that the json you are parsing is not dynamic but instead has a specific structure you should define a struct type that mirrors that structure and unmarshal the json into that.

Comments

0

All you have to do is repeatedly accessing the map via type-switching or assertion:

for _, table := range m["tables"] {
    switch val := table {
        case string:
                fmt.Println("table is string")
        case int:
                fmt.Println("table is integer")

        // This is your case, since JSON is unmarshaled to type []interface{} and map[string]interface{}
        case []interface{}:
                fmt.Println("table is a slice of interface{}")
                for _, tb := range value {
                        if m, ok := tb.(map[string]interface{}); ok {
                                // Now it's accessible
                                fmt.Println(m["name"])

                        }
                }
        default:
                fmt.Println("unknown type")
        }
}

You might want to handle errors better than this.

To read more, check out my writing from a while ago https://medium.com/code-zen/dynamically-creating-instances-from-key-value-pair-map-and-json-in-go-feef83ab9db2.

1 Comment

thanks I tried this ---> r.Name := m["tables"]["name"] but when I compile it gives error invalid operation: m["tables"]["name"] (type interface {} does not support indexing)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.