How to get/extract values of a JSON - go

I'm trying to get values/index/key of a JSON but no success. I found some answers and tutorials but I couldn't get them working for me.
I have the following JSON:
{
"id": "3479",
"product": "Camera",
"price": "",
"creation": 04032020,
"products": [
{
"camera": "Nikon",
"available": true,
"freeshipping": false,
"price": "1,813",
"color": "black"
},
{
"camera": "Sony",
"available": true,
"freeshipping": true,
"price": "931",
"color": "black"
}
],
"category": "eletronics",
"type": "camera"
}
I have tried several examples but none worked for this type of Json.
The error I'm getting:
panic: interface conversion: interface {} is nil, not
map[string]interface {}
I believe it's because of "products[]" I tried map[string]interface{} and []interface{} it compiles but gives me that error afterwards.
Could you give me an example on how I can extract these values?
The code I'm using:
//gets the json
product,_:=conn.GetProductData(shop.Info.Id)
// assing a variable
productInformation:=<-product
//prints the json
fmt.Printf(productInformation)
//try to get json values
type Product struct {
Id string
Product string
}
var product Product
json.Unmarshal([]byte(productInformation), &product)
fmt.Printf("Id: %s, Product: %s", product.Id, product.Product)
This code does not panic but it doesn't print all results either so I tried
this one below (which was supposed to give me all results) but it panics
var result map[string]interface{}
json.Unmarshal([]byte(productInformation), &result)
// The object stored in the "birds" key is also stored as
// a map[string]interface{} type, and its type is asserted from
// the interface{} type
products := result["products"].(map[string]interface{})
for key, value := range products {
// Each value is an interface{} type, that is type asserted as a string
fmt.Println(key, value.(string))
}

You need to add json tag to specify the field name in json as it is in lowercase
type Product struct {
Id string `json:"id"`
Product string `json:"product"`
}
And in the second case according to json it is a slice not a map so you need to cast it to []interface{}
var result map[string]interface{}
json.Unmarshal([]byte(productInformation), &result)
// The object stored in the "birds" key is also stored as
// a map[string]interface{} type, and its type is asserted from
// the interface{} type
products := result["products"].([]interface{})
for key, value := range products {
// Each value is an interface{} type, that is type asserted as a string
fmt.Println(key, value)
}

Related

How to parse JSON-RPC table with different type

