I'm new in Go. I have json like this:
{
"3415": {
"age": 25,
"name": "Tommy"
},
"3414": {
"age": 21,
"name": "Billy"
}
}
I want to unmarshall it to struct:
type People struct {
Id map[string]PeopleDetails
}
type PeopleDetails struct {
Age int `json:"age"`
Name string `json:"name"`
}
But while I run it, I see that struct return nil value.
I did read some tutorials, but most of them have predefined keys, as You see here "id" e.g. 3415 is different for every new json.
When you have to deal with a "dynamic" json key, the answer is use a map of struct.
You can use the following code:
package main
import (
"encoding/json"
"fmt"
)
// Use the struct pointed by #Adirio
type People map[string]PeopleDetails
type PeopleDetails struct {
Age int `json:"age"`
Name string `json:"name"`
}
var data string = `{"3415":{"age":25,"name":"Tommy"},"3414":{"age":21,"name":"Billy"}}`
func main() {
var p People
if err := json.Unmarshal([]byte(data), &p); err != nil {
fmt.Println(err)
}
fmt.Println(p)
}
GoPlayground: https://play.golang.org/p/kVzNV56NcTd
Try with these types instead:
type People map[string]PeopleDetails
type PeopleDetails struct {
Age int `json:"age"`
Name string `json:"name"`
}
Related
I'm using the VirusTotal HTTP API in Golang to get the votes on an URL, both malicious and harmless.
I want to get them using structs, and then unmarshaling the URL using data from these structs. But when I try that, this error shows up:
cannot convert "harmless" (untyped string constant) to int
The code I am currently using is:
type Votes struct {
harmless int
malicious int
}
type Attributes struct {
votes []Votes `json:"total_votes"`
}
type data struct {
attributes []Attributes `json:"attributes"`
}
type jsonstruct struct {
data []data `json:"data"`
}
var printdata jsonstruct
fmt.Println(resp)
json.Unmarshal([]byte(body), &printdata)
fmt.Println(printdata.data[0].attributes[0].votes["harmless"])
And the part of the JSON that I want to get is:
{
"data": {
"attributes": {
"last_modification_date": 1642534694,
"last_http_response_cookies": {
"1P_JAR": "2022-01-18-19",
"NID": "511=drq8-0Gwl0gpw2D-iyZhxrizpE--UMOyc_bO381XXkxknypvl_IETvsxRw3p8kMlBtiYEuSbASKK1wHirmgxce79kgzGMg9MryT0PnHox6kWbmEQTe2vsv_HtVZDFDXiLt4HKpcyDczOT8c_OK8bPb_P-f8rbIXJu_xrA0Ce4lw",
"SameSite": "none"
},
"times_submitted": 82289,
"total_votes": {
"harmless": 54,
"malicious": 18
},
As you can see, I want to get the contents of the subsection total_votes, which are integers harmless and malicious. In short, how can I get them without getting the error about them being untyped strings?
You need to define valid data structure that matches json structure. And after Unmarshal you can access votes fields as res.Data.Attributes.TotalVotes.Harmless . For example (https://go.dev/play/p/YCtV1u-KF7Y):
package main
import (
"encoding/json"
"fmt"
"log"
)
type Result struct {
Data struct {
Attributes struct {
LastHTTPResponseCookies struct {
OnePJAR string `json:"1P_JAR"`
Nid string `json:"NID"`
SameSite string `json:"SameSite"`
} `json:"last_http_response_cookies"`
LastModificationDate float64 `json:"last_modification_date"`
TimesSubmitted float64 `json:"times_submitted"`
TotalVotes struct {
Harmless float64 `json:"harmless"`
Malicious float64 `json:"malicious"`
} `json:"total_votes"`
} `json:"attributes"`
} `json:"data"`
}
var input = `{
"data": {
"attributes": {
"last_modification_date": 1642534694,
"last_http_response_cookies": {
"1P_JAR": "2022-01-18-19",
"NID": "pcyDczOT8c_OK8bPb_P-f8rbIXJu_xrA0Ce4lw",
"SameSite": "none"
},
"times_submitted": 82289,
"total_votes": {
"harmless": 54,
"malicious": 18
}
}
}
}
`
func main() {
var res Result
if err := json.Unmarshal([]byte(input), &res); err != nil {
log.Fatal(err)
}
fmt.Printf("%+v", res.Data.Attributes.TotalVotes.Harmless)
}
Json is -
{
"apiAddr":"abc",
"data":
[
{
"key":"uid1",
"name":"test",
"commandList":["dummy cmd"],
"frequency":"1",
"deviceList":["dev1"],
"lastUpdatedBy": "user",
"status":"Do something"
}
]
}
And the code to unmarshall is -
type Data struct {
APIAddr string `json:"apiAddr"`
Data []Template `json:"data"`
}
type Template struct {
Key string `json:"key"`
Name string `json:"name"`
CommandList []string `json:"commandList"`
Frequency string `json:"frequency"`
DeviceList []string `json:"deviceList"`
LastUpdatedBy string `json:"lastUpdatedBy"`
Status string `json:"status"`
}
raw, err := ioutil.ReadFile(*testFile)
if err != nil {
return
}
var testTemplates Data
err = json.Unmarshal(raw, &testTemplates)
if err != nil {
return
}
where testFile is the json file.
I am getting this error
json: cannot unmarshal array into Go value of type main.Data.
Looking at the existing questions in stackoverflow, looks like I am doing all right.Anyone?
Made a few modification and Unmarshaling worked just fine.
package main
import (
"encoding/json"
"fmt"
)
var raw = ` {
"apiAddr":"abc",
"data":
[
{
"key":"uid1",
"name":"test",
"commandList":["dummy cmd"],
"frequency":"1",
"deviceList":["dev1"],
"lastUpdatedBy": "user",
"status":"Do something"
}
]
}`
func main() {
var testTemplates Data
err := json.Unmarshal([]byte(raw), &testTemplates)
if err != nil {
return
}
fmt.Println("Hello, playground", testTemplates)
}
type Data struct {
APIAddr string `json:"apiAddr"`
Data []Template `json:"data"`
}
type Template struct {
Key string `json:"key"`
Name string `json:"name"`
CommandList []string `json:"commandList"`
Frequency string `json:"frequency"`
DeviceList []string `json:"deviceList"`
LastUpdatedBy string `json:"lastUpdatedBy"`
Status string `json:"status"`
}
You can run it in Playground as well: https://play.golang.org/p/TSmUnFYO97-
I have trouble unmarschaling a JSON contruct:
{
"id": 10,
"result": [
{
"bundled": true,
"type": "RM-J1100"
},
[
{
"name": "PowerOff",
"value": "AAAAAQAAAAEAAAAvAw=="
},
{
"name": "Input",
"value": "AAAAAQAAAAEAAAAlAw=="
}
]
]
}
I actually need the second slice item from the result.
My current attempt is
type Codes struct {
Id int32 `json:"id"`
Result []interface{} `json:"result"`
}
type ResultList struct {
Info InfoMap
Codes []Code
}
type InfoMap struct {
Bundled bool `json:"bundled"`
Type string `json:"type"`
}
type Code struct {
Name string `json:"name"`
Value string `json:"value"`
}
the output is like:
{10 {{false } []}}
but I also tried to use this:
type Codes struct {
Id int32 `json:"id"`
Result []interface{} `json:"result"`
}
the output is okay:
{10 [map[type:RM-J1100 bundled:true] [map[name:PowerOff value:AAAAAQAAAAEAAAAvAw==] map[name:Input value:AAAAAQAAAAEAAAAlAw==]]]}
I can also reference the Result[1] index:
[map[name:PowerOff value:AAAAAQAAAAEAAAAvAw==] map[name:Input value:AAAAAQAAAAEAAAAlAw==]]
But I fail to convert the interface type to any other Type which would match. Can anybody tell me how to do interface conversion. And what approach would be the "best".
One option would be to unmarshal the top level thing into a slice of json.RawMessage initially.
Then loop through the members, and look at the first character of each one. If it is an object, unmarshal that into your InfoMap header struct, and if it is an array, unmarshal it into a slice of the Code struct.
Or if it is predictable enough, just unmarshal the first member to the one struct and the second to a slice.
I made a playground example of this approach.
type Response struct {
ID int `json:"id"`
RawResult []json.RawMessage `json:"result"`
Header *Header `json:"-"`
Values []*Value `json:"-"`
}
type Header struct {
Bundled bool `json:"bundled"`
Type string `json:"type"`
}
type Value struct {
Name string `json:"name"`
Value string `json:"value"`
}
func main() {
//error checks ommitted
resp := &Response{}
json.Unmarshal(rawJ, resp)
resp.Header = &Header{}
json.Unmarshal(resp.RawResult[0], resp.Header)
resp.Values = []*Value{}
json.Unmarshal(resp.RawResult[1], &resp.Values)
}
(I will not point out how horrific it is this JSON struct, but as always: sXXt happens)
You can convert your struct like this, by using a cycle of JSON Marshal / Unmarshal. Code follows:
package main
import (
"encoding/json"
"log"
)
const (
inputJSON = `{
"id": 10,
"result": [
{
"bundled": true,
"type": "RM-J1100"
},
[
{
"name": "PowerOff",
"value": "AAAAAQAAAAEAAAAvAw=="
},
{
"name": "Input",
"value": "AAAAAQAAAAEAAAAlAw=="
}
]
]
}`
)
type Codes struct {
Id int32 `json:"id"`
Result [2]interface{} `json:"result"`
}
type Result struct {
Info InfoMap
Codes []Code
}
type InfoMap struct {
Bundled bool `json:"bundled"`
Type string `json:"type"`
}
type Code struct {
Name string `json:"name"`
Value string `json:"value"`
}
func main() {
newCodes := &Codes{}
err := json.Unmarshal([]byte(inputJSON), newCodes)
if err != nil {
log.Fatal(err)
}
// Prints the whole object
log.Println(newCodes)
// Prints the Result array (!)
log.Println(newCodes.Result)
if len(newCodes.Result) != 2 {
log.Fatal("Invalid Result struct")
}
// Marshal and Unmarshal data to obtain the code list
byteCodeList, _ := json.Marshal(newCodes.Result[1])
codeList := make([]Code, 0)
err = json.Unmarshal(byteCodeList, &codeList)
if err != nil {
log.Fatal("Invalid Code list")
}
// Prints the codeList
log.Println(codeList)
}
Test it on playground.
{
"devices": [
{
"id": 20081691,
"targetIp": "10.1.1.1",
"iops": "0.25 IOPS per GB",
"capacity": 20,
"allowedVirtualGuests": [
{
"Name": "akhil1"
},
{
"Name": "akhil2"
}
]
}
]
}
How to write a structure representation of this JSON data so that I can add and delete devices to the list. I tried with the different structure representations but nothing is working. Below is one of the example I have tried with a similar kind of json data. I am not able to add new data to it. The structure representation and the way the append is done might be wrong here
package main
import (
"encoding/json"
"fmt"
)
type Person struct {
ID string `json:"id,omitempty"`
Firstname string `json:"firstname,omitempty"`
Lastname string `json:"lastname,omitempty"`
Address []Address `json:"address,omitempty"`
}
type Address[] struct {
City string `json:"city,omitempty"`
}
func main() {
var people []Person
people = append(people, Person{ID: "1", Firstname: "Nic", Lastname: "Raboy", Address: []Address{{City: "Dublin"},{ City: "CA"}}} )
b, err := json.Marshal(people)
if err != nil {
fmt.Println("json err:", err)
}
fmt.Println(string(b))
}
It will be below. This was generated using the excellent JSON-to-GO tool:
type MyStruct struct {
Devices []struct {
ID int `json:"id"`
TargetIP string `json:"targetIp"`
Iops string `json:"iops"`
Capacity int `json:"capacity"`
AllowedVirtualGuests []struct {
Name string `json:"Name"`
} `json:"allowedVirtualGuests"`
} `json:"devices"`
}
To simplify that though, you can break it into smaller structs for readability. An example is below:
package main
import "fmt"
type VirtualGuest struct {
Name string `json:"Name"`
}
type Device struct {
ID int `json:"id"`
TargetIP string `json:"targetIp"`
Iops string `json:"iops"`
Capacity int `json:"capacity"`
AllowedVirtualGuests []VirtualGuest `json:"allowedVirtualGuests"`
}
type MyStruct struct {
Devices []Device `json:"devices"`
}
func main() {
var myStruct MyStruct
// Add a MyStruct
myStruct.Devices = append(myStruct.Devices, Device{
ID:1,
TargetIP:"1.2.3.4",
Iops:"iops",
Capacity:1,
AllowedVirtualGuests:[]VirtualGuest{
VirtualGuest{
Name:"guest 1",
},
VirtualGuest{
Name:"guest 2",
},
},
})
fmt.Printf("MyStruct: %v\n", myStruct)
}
you can use struct tag like json:"id", try struct below:
type Data struct {
Devices []struct {
Id int `json:"id"`
IP string `json:"targetIp"`
IOPS string `json:"iops"`
Capacity int `json:"capacity"`
AllowedVirtualGuests []struct {
Name string `json:"Name"`
} `json:"allowedVirtualGuests"`
} `json:"devices"`
}
I have a struct like so:
type my_struct struct {
First string `json:"first"`
Second string `json:"second"`
Number int `json:"number"`
}
When I marshal that into JSON, it outputs very simple JSON as you'd expect:
var output_json []byte
output_json, _ = json.Marshal(output)
fmt.Println(string(output_json))
Result:
{"first":"my_string","second":"another_string","number":2}
All fine so far!
What I'd like to do, before marshalling that struct into JSON, is nest it inside another struct. The resulting output would be JSON that looks like this:
{
"fields": {
"first": "my_string",
"number": 2,
"second": "another_string"
},
"meta": "data"
}
How can I do that?
I think you could statically declare another struct to use:
type WrappedOutput struct {
fields my_struct
meta string
}
Then you could embed your struct before marshalling
o := WrappedOutput{fields: output}
Brand new to go so not sure if this is the easiest way to do it
The clean way to do this would be to declare 2 structs (I've done it globally below) and nest My_struct inside the higher level struct.
Then you can initialize the higher level struct before Mashalling it:
package main
import (
"encoding/json"
"fmt"
)
type My_struct struct {
First string `json:"first"`
Second string `json:"second"`
Number int `json:"number"`
}
type Big_struct struct {
Fields My_struct
Meta string
}
func main() {
output := Big_struct{
Fields: My_struct{
First: "my_string",
Second: "another_string",
Number: 2,
},
Meta: "data",
}
output_json, err := json.Marshal(output)
if err != nil {
fmt.Println(err)
}
fmt.Println(string(output_json))
}
if you don't want Big_struct you can declare an anonymous struct within your code as you need it and nest My_struct inside:
package main
import (
"encoding/json"
"fmt"
)
type My_struct struct {
First string `json:"first"`
Second string `json: "second"`
Number int `json:"number"`
}
func main() {
output := struct {
Fields My_struct
Meta string
}{
Fields: My_struct{
First: "my_string",
Second: "another_string",
Number: 2,
},
Meta: "data",
}
output_json, err := json.Marshal(output)
if err != nil {
fmt.Println(err)
}
fmt.Println(string(output_json))
}
If you don't want to use a new structure, you can do:
data := my_struct{First: "first", Second: "2", Number: 123}
result, _ := json.Marshal(&map[string]interface{}{"fields":data,"meta":"data"})
fmt.Println(string(result))
it's not clean, but it does the work.