3

I have a structure I'm working with, and I'm not sure how to loop through it properly. I would like to access the field names, but all it is doing is just incrementally counting at each loop.

Here is my structure:

type ImgurJson struct {
      Status int16 `json:"status"`
      Success bool `json:"success"`
      Data []struct {
            Width int16 `json:"width"`
            Points int32 `json:"points"`
            CommentCount int32 `json:"comment_count"`
            TopicId int32 `json:"topic_id"`
            AccountId int32 `json:"account_id"`
            Ups int32 `json:"ups"`
            Downs int32 `json:"downs"`
            Bandwidth int64 `json:"bandwidth"`
            Datetime int64 `json:"datetime"`
            Score int64 `json:"score"`
            Account_Url string `json:"account_url"`
            Topic string `json:"topic"`
            Link string `json:"link"`
            Id string `json:"id"`
            Description string`json:"description"`
            CommentPreview string `json:"comment_preview"`
            Vote string `json:"vote"`
            Title string `json:"title"`
            Section string `json:"section"`
            Favorite bool `json:"favorite"`
            Is_Album bool `json:"is_album"`
            Nsfw bool `json:"nsfw"`
             } `json:"data"`
}

Here is my function:

func parseJson(file string) {
      jsonFile, err := ioutil.ReadFile(file)
      if err != nil {
            ...
            }
      jsonParser := ImgurJson{}
      err = json.Unmarshal(jsonFile, &jsonParser)
      for field, value := range jsonParser.Data {
            fmt.Print("key: ", field, "\n")
            fmt.Print("value: ", value, "\n")
      }
}

How do I loop through a nested, []struct in Go and return the fields? I've seen several posts about reflection, but I don't understand if that would assist me or not. I can return the values of each field, but I don't understand how to map the field name to the key value.

Edit:

Renamed "keys" to "field", sorry! Didn't realise they were called fields.

I would like to be able to print:

field: Width
value: 1234

I would like to learn how to do this so I can later call a specific field by name so I can map it to a SQL column name.

4
  • If I understand your question I believe you could iterate through the Data []struct and treat each one as an interface with an unknown type and use a type switch to access each one : golang.org/doc/effective_go.html#type_switch so if we reach an element that is a string treat it this way, if its an int treat it this way, etc. Commented Sep 15, 2015 at 21:55
  • sorry, I was calling them keys because I was thinking of key value pairs, I fixed it! I would like to print out the structure field name. Commented Sep 15, 2015 at 22:12
  • That is going to require reflection. The iteration is for the array of those objects, key, value are actually getting index and value, the value being the entire object. Inside the loop you then need to use reflect to get all the properties and iterate over them printing their name and value. Commented Sep 15, 2015 at 22:13
  • The loop will range over the slice of Data struct objects, not the fields within the struct. Inside your loop, fmt.Println(value.Width) will print out 1234 as you expect. Simply access the field you want with the name you've given it in the struct. json.Unmarshal(jsonFile, &jsonParser) will match the json keys to the struct fields and fill the struct. Commented Sep 15, 2015 at 22:13

3 Answers 3

6

This code based off an example here should do it for you; http://blog.golang.org/laws-of-reflection

import "reflect"


for _, value := range jsonParser.Data {
            s := reflect.ValueOf(&value).Elem()
            typeOfT := s.Type()
            for i := 0; i < s.NumField(); i++ {
            f := s.Field(i)
            fmt.Print("key: ", typeOfT.Field(i).Name, "\n")
            fmt.Print("value: ", f.Interface(), "\n")
       }

}

Note that in your original code the loop is iterating items in the slice called Data. Each of those things an object of that anonymous struct type. You're not dealing with the fields at that point, from there, you can leverage the reflect package to print the names and values of fields in the struct. You can't just range over a struct natively, the operation isn't defined.

Sign up to request clarification or add additional context in comments.

5 Comments

your code doesn't work at all, but I understand what you're getting at! Thanks!
@mynameismevin yeah sorry, I had to go to a meeting before I was able to finish editing/writing that. I'll edit it if I have a minute.
@mynameismevin I believe it should work now. Just a couple typos from me copying the example in that blog. If it were easier to instantiate your type I'd test it further but I don't have time to mock out a bunch of data for it.
@mynameismevin keep in mind that using reflection is slow, and it would be slightly more efficient to use Data []map[string]interface{} and type assert it.
@OneOfOne thanks! I'm not very good with type assertion in golang just yet, but I appreciate it! do you have a link to a good type assertion of structures?
1

This is an alternative approach that we discussed in the comments:

Keep in mind that while this is faster than reflection, it's still better and more efficient to use the struct fields directly and make it a pointer (Data []*struct{....}).

type ImgurJson struct {
    Status  int16                    `json:"status"`
    Success bool                     `json:"success"`
    Data    []map[string]interface{} `json:"data"`
}
//.....
for i, d := range ij.Data {
    fmt.Println(i, ":")
    for k, v := range d {
        fmt.Printf("\t%s: ", k)
        switch v := v.(type) {
        case float64:
            fmt.Printf("%v (number)\n", v)
        case string:
            fmt.Printf("%v (str)\n", v)
        case bool:
            fmt.Printf("%v (bool)\n", v)
        default:
            fmt.Printf("%v (%T)\n", v, v)
        }
    }
}

playground

Comments

0

You can also iterate using normal golang for loop on nested struct.

type ImgurJson struct {
      Status int16 `json:"status"`
      Success bool `json:"success"`
      Data []struct {
            Width int16 `json:"width"`
            Points int32 `json:"points"`
            CommentCount int32 `json:"comment_count"`
            TopicId int32 `json:"topic_id"`
            AccountId int32 `json:"account_id"`
            Ups int32 `json:"ups"`
            Downs int32 `json:"downs"`
            Bandwidth int64 `json:"bandwidth"`
            Datetime int64 `json:"datetime"`
            Score int64 `json:"score"`
            Account_Url string `json:"account_url"`
            Topic string `json:"topic"`
            Link string `json:"link"`
            Id string `json:"id"`
            Description string`json:"description"`
            CommentPreview string `json:"comment_preview"`
            Vote string `json:"vote"`
            Title string `json:"title"`
            Section string `json:"section"`
            Favorite bool `json:"favorite"`
            Is_Album bool `json:"is_album"`
            Nsfw bool `json:"nsfw"`
             } `json:"data"`
}

func parseJson(file string) {
      jsonFile, err := ioutil.ReadFile(file)
      if err != nil {
            ...
            }
      jsonParser := ImgurJson{}
      err = json.Unmarshal(jsonFile, &jsonParser)
      for i :=0; i<len(jsonParser.Data); i++ {
            fmt.Print("key: ", jsonParser.Data[i].TopicId, "\n")
      }
}

Comments

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.