I want get informations in JSON-RPC file with this structure :
{
"id": "foo1",
"error": null,
"result": [
{
"key": [
"hello 1",
1,
"world 1"
],
"val": {
"type": "static"
}
},
{
"key": [
"hello 2",
1,
"world 2"
],
"val": {
"type": "static"
}
}
]
}
This is my parsing function, Key is string table (can't accept int type) :
type JsonRpcRsp struct {
Id string `json:"id"`
Error *string `json:"error"`
Result json.RawMessage `json:"result"`
}
type JsonRpcEntry_Val struct {
Type string `json:"type"`
}
type JsonRpcEntry struct {
Key [3]string `json:"key"`
Val JsonRpcEntry_Val `json:"val"`
}
jsonResult := JsonRpcRsp{}
json.Unmarshal(data, &jsonResult)
entries := []JsonRpcEntry{}
for _, val := range jsonResult {
json.Unmarshal(val.Result, &entries)
}
How to parse "key" table ?... problem is there are different types
key table structure is :
[ <string>, <int>, <string>]
To unmarshal arrays of different types in Go you'll need to use interfaces and consequently type assertions if you need access to the types.
This will work for you:
type Result struct {
Key [3]interface{} `json:"key"`
Val struct {
Type string `json:"type"`
} `json:"val"`
}
msg := JsonRpcRsp{}
json.Unmarshal(data, &msg)
var result []Result
json.Unmarshal(msg.Result, &result)
for _, v := range result {
key1 := v.Key[0].(string)
key2 := v.Key[1].(float64)
key3 := v.Key[2].(string)
fmt.Println(key1, key2, key3)
}
After asserting the three interfaces to their types, you can then work with them further, depending on your use case.

Create slice of integers from JSON array

I'm trying to figure out how to create a slice I can more easily manipulate and use JUST the values from to later iterate over to make a number of API requests. The slice of integers are API IDs. I am successfully making a struct with custom types after making a GET to retrieve the JSON Array of IDs, but I now need to pull only the values from that JSON array and dump them into a slice without the key "id" (which will likely need to change over time in size).
This is my JSON:
{
"data": [
{
"id": 38926
},
{
"id": 38927
}
],
"meta": {
"pagination": {
"total": 163795,
"current_page": 3,
"total_pages": 81898
}
}
}
And I would like this from it:
{38926, 38927}
If you want custom Unmarshaling behavior, you need a custom type with its own json.Unmarshaler e.g.
type ID int
func (i *ID) UnmarshalJSON(data []byte) error {
id := struct {
ID int `json:"id"`
}{}
err := json.Unmarshal(data, &id)
if err != nil {
return err
}
*i = ID(id.ID)
return nil
}
To use this, reference this type in your struct e.g.
type data struct {
IDs []ID `json:"data"`
}
var d data
working example: https://go.dev/play/p/i3MAy85nr4X

how to unmarshal a map from DynamoDB to struct?

There is the following field on dynamo
{
"config": {
"BASE_AUTH_URL_KEY": "https://auth.blab.bob.com",
"BASE_URL": "https://api.dummy.data.com",
"CONN_TIME_OUT_SECONDS": "300000",
"READ_TIME_OUT_SECONDS": "300000"
},
"id": "myConfig"
}
and getting the element with dynamodbattribute
import(
"github.com/aws/aws-sdk-go/service/dynamodb"
"github.com/aws/aws-sdk-go/service/dynamodb/dynamodbattribute")
result, err := svc.GetItem(&dynamodb.GetItemInput{
TableName: aws.String(tableName),
Key: map[string]*dynamodb.AttributeValue{
"id": {
S: aws.String(configId),
},
},
})
this code its working but when i try to retrieve the object its rendered like this
map[config:{
M: {
BASE_AUTH_URL_KEY: {
S: "https://auth.blab.bob.com"
},
CONN_TIME_OUT_SECONDS: {
S: "300000"
},
READ_TIME_OUT_SECONDS: {
S: "300000"
},
BASE_URL: {
S: "https://api.dummy.data.com"
}
}
} id:{
S: "myConfig"
}]
for that reason when i try to unmarshal my object the object unmarshalled returns as {}
type Config struct {
id string
baseAuthUrlKey string
baseUrl string
connectTimeOutSecs string
readTimeOutSecs string
}
item := Config{}
err = dynamodbattribute.UnmarshalMap(result.Item, &item)
how can i assign the value return from the GetItem that seems to be a map to my struct ?
The root of the issue is that your Config struct is incorrectly structured.
I recommend using json-to-go when converting JSON to Go structs; this tool will help you catch issues like this in the future.
Once you get your struct constructed correctly, you'll also notice that your struct fields are not capitalized, meaning they will not be exported (i.e. able to be used by other packages), which is another reason that your UnmarshalMap code will not return the result you are expecting.
Here is a good answer on struct field visibility and its importance, briefly summarized above.
Below is a corrected version of your struct that, combined with your UnmarshalMap code, will correctly allow you to print your item and not receive a {} which is no fun.
type Item struct {
Config struct {
BaseAuthUrlKey string `json:"BASE_AUTH_URL_KEY"`
BaseUrl string `json:"BASE_URL"`
ConnTimeoutSeconds string `json:"CONN_TIME_OUT_SECONDS"`
ReadTimeoutSeconds string `json:"READ_TIME_OUT_SECONDS"`
} `json:"config"`
ID string `json:"id"`
}

Unmarshal an array nested inside an JSON object

I am having some problems unmarshalling this JSON data to a Go slice of items that holds item structs:
response := {
"data": [
{
"name": "a",
"products": [
{
"name": "c"
}
]
},
{
"name": "b",
"products": [
{
"name": "d"
}
]
},
{
"name": "c",
"products": [
{
"name": "e"
}
]
}
]
}
These are my structs:
type Item struct {
Name string
Products
}
type Products struct {
Name string
}
The slice should basically be the value the "data" attribute (which is an array) transformed to a Go Items slice. I tried the following, but I kept getting an empty array
var items []Item{}
json.Unmarshal(response, &items)
fmt.Println(items)
You're trying to unmarshal that JSON into a slice, but the root of that JSON is an object (you can tell because the JSON begins with a {, indicating the start of an object). You need to account for that outer layer:
type Payload struct {
Data []Item
}
var payload Payload
json.Unmarshal(response, &payload)
fmt.Println(payload.Data)
I would recommend using this cool tool to help you design your struct
https://mholt.github.io/json-to-go/
Also as Adrian said you're trying to unmarshaling json with an array to an object.
type Product struct {
Name string `json:"name"`
}
type Response struct {
Data []struct {
Name string `json:"name"`
Products []Product `json:"products"`
} `json:"data"`
}

Decode 2nd level response body to struct

I have 2 structs:
type List struct {
ListID string `json:"listid"`
Name string `json:"name"`
Users []User `json:"users"`
}
type User struct {
Email string `json:"email"`
Name string `json:"name"`
}
I am calling an endpoint and successfully getting a response which has the structure below:
{
"Results":[
{"Email": "user1#domain.com", "Name": "test1" "State": "Active",…},
{"Email": "user2#domain.com", "Name": "test2" "State": "Active",…},
{"Email": "user3#domain.com", "Name": "test3", "State": "Active",…}
],
"SomeOtherStuff": "email"
}
I am trying to decode the JSON response to my struct like this:
err = json.NewDecoder(response.Body).Decode(&list.Users)
But there is no "Results" attribute in my struct to map to. How can I map only the Results key of the response to my array of User structs ?
To get your data there are at least two options:
Decode into map[string]interface{}
m := create(map[string]interface{})
err = json.NewDecoder(response.Body).Decode(&m)
Then use the m["results"] key to get at your users.
Or you could Decode into a container struct then assign list.Users = container.Results.
type Container struct {
Results []User `json:"Results"`
SomeOtherStuff string `json:"SomeOtherStuff"`
}
To get an idea of structs for arbitrary json look at json2go.

Resources