Parse AWS ec2 DescribeInstanceStatusOutput JSON response using Golang - json

Hi all I am a beginner trying to learn Go. I am trying to get the status of my EC2 instance using AWS SDK with Golang. I was successfully able to get a JSON response for my instance. I am however, having issues trying to parse the JSON response to get the instance state.
The following is how I get the JSON response:
result, err := ec2Svc.DescribeInstanceStatus(input)
where input is of type ec2.DescribeInstanceStatusInput
The following is the JSON response I get:
{
"InstanceStatuses": [
{
"AvailabilityZone": "ap-southeast-2b",
"Events": null,
"InstanceId": "[VALIDIMAGEID]",
"InstanceState": {
"Code": 16,
"Name": "running"
},
"InstanceStatus": {
"Details": [
{
"ImpairedSince": null,
"Name": "reachability",
"Status": "passed"
}
],
"Status": "ok"
},
"OutpostArn": null,
"SystemStatus": {
"Details": [
{
"ImpairedSince": null,
"Name": "reachability",
"Status": "passed"
}
],
"Status": "ok"
}
}
],
"NextToken": null
}
I then parse the result which is of type DescribeInstanceStatusOutput to String and then pass it to the function parseJson where I attempt to parse the JSON.
result, err := ec2Svc.DescribeInstanceStatus(input)
if err != nil {
fmt.Println("Error", err)
} else {
parseJson(result.String())
//fmt.Printf("%v\n", result)
}
func parseJson(ec2StatusDescription string){
//var x string = ec2StatusDescription.String()
//fmt.Print(ec2StatusDescription)
data := []byte(ec2StatusDescription)
var instanceOutputs InstanceOutput
json.Unmarshal(data, &instanceOutputs)
fmt.Print(instanceOutputs.InstanceStatuses[0].InstanceState.Name)
}
type InstanceOutput struct {
InstanceStatuses [] InstanceStatuses
NextToken *string
}
type InstanceStatuses struct {
AvailabilityZone string
Events *string
InstanceId string
InstanceState InstanceState
InstanceStatus InstanceStatus
OutpostArn *string
SystemStatus SystemStatus
}
type InstanceState struct {
Code int
Name string
}
type InstanceStatus struct {
Details [] InstanceDetails
Status string
}
type InstanceDetails struct {
ImpairedSince *string
Name string
Status string
}
type SystemStatus struct {
Details [] InstanceDetails
Status string
}
I am trying to print the instance state but I keep getting index out of range, and this is because the JSON is not being parsed properly. What am i missing here that's causing the json to not parse correctly? Or am I getting it completely wrong in what I'm doing here? Any help would be highly appreciated.

Related

Go Struct to decode expo push notification response?

I am making a service in go that sends push notification to Expo Backend. Once the http call is made Expo respond with bellow format(according to Expo):
{
"data": [
{
"status": "error" | "ok",
"id": string, // this is the Receipt ID
// if status === "error"
"message": string,
"details": JSON
},
...
],
// only populated if there was an error with the entire request
"errors": [{
"code": number,
"message": string
}]
}
And here is an provioded example of a response:
{
"data": [
{
"status": "error",
"message": "\\\"ExponentPushToken[xxxxxxxxxxxxxxxxxxxxxx]\\\" is not a registered push notification recipient",
"details": {
"error": "DeviceNotRegistered"
}
},
{
"status": "ok",
"id": "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"
}
]
}
I created struct to decode the response.Now i get a error try to decode the reponse for the "details" field, No matter what type i use in my Struct. How can i deal with that field that expo mark as "JSON"?
import (
uuid "github.com/satori/go.uuid"
)
type PushResult struct {
Errors []ErrorDetails `json:"errors,omitempty"`
Datas []DataPart `json:"data,omitempty"`
}
type ErrorDetails struct {
Code int32 `json:"code,omitempty"`
Message string `json:"message,omitempty"`
}
type DataPart struct {
Status string `json:"status,omitempty"`
ID uuid.UUID `json:"id,omitempty"`
Message string `json:"message,omitempty"`
Details string `json:"details,omitempty"` //also tried map[string]string and interface{}
}
This is the struct i am using. i also tried this by looking at the example:
type DataPart struct {
Status string `json:"status,omitempty"`
ID uuid.UUID `json:"id,omitempty"`
Message string `json:"message,omitempty"`
Details struct{
Error string `json:"error"`} `json:"details,omitempty"`
}
But every time get an error like "json: cannot unmarshal object into Go struct field DataPart.data.details"
I just created you struct using your response exemple and it worked completly fine.
Playground
That said if you wana a better way of debugging an "unmarshal error" you can unwrap it. That way you can print aditional data.
var t *json.UnmarshalTypeError
if errors.As(err, &t) {
spew.Dump(t)
}
Example -> I changed the error type to integer so I can fake the error.
OBS: Note that your "Datas" is an array and only the 1st one have an error.

