How do I properly loop through two nested json structures in golang - json
I am new to Go. I have 2 identical json nested structures that are each populated with the output of 2 api calls. One call fetches cpu and the other memory metrics. I can unmarshal each of them individually and print out the project name and values of cpu and memory, albeit in 2 separate code blocks. The problem I am having is that I would like to print out both cpu and memory metrics on the same line, next to their project name.
Here is the code I am using to print out the CPU metrics by Project. It creates a nice CSV formatted output:
// CPU Metrics
// Loop through the data for the Month, Project, CPU requests, and CPU Usage
fmt.Println("Month, Project, CPU Request(Core hours), CPU Usage(Core hours)\n")
for _, value_cpu := range rh_values_cpu.Data {
for _, val_cpu := range value_cpu.Projects {
str := val_cpu.Project
s := strings.Contains(str, "openshift")
if s == true {
continue
}
fmt.Printf("%s, %s, ", value_cpu.Date, val_cpu.Project)
for _, v_cpu := range val_cpu.Values {
fmt.Printf("%.1f, %.1f\n", v_cpu.Request.Value, v_cpu.Usage.Value)
}
}
}
I have similar code for the memory metrics which also works fine.
Here is the code I am using to loop through the two json structures. I suspect that I'm not using the nested loops properly or need to solve the problem differently.
// CPU & Memory Metrics
// Loop through the data for the Month, Project, CPU requests, CPU Usage, Memory requests, and Memory Usage
fmt.Println("Month, Project, CPU Request(Core hours), CPU Usage(Core hours) Memory Request(mBytes), Memory Usage(mBytes)\n")
for _, value_cpu := range rh_values_cpu.Data {
for _, value_mem := range rh_values_MEM.Data {
for _, val_cpu := range value_cpu.Projects {
for _, val_mem := range value_mem.Projects {
str := val_cpu.Project
s := strings.Contains(str, "openshift")
if s == true {
continue
}
fmt.Printf("%s, %s, ", value_cpu.Date, val_cpu.Project)
for _, v_cpu := range val_cpu.Values {
fmt.Printf("%.1f, %.1f ", v_cpu.Request.Value, v_cpu.Usage.Value)
for _,v_mem := range val_mem.Values {
fmt.Printf("%.1f, %.1f\n", v_mem.Request.Value, v_mem.Usage.Value)
}
}
}
}
}
}
And here is of one the json structures:
type RH_Output_MEM struct {
Meta struct {
Count int `json:"count"`
Others int `json:"others"`
Currency string `json:"currency"`
Filter struct {
Resolution string `json:"resolution"`
TimeScopeValue string `json:"time_scope_value"`
TimeScopeUnits string `json:"time_scope_units"`
Limit int `json:"limit"`
Offset int `json:"offset"`
} `json:"filter"`
GroupBy struct {
Project []string `json:"project"`
} `json:"group_by"`
OrderBy struct {
} `json:"order_by"`
Exclude struct {
} `json:"exclude"`
Total struct {
Usage struct {
Value float64 `json:"value"`
Units string `json:"units"`
} `json:"usage"`
Request struct {
Value float64 `json:"value"`
Units string `json:"units"`
} `json:"request"`
Limit struct {
Value float64 `json:"value"`
Units string `json:"units"`
} `json:"limit"`
Capacity struct {
Value float64 `json:"value"`
Units string `json:"units"`
} `json:"capacity"`
Infrastructure struct {
Raw struct {
Value float64 `json:"value"`
Units string `json:"units"`
} `json:"raw"`
Markup struct {
Value float64 `json:"value"`
Units string `json:"units"`
} `json:"markup"`
Usage struct {
Value float64 `json:"value"`
Units string `json:"units"`
} `json:"usage"`
Distributed struct {
Value float64 `json:"value"`
Units string `json:"units"`
} `json:"distributed"`
Total struct {
Value float64 `json:"value"`
Units string `json:"units"`
} `json:"total"`
} `json:"infrastructure"`
Supplementary struct {
Raw struct {
Value float64 `json:"value"`
Units string `json:"units"`
} `json:"raw"`
Markup struct {
Value float64 `json:"value"`
Units string `json:"units"`
} `json:"markup"`
Usage struct {
Value float64 `json:"value"`
Units string `json:"units"`
} `json:"usage"`
Distributed struct {
Value float64 `json:"value"`
Units string `json:"units"`
} `json:"distributed"`
Total struct {
Value float64 `json:"value"`
Units string `json:"units"`
} `json:"total"`
} `json:"supplementary"`
Cost struct {
Raw struct {
Value float64 `json:"value"`
Units string `json:"units"`
} `json:"raw"`
Markup struct {
Value float64 `json:"value"`
Units string `json:"units"`
} `json:"markup"`
Usage struct {
Value float64 `json:"value"`
Units string `json:"units"`
} `json:"usage"`
Distributed struct {
Value float64 `json:"value"`
Units string `json:"units"`
} `json:"distributed"`
Total struct {
Value float64 `json:"value"`
Units string `json:"units"`
} `json:"total"`
} `json:"cost"`
} `json:"total"`
} `json:"meta"`
Links struct {
First string `json:"first"`
Next string `json:"next"`
Previous interface{} `json:"previous"`
Last string `json:"last"`
} `json:"links"`
Data []struct {
Date string `json:"date"`
Projects []struct {
Project string `json:"project"`
Values []struct {
Date string `json:"date"`
Project string `json:"project"`
Usage struct {
Value float64 `json:"value"`
Units string `json:"units"`
} `json:"usage"`
Request struct {
Value float64 `json:"value"`
Units string `json:"units"`
} `json:"request"`
Limit struct {
Value float64 `json:"value"`
Units string `json:"units"`
} `json:"limit"`
Capacity struct {
Value float64 `json:"value"`
Units string `json:"units"`
} `json:"capacity"`
Classification string `json:"classification"`
SourceUUID []string `json:"source_uuid"`
Clusters []string `json:"clusters"`
Infrastructure struct {
Raw struct {
Value float64 `json:"value"`
Units string `json:"units"`
} `json:"raw"`
Markup struct {
Value float64 `json:"value"`
Units string `json:"units"`
} `json:"markup"`
Usage struct {
Value float64 `json:"value"`
Units string `json:"units"`
} `json:"usage"`
Distributed struct {
Value float64 `json:"value"`
Units string `json:"units"`
} `json:"distributed"`
Total struct {
Value float64 `json:"value"`
Units string `json:"units"`
} `json:"total"`
} `json:"infrastructure"`
Supplementary struct {
Raw struct {
Value float64 `json:"value"`
Units string `json:"units"`
} `json:"raw"`
Markup struct {
Value float64 `json:"value"`
Units string `json:"units"`
} `json:"markup"`
Usage struct {
Value float64 `json:"value"`
Units string `json:"units"`
} `json:"usage"`
Distributed struct {
Value float64 `json:"value"`
Units string `json:"units"`
} `json:"distributed"`
Total struct {
Value float64 `json:"value"`
Units string `json:"units"`
} `json:"total"`
} `json:"supplementary"`
Cost struct {
Raw struct {
Value float64 `json:"value"`
Units string `json:"units"`
} `json:"raw"`
Markup struct {
Value float64 `json:"value"`
Units string `json:"units"`
} `json:"markup"`
Usage struct {
Value float64 `json:"value"`
Units string `json:"units"`
} `json:"usage"`
Distributed struct {
Value float64 `json:"value"`
Units string `json:"units"`
} `json:"distributed"`
Total struct {
Value float64 `json:"value"`
Units string `json:"units"`
} `json:"total"`
} `json:"cost"`
} `json:"values"`
} `json:"projects"`
} `json:"data"`
}
And here is a snippet of the output I am getting when running the program. As you can see, the Date, Project, and inner loop (CPU metrics) repeats itself, while the outer loop (Memory metrics) runs:
I'm looking for an output where I have one line per project (Month, Project, CPU metrics, Memory metrics)
Month, Project, CPU Request(Core hours), CPU Usage(Core hours) Memory Request(mBytes), Memory Usage(mBytes)
2022-12, amq-demo-streams, 0.0, 34.0, 0.0, 4353.2
2022-12, amq-demo-streams, 0.0, 34.0, 1115.6, 1081.4
2022-12, amq-demo-streams, 0.0, 34.0, 0.0, 10675.9
2022-12, amq-demo-streams, 0.0, 34.0, 100.9, 284.0
2022-12, amq-demo-streams, 0.0, 34.0, 0.0, 70064.5
2022-12, amq-demo-streams, 0.0, 34.0, 773088.9, 427757.8
2022-12, amq-demo-streams, 0.0, 34.0, 9440.0, 11610.3
2022-12, amq-demo-streams, 0.0, 34.0, 9471.3, 11696.9
2022-12, amq-demo-streams, 0.0, 34.0, 0.0, 2455.2
2022-12, amq-demo-streams, 0.0, 34.0, 0.0, 3.3
2022-12, amq-demo-streams, 0.0, 34.0, 0.0, 0.0
2022-12, amq-demo-streams, 0.0, 34.0, -0.3, 0.0
2022-12, amq-demo-streams, 0.0, 34.0, 3785.0, 6610.4
2022-12, amq-demo-streams, 0.0, 34.0, 252.3, 1007.8
2022-12, amq-demo-streams, 0.0, 34.0, 757.0, 883.0
2022-12, amq-demo-streams, 0.0, 34.0, 1009.4, 1613.4
2022-12, amq-demo-streams, 0.0, 34.0, 378.5, 413.5
2022-12, amq-demo-streams, 0.0, 34.0, 908.4, 2856.8
2022-12, amq-demo-streams, 0.0, 34.0, 252.3, 248.7
2022-12, amq-demo-streams, 0.0, 34.0, 66873.8, 21035.3
2022-12, amq-demo-streams, 0.0, 34.0, 353.3, 611.9
2022-12, amq-demo-streams, 0.0, 34.0, 10203.6, 12418.3
2022-12, amq-demo-streams, 0.0, 34.0, 504.7, 398.3
2022-12, amq-demo-streams, 0.0, 34.0, 1135.5, 2248.5
2022-12, amq-demo-streams, 0.0, 34.0, 252.3, 610.6
2022-12, amq-demo-streams, 0.0, 34.0, 252.3, 370.6
I allowed myself to use simpler struct definition. You can always adapt this code to your structs.
type Cpu struct {
Project string
Data []Data
}
type Memory struct {
Project string
Data []Data
}
type Data struct {
Date string
Projects []Project
}
type Project struct {
Project string
Values []struct {
Request float64
Value float64
}
}
func CSVOutput(cpu Cpu, mem Memory) error {
// Returns an error if cpu & memory's data are the same length
if len(cpu.Data) != len(mem.Data) {
return fmt.Errorf("cpu.Data and mem.Data don't have the same length")
}
// Printing CSV file header
fmt.Println("Month, Project, CPU Request(Core hours), CPU Usage(Core hours) Memory Request(mBytes), Memory Usage(mBytes)")
for i := range cpu.Data {
cpuData := cpu.Data[i]
memData := mem.Data[i]
// Using the format from Errorf to add context to the error
if len(cpuData.Projects) != len(memData.Projects) {
return fmt.Errorf("cpu.Data[%d].Projects and mem.Data[%d].Projects don't have the same length", i, i)
}
for j := range cpuData.Projects {
cpuProject := cpuData.Projects[j]
memProject := memData.Projects[j]
if len(cpuProject.Values) != len(memProject.Values) {
return fmt.Errorf("cpu.Data[%d].Projects[%d].Values and mem.Data[%d].Projects[%d].Values don't have the same length", i, j, i, j)
}
name := cpuProject.Project
date := cpuData.Date
// Continue if the cpu project concerns openshift
if strings.Contains(name, "openshift") {
continue
}
for k := range cpuProject.Values {
cpuValue := cpuProject.Values[k]
memValue := memProject.Values[k]
fmt.Printf("%s, %s, %.1f, %.1f, %.1f, %.1f", date, name, cpuValue.Request, cpuValue.Value, memValue.Request, memValue.Value)
}
}
}
return nil
}
This code only works if you receive as much cpu's data as memory's data.
If it isn't the case, you will have to find a way to link a certain cpu's data to its memory equivalent.
This issue can be furthermore discussed if you think the situation might show up.
Related
How to filter JSON data based off an empty field?
I'm calling an API and trying to parse the body response to filter out data. I've created a conditional for-loop to go through each object and check whether this field: Relationships.CurrentConfigurationVersion.Data is empty. Here is my first attempt: func (s *Server) getEmptyWorkSpaces(w http.ResponseWriter, r *http.Request) { // omitted API calls for brevity // body holds the JSON response body, err := ioutil.ReadAll(resp.Body) // label my struct type as data var data WorkspacesJSON err = json.Unmarshal(body, data) if err != nil { panic(err) } var data2 []*WorkspacesJSON for _, v := range data.Data { if v.Relationships.CurrentConfigurationVersion.Data == " " { data2 = append(data2, v) } } } The error occurs in the conditional statement because I'm comparing a struct type to a string. cannot convert "" (untyped string constant) to struct{ID string "json:"id""; Type string "json:"type""} Attempt 2 After some searching around I tried another attempt from information I learned: func (s *Server) getEmptyWorkSpaces(w http.ResponseWriter, r *http.Request) { // omitted API calls for brevity // body holds the JSON response body, err := ioutil.ReadAll(resp.Body) // label my struct type as data, this time attach []* var data []*WorkspacesJSON // added & in this new attempt err = json.Unmarshal(body, &data) if err != nil { panic(err) } var data2 []*WorkspacesJSON for _, v := range data.Data { if v.Relationships.CurrentConfigurationVersion.Data == " " { data2 = append(data2, v) } } } The compiler throw another error but this time targeting v.Relationships.CurrentConfigurationVersion.Data saying: v.Relationships undefined (type *WorkspacesJSON has no field or method Relationships)compilerMissingFieldOrMethod I'm not sure why I'm getting this error because in my first attempt this was not a problem? Clearly I am not understanding this. Here is my type struct: It's fairly long but the only field of important is Data.Relationships.CurrentConfigurationVersion.Data type WorkspacesJSON struct { Data []struct { ID string `json:"id"` Type string `json:"type"` Attributes struct { AllowDestroyPlan bool `json:"allow-destroy-plan"` AutoApply bool `json:"auto-apply"` AutoDestroyAt interface{} `json:"auto-destroy-at"` CreatedAt time.Time `json:"created-at"` Environment string `json:"environment"` Locked bool `json:"locked"` Name string `json:"name"` QueueAllRuns bool `json:"queue-all-runs"` SpeculativeEnabled bool `json:"speculative-enabled"` StructuredRunOutputEnabled bool `json:"structured-run-output-enabled"` TerraformVersion string `json:"terraform-version"` WorkingDirectory string `json:"working-directory"` GlobalRemoteState bool `json:"global-remote-state"` UpdatedAt time.Time `json:"updated-at"` ResourceCount int `json:"resource-count"` ApplyDurationAverage int `json:"apply-duration-average"` PlanDurationAverage int `json:"plan-duration-average"` PolicyCheckFailures int `json:"policy-check-failures"` RunFailures int `json:"run-failures"` WorkspaceKpisRunsCount int `json:"workspace-kpis-runs-count"` LatestChangeAt time.Time `json:"latest-change-at"` Operations bool `json:"operations"` ExecutionMode string `json:"execution-mode"` VcsRepo struct { Branch string `json:"branch"` IngressSubmodules bool `json:"ingress-submodules"` Identifier string `json:"identifier"` DisplayIdentifier string `json:"display-identifier"` OauthTokenID string `json:"oauth-token-id"` WebhookURL string `json:"webhook-url"` RepositoryHTTPURL string `json:"repository-http-url"` ServiceProvider string `json:"service-provider"` } `json:"vcs-repo"` VcsRepoIdentifier string `json:"vcs-repo-identifier"` Permissions struct { CanUpdate bool `json:"can-update"` CanDestroy bool `json:"can-destroy"` CanQueueDestroy bool `json:"can-queue-destroy"` CanQueueRun bool `json:"can-queue-run"` CanQueueApply bool `json:"can-queue-apply"` CanReadStateVersions bool `json:"can-read-state-versions"` CanCreateStateVersions bool `json:"can-create-state-versions"` CanReadVariable bool `json:"can-read-variable"` CanUpdateVariable bool `json:"can-update-variable"` CanLock bool `json:"can-lock"` CanUnlock bool `json:"can-unlock"` CanForceUnlock bool `json:"can-force-unlock"` CanReadSettings bool `json:"can-read-settings"` CanManageTags bool `json:"can-manage-tags"` } `json:"permissions"` Actions struct { IsDestroyable bool `json:"is-destroyable"` } `json:"actions"` Description interface{} `json:"description"` FileTriggersEnabled bool `json:"file-triggers-enabled"` TriggerPrefixes []interface{} `json:"trigger-prefixes"` Source string `json:"source"` SourceName interface{} `json:"source-name"` SourceURL interface{} `json:"source-url"` TagNames []interface{} `json:"tag-names"` } `json:"attributes"` Relationships struct { Organization struct { Data struct { ID string `json:"id"` Type string `json:"type"` } `json:"data"` } `json:"organization"` CurrentRun struct { Data struct { ID string `json:"id"` Type string `json:"type"` } `json:"data"` Links struct { Related string `json:"related"` } `json:"links"` } `json:"current-run"` LatestRun struct { Data struct { ID string `json:"id"` Type string `json:"type"` } `json:"data"` Links struct { Related string `json:"related"` } `json:"links"` } `json:"latest-run"` Outputs struct { Data []interface{} `json:"data"` } `json:"outputs"` RemoteStateConsumers struct { Links struct { Related string `json:"related"` } `json:"links"` } `json:"remote-state-consumers"` CurrentStateVersion struct { Data struct { ID string `json:"id"` Type string `json:"type"` } `json:"data"` Links struct { Related string `json:"related"` } `json:"links"` } `json:"current-state-version"` CurrentConfigurationVersion struct { Data struct { ID string `json:"id"` Type string `json:"type"` } `json:"data"` Links struct { Related string `json:"related"` } `json:"links"` } `json:"current-configuration-version"` AgentPool struct { Data interface{} `json:"data"` } `json:"agent-pool"` Readme struct { Data struct { ID string `json:"id"` Type string `json:"type"` } `json:"data"` } `json:"readme"` } `json:"relationships"` Links struct { Self string `json:"self"` } `json:"links"` } `json:"data"` Links struct { Self string `json:"self"` First string `json:"first"` Prev interface{} `json:"prev"` Next string `json:"next"` Last string `json:"last"` } `json:"links"` Meta struct { StatusCounts struct { Pending int `json:"pending"` PlanQueued int `json:"plan-queued"` Planning int `json:"planning"` Planned int `json:"planned"` Confirmed int `json:"confirmed"` ApplyQueued int `json:"apply-queued"` Applying int `json:"applying"` Applied int `json:"applied"` Discarded int `json:"discarded"` Errored int `json:"errored"` Canceled int `json:"canceled"` CostEstimating int `json:"cost-estimating"` CostEstimated int `json:"cost-estimated"` PolicyChecking int `json:"policy-checking"` PolicyOverride int `json:"policy-override"` PolicyChecked int `json:"policy-checked"` PolicySoftFailed int `json:"policy-soft-failed"` PlannedAndFinished int `json:"planned-and-finished"` PostPlanRunning int `json:"post-plan-running"` PostPlanCompleted int `json:"post-plan-completed"` PreApplyRunning int `json:"pre-apply-running"` PreApplyCompleted int `json:"pre-apply-completed"` Fetching int `json:"fetching"` None int `json:"none"` Total int `json:"total"` } `json:"status-counts"` Pagination struct { CurrentPage int `json:"current-page"` PageSize int `json:"page-size"` PrevPage interface{} `json:"prev-page"` NextPage int `json:"next-page"` TotalPages int `json:"total-pages"` TotalCount int `json:"total-count"` } `json:"pagination"` } `json:"meta"` } I'm stuck in an eternal loop of those two same errors above, and trying to create hacky functions to get the job done, but no luck. I'm very new to Go, I have experience with Python and working with JSON in Py is much easier for me but I want to try this using Go. Thanks in advance for your guidance. How can I filter my JSON body using a conditional to check if a field is empty? EDIT: In my second attempt, How would I be able to select the field I'm looking for, Relationships.CurrentConfigurationVersion.Data? Once I figure that out I think I will be okay. Currently its saying that WorkspaceJSON has no field of method forRelationships. This was not the case for my first attempt.
There are lots of ways to check is struct empty or not, which is discussed here: How to check for an empty struct? Also the append part of the code, must have the same type; as the following code: data2 := WorkspacesJSON{} for _, v := range data.Data { if fmt.Sprintf("%v", v.Relationships.CurrentConfigurationVersion.Data) == "{ }" { data2.Data = append(data2.Data, v) } }
Interact with []struct in golang
I have the code below: type AzStorageAccount struct { Type string `json:"type"` Location string `json:"location"` Tags struct { } `json:"tags"` Properties struct { PrivateLinkServiceConnections []struct { Name string `json:"name"` Properties struct { PrivateLinkServiceID string `json:"privateLinkServiceId"` GroupIds string `json:"groupIds"` PrivateLinkServiceConnectionState struct { Status string `json:"status"` Description string `json:"description"` ActionsRequired string `json:"actionsRequired"` } `json:"privateLinkServiceConnectionState"` } `json:"properties"` } `json:"privateLinkServiceConnections"` ManualPrivateLinkServiceConnections []interface{} `json:"manualPrivateLinkServiceConnections"` Subnet struct { ID string `json:"id"` } `json:"subnet"` CustomDNSConfigs []interface{} `json:"customDnsConfigs"` } `json:"properties"` } But I'm having issues to assung the values to the variables inside PrivateLinkServiceConnections []struct {} At first I was using, but since I need to use []struct it does not work anymore. storageAccount.Location = "eastus2" storageAccount.Type = "Microsoft.Network/privateEndpoints" storageAccount.Properties.PrivateLinkServiceConnections.Properties.PrivateLinkServiceId = "/subscriptions" storageAccount.Properties.PrivateLinkServiceConnections.Name = "priv-endpoint" storageAccount.Properties.PrivateLinkServiceConnections.Properties.GroupIds = "postgresqlServer" storageAccount.Properties.PrivateLinkServiceConnections.Properties.PrivateLinkServiceConnectionState.Status = "Approved" storageAccount.Properties.PrivateLinkServiceConnections.Properties.PrivateLinkServiceConnectionState.Description = "Auto-approved" storageAccount.Properties.PrivateLinkServiceConnections.Properties.PrivateLinkServiceConnectionState.ActionsRequired = "None" storageAccount.Properties.Subnet.Id = "/subscriptions/..." marshaledStorageAccount, _ := json.Marshal(storageAccount) utils.SendPut(endpoint, marshaledStorageAccount) How can assign values to the code below? PrivateLinkServiceConnections []struct { Name string `json:"name"` Properties struct { PrivateLinkServiceID string `json:"privateLinkServiceId"` GroupIds string `json:"groupIds"` PrivateLinkServiceConnectionState struct { Status string `json:"status"` Description string `json:"description"` ActionsRequired string `json:"actionsRequired"` } `json:"privateLinkServiceConnectionState"` } `json:"properties"` } `json:"privateLinkServiceConnections"` Thanks!
The easiest and sane way to do this is by defining a new type: type PrivateLinkServiceConnections struct { Name string `json:"name"` ... } Properties struct { Connections []PrivateLinkServiceConnections `json:"privateLinkServiceConnections"` ... Otherwise, you have to explicitly specify the structure of each struct every time you initialize an instance, like: x:=struct { Name string `json:"name"` Properties struct { PrivateLinkServiceID string `json:"privateLinkServiceId"` GroupIds string `json:"groupIds"` PrivateLinkServiceConnectionState struct { Status string `json:"status"` Description string `json:"description"` ActionsRequired string `json:"actionsRequired"` } `json:"privateLinkServiceConnectionState"` } `json:"properties"` }{ Name:"name", Properties:struct { PrivateLinkServiceID string `json:"privateLinkServiceId"` GroupIds string `json:"groupIds"` PrivateLinkServiceConnectionState struct { Status string `json:"status"` Description string `json:"description"` ActionsRequired string `json:"actionsRequired"` } `json:"privateLinkServiceConnectionState"` } { PrivateLinkServiceID: id, }, } storageAccount.PrivateLinkServiceconnections=append(storageAccount.PrivateLinkServiceConnections, x) ...
PrivateLinkServiceConnections is defined as an array. You cannot access it as you do with objects. To add items to it, you need to use append function. Also, you have defined it as an inline anonymous struct, hence your code has become messy. Define a specific type for PrivateLinkServiceConnections and then each time on append, simply assign it without the need to redeclare it. It is a bad practice.
At first I did a workaround to add "[]" that I needed and it worked but it was not great. But now I finally undertood how to it #models file type AccessPolicieS struct { TenantID string `json:"tenantId"` ObjectID string `json:"objectId"` Permissions struct { Keys []string `json:"keys"` Secrets []string `json:"secrets"` Certificates []string `json:"certificates"` } `json:"permissions"` } type AzVaultPriv struct { Properties struct { AccessPolicies []AccessPolicieS } `json:"properties"` } acessP := models.AccessPolicieS{} acessP.TenantID = "*******" acessP.Permissions.Keys = append(acessP.Permissions.Keys, "UnwrapKey") acessP.Permissions.Keys = append(acessP.Permissions.Keys, "WrapKey") acessP.Permissions.Keys = append(acessP.Permissions.Keys, "Get") acessP.Permissions.Secrets = append(acessP.Permissions.Secrets, "get") acessP.Permissions.Certificates = append(acessP.Permissions.Certificates, "get") newModel := models.AzVaultPriv {} newModel.Properties.AccessPolicies = append(newModel.Properties.AccessPolicies, acessP) marshaledObject, _ := json.Marshal(newModel) Follow below a work around that's not ideal, but kept me going foward until I fixed this. func FormatJsonStructVaultPriv(json []byte) []byte { json = bytes.Replace(json, []byte("Policies\":{"), []byte("Policies\":[{"), 1) json = bytes.Replace(json, []byte("get\"]}}}}"), []byte("get\"]}}]}}"), 1) return json } Thanks for the help!
Can´t unmarshal json to an exported struct from other package
I wan´t to unmarshal json to an exported struct from other package but I does not work propertly package anyPackage type DataStruct struct{ Size int `json:"size"` Material string `json:"material"` Date time.Time } package main import ( "fmt" "log" "encoding/json" "customPackage/anyPackage" ) type NewStruct struct{ Name string `json:"name"` Code int `json:"code"` ExtraData anyPackage.DataStruct } func main(){ blob := `{ "name":"John", "code":12546, "material":"wood","size":456 }` var aux NewStruct if err := json.Unmarshal([]byte(blob), &aux); err != nil { log.Fatal(err) } fmt.Printf("%+v", aux) } In that case, name and code are correctly unmarshal, but material and size don´t, they are empty
You should be able to fix the issue by embedding the DataStruct instead of having a separate field. type NewStruct struct{ Name string `json:"name"` Code int `json:"code"` anyPackage.DataStruct }
The fact that the type is in another package is irrelevant to unmarshalling JSON. The problem is that your data structure doesn't match the JSON. Your structure is effectively: struct { Name string `json:"name"` Code int `json:"code"` ExtraData struct { Size int `json:"size"` Material string `json:"material"` Date time.Time } } Which would equate to JSON like: { "name":"John", "code":12546, "ExtraData": { "material":"wood", "size":456 } } But that is not your JSON structure. Either your data structure or your JSON needs to be modified such that they match.
looping with nested []struct in Go?
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.
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.
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
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") } }
Using []struct with Json
I'm trying to parse JSON to a []struct, the JSON is retrieved from https://api.github.com/events However when I try to access each struct within the array I get an error: type GITHUB_EVENT does not support indexing How am I able to access each struct within the array? func httpGetEvents() { eventDataRAW := httpPageGet("https://api.github.com/events", true) eventDataJSON := new(GITHUB_EVENT) _ = json.Unmarshal([]byte(eventDataRAW), &eventDataJSON) fmt.Println(eventDataJSON[0].Id) } //--------------------------------------------------------------------------------------// type GITHUB_EVENT []struct { Id string `json:"id"` Type string `json:"type"` Actor struct { Id int `json:"id"` Login string `json:"login"` GravatarId string `json:"gravatar_id"` Url string `json:"url"` AvatarUrl string `json:"avatar_url"` } `json:"actor"` Repo struct { Id int `json:"id"` Name string `json:"name"` Url string `json:"url"` } `json:"repo"` Payload struct { PushId int `json:"push_id"` Size int `json:"size"` DistinctSize int `json:"distinct_size"` Ref string `json:"ref"` Head string `json:"head"` Before string `json:"before"` Commits []struct { Sha string `json:"sha"` Author struct { Email string `json:"email"` Name string `json:"name"` } `json:"author"` Message string `json:"message"` Distinct bool `json:"distinct"` Url string `json:"url"` } `json:"commits"` } `json:"payload"` Public bool `json:"public"` CreatedAt string `json:"created_at"` }
This statement: eventDataJSON := new(GITHUB_EVENT) declares eventDataJSON as *GITHUB_EVENT (a pointer to a slice), and initializes it as a nil pointer. After unmarshaling, you could access the first event by explicitly deref-ing the pointer before indexing: (*eventDataJSON)[0].Id However, the more conventional approach is to use make: eventDataJSON := make(GITHUB_EVENT, 0) which declares eventDataJSON as a GITHUB_EVENT, and initializes it as an empty slice. (Remember that make is for special built-in types such as slices, maps, and channels). You could pass a pointer to this slice to json.Unmarshal: _ = json.Unmarshal([]byte(eventDataRAW), &eventDataJSON) ...and then index the slice directly: fmt.Println(eventDataJSON[0].Id) Additionally, json.Marshal will take care of allocating the output value, so you could declare eventDataJSON and skip any explicit initialization: var eventDataJSON GITHUB_EVENT Example: http://play.golang.org/p/zaELDgnpB2