Trouble mapping json to golang struct

I have a json stream as follows ...
[
{
"page": 1,
"pages": 7,
"per_page": "2000",
"total": 13200
},
[
{
"indicator": {
"id": "SP.POP.TOTL",
"value": "Population, total"
},
"country": {
"id": "1A",
"value": "Arab World"
},
"value": null,
"decimal": "0",
"date": "2019"
},
{
"indicator": {
"id": "SP.POP.TOTL",
"value": "Population, total"
},
"country": {
"id": "1A",
"value": "Arab World"
},
"value": "419790588",
"decimal": "0",
"date": "2018"
},
...
]
]
And I'm trying to decode it ... so I have the following struct ... but I keep getting
"cannot unmarshal array into Go value of type struct { P struct ... "
type Message []struct {
P struct {
Page int
}
V []struct {
Indicator struct {
Id string
Value string
}
Country struct {
Value string
}
Value string
Decimal string
Date string
}
}
My struct looks to match the json ... but obviously not! Any ideas?
Since your JSON array have two different types first unmarshal them into a slice of json.RawMessage which is []byte as underlying type so that we can unmarshal again JSON array data.
So unmarshal data for P and V struct type using index directly (predict) or detect if object(starting with '{') then unmarshal into P and array(starting with '[') then unmarshal into V. Now prepare your Message using those data.
type Message struct {
PageData P
ValData []V
}
type P struct {
Page int
}
type V struct {
Indicator struct {
Id string
Value string
}
Country struct {
Value string
}
Value string
Decimal string
Date string
}
func main() {
var rawdata []json.RawMessage
json.Unmarshal([]byte(jsonData), &rawdata)
var pageData P
json.Unmarshal(rawdata[0], &pageData)
var valData []V
json.Unmarshal(rawdata[1], &valData)
res := Message{pageData, valData}
fmt.Println(res)
}
var jsonData = `[...]` //your json data
Full code in Go Playground
As poWar said, the JSON you actually have is a list of objects whose types do not conform to each other. You must therefore unmarshal into something capable of holding different object types, such as interface{} or—since there is an outer array—[]interface{}.
You can also, if you like, decode into a []json.RawMessage. The underlying json.RawMessage itself has underlying type []byte so that it's basically the undecoded "inner" JSON. In at least some cases this is going to be more work than just decoding directly to []interface{} and checking each resulting interface, but you can, if you wish, decode to struct once you have the JSON separated out. For instance:
func main() {
var x []json.RawMessage
err := json.Unmarshal(input, &x)
if err != nil {
fmt.Printf("err = %v\n", err)
return
}
if len(x) != 2 {
fmt.Println("unexpected input")
return
}
var page struct {
Page int
}
err = json.Unmarshal(x[0], &page)
if err != nil {
fmt.Printf("unable to unmarshal page part: %v\n", err)
return
}
fmt.Printf("page = %d\n", page.Page)
// ...
}
Here on the Go Playground is a more complete example. See also Eklavya's answer.
Looking at your struct, your corresponding JSON should look something like this.
[
{
"P": {"page": 1},
"V": [
{
"Indicator": {"Id": ...},
"Country": {"Value":""},
"Value": "",
...
}
]
},
...
]
The JSON structure you are trying to Unmarshal looks like a list of objects where each object is not of the same type. You can start unmarshalling them into interfaces and defining each interface based on the object being unmarhsalled.
package main
import (
"encoding/json"
"log"
)
type Message []interface{}
func main() {
data := `[{"page":1,"pages":7,"per_page":"2000","total":13200},[{"indicator":{"id":"SP.POP.TOTL","value":"Population, total"},"country":{"id":"1A","value":"Arab World"},"value":null,"decimal":"0","date":"2019"},{"indicator":{"id":"SP.POP.TOTL","value":"Population, total"},"country":{"id":"1A","value":"Arab World"},"value":"419790588","decimal":"0","date":"2018"}]]`
var m Message
if err := json.Unmarshal([]byte(data), &m); err != nil {
log.Fatalf("could not unmarshal")
}
log.Printf("message: %v", m)
}
Output:
message: [map[page:1 pages:7 per_page:2000 total:13200] [map[country:map[id:1A value:Arab World] date:2019 decimal:0 indicator:map[id:SP.POP.TOTL value:Population, total] value:<nil>] map[country:map[id:1A value:Arab World] date:2018 decimal:0 indicator:map[id:SP.POP.TOTL value:Population, total] value:419790588]]]
[Edit]: Ideally you should change your JSON to be structured better for unmarshalling. If you do not have control on it, then your corresponding Go structure is just embedded maps of string to interfaces, which you will have to manually type cast and access.

JSON Decoding using Go fails

i have some problems decoding the body of a http response. The response I get from using Insomnia looks like this:
[
{
"name": "monitoring",
"instances": [
{
"host": "ite00716.local",
"id": "2058b934-720f-47c5-a1da-3d1535423b83",
"port": 8080
}
]
},
{
"name": "app1",
"instances": [
{
"host": "172.20.10.2",
"id": "bc9a5859-8dda-418a-a323-11f67fbe1a71",
"port": 8081
}
]
}
]
When I use the following go code, the struct I decode to is empty. I'm not sure why. Please help me!
type Service struct {
Name string `json:"name"`
Instances []Instance `json:"instances"`
}
type Instance struct {
Host string `json:"host"`
Id string `json:"id"`
Port int `json:"port"`
}
func main() {
resp, err := http.Get("http://localhost:8080/services")
if err != nil {
panic(err)
}
defer resp.Body.Close()
var s Service
json.NewDecoder(resp.Body).Decode(&s)
fmt.Println(s)
}
Your json response is array of service
var s []Service
The problem may be that your variable is a Service, while your json represent an array of "Service"s.
Try declaring s as:
var s []Service;

Unmarshalling complex json in Go

So I am trying to fetch the analytics of an app by pinging and endpoint. I make the GET request which is successfull (no errors there) but I am unable to decode the JSON
I need to to decode the following json into structs
{
"noResultSearches": {
"results": [
{
"count": 1,
"key": "\"note 9\""
},
{
"count": 1,
"key": "nokia"
}
]
},
"popularSearches": {
"results": [
{
"count": 4,
"key": "6"
},
{
"count": 2,
"key": "\"note 9\""
},
{
"count": 1,
"key": "nokia"
}
]
},
"searchVolume": {
"results": [
{
"count": 7,
"key": 1537401600000,
"key_as_string": "2018/09/20 00:00:00"
}
]
}
}
For which I am using the following structs
type analyticsResults struct {
Count int `json:"count"`
Key string `json:"key"`
}
type analyticsVolumeResults struct {
Count int `json:"count"`
Key int64 `json:"key"`
DateAsStr string `json:"key_as_string"`
}
type analyticsPopularSearches struct {
Results []analyticsResults `json:"results"`
}
type analyticsNoResultSearches struct {
Results []analyticsResults `json:"results"`
}
type analyticsSearchVolume struct {
Results []analyticsVolumeResults `json:"results"`
}
type overviewAnalyticsBody struct {
NoResultSearches analyticsNoResultSearches `json:"noResultSearches"`
PopularSearches analyticsPopularSearches `json:"popularSearches"`
SearchVolume analyticsSearchVolume `json:"searchVolume"`
}
I make a GET request to an endpoint and then use the response body to decode the json but I get an error. Following is a part of the code that stays in my ShowAnalytics function
func ShowAppAnalytics(app string) error {
spinner.StartText("Fetching app analytics")
defer spinner.Stop()
fmt.Println()
req, err := http.NewRequest("GET", "<some-endpoint>", nil)
if err != nil {
return err
}
resp, err := session.SendRequest(req)
if err != nil {
return err
}
spinner.Stop()
var res overviewAnalyticsBody
dec := json.NewDecoder(resp.Body)
err = dec.Decode(&res)
if err != nil {
return err
}
fmt.Println(res)
return nil
}
json: cannot unmarshal array into Go struct field
overviewAnalyticsBody.noResultSearches of type
app.analyticsNoResultSearches
What am I doing wrong here? Why do I get this error?
EDIT: After you edited, your current code works as-is. Check it out here: Go Playground.
Original answer follows.
There is some inconsistency between the code you posted and the error you get.
I tried it on the Go Playground (here's your version), and I get the following error:
json: cannot unmarshal number into Go struct field analyticsVolumeResults.key of type string
We get this error because in the JSON searchVolume.results.key is a number:
"key": 1537401600000,
And you used string in the Go model:
Key string `json:"key"`
If we change it to int64:
Key int64 `json:"key"`
It works, and prints (try it on the Go Playground):
{{[{1 "note 9"} {1 nokia}]} {[{4 6} {2 "note 9"} {1 nokia}]} {[{7 1537401600000 2018/09/20 00:00:00}]}}
If that key may sometimes be a number and sometimes a string, you may also use json.Number in the Go model:
Key json.Number `json:"key"`

Golang Json marshalling

type Status struct {
slug string `json:"slug"`
}
type Instance struct {
Slug string `json:"slug"`
Stat map[string]*Status `json:"status"`
}
This Json response is recieved for API call:
[ {
"status": {
"stop": false,
"traffic": true,
"label": null,
"dead": true,
"slug": "up",
},
"slug": "instances_raw",
"started_at": "15120736198",
"replacement": null
},
{
"status": {
"stop": false,
"traffic": true,
"label": null,
"dead": true,
"slug": "down",
},
"slug": "instance_raw2",
"started_at": "1512073194",
"replacement": null
}
]
I am trying to marshall json into above struct but running into issue:
instances := make([]Instance, 0)
res := api call return above json
body, _ := ioutil.ReadAll(res.Body)
json.Unmarshal(body, &instances)
fmt.Println("I am struct %s",instances)
It is marshalling into:
I am struct %s [{ map[stop:0xc42018e1b0 dead:0xc42018e1e0 label:<nil> running:0xc42018e220 down:0xc42018e150 traffic:0xc42018e180]}]
Can someone help me figure out why it is not marshalling as I am expecting?
Expected marshalling:
[{instances_raw map[slug:up]} {instances_raw2 map[slug:down]}]
The structs do not match the structure of the data. Perhaps you wanted this:
type Status struct {
Slug string `json:"slug"`
}
type Instance struct {
Slug string `json:"slug"`
Stat Status `json:"status"`
}
Output: [{instances_raw {up}} {instance_raw2 {down}}]
run it on the plaground
or this:
type Instance struct {
Slug string `json:"slug"`
Stat map[string]interface{} `json:"status"`
}
Output: [{instances_raw map[label: dead:true slug:up stop:false traffic:true]} {instance_raw2 map[slug:down stop:false traffic:true label: dead:true]}]
run it on the playground
Always check errors. The example JSON above is not valid and the json.Unmarshal function reports this error.