How to use golang create nest json array object - go

I try to query database and use query result to create json like these
[ {"TransID": "Transaction ID1",ProductID": ["ProID1","ProID2","ProID3","ProID4" ]},
{"TransID": "Transaction ID2","ProductID": ["ProID5","ProID6" ]} ]
so I create type struct from
type DataRecent []struct {
TransID string `json:"transID"`
ProductID []string `json:"productID"`}
and golang code is
var dataRecent DataRecent
var recent [5]string
for _, Trans := range recent {
if Trans != "" {
var TransID, ProductID string
selectTrans, err := db.Query("select transaction_id, product_id from detail where transaction_id = ?", Trans)
var arr []string
for selectTrans.Next() {
if err != nil {
panic(err.Error())
}
errTrans := selectTrans.Scan(&TransID, &ProductID)
if errTrans != nil {
panic(errTrans.Error())
}
arr = append(arr, ProductID)
}
}
dataRecent.TransID = Trans
dataRecent.ProductID = arr
}
c.JSON(http.StatusOK, gin.H{"status": "success", "message": "Find transactions success", "recent_trans": dataRecent})
defer db.Close()
but I can't build the code and got error
dataRecent.TransID undefined (type DataRecent has no field or method TransID)
dataRecent.ProductID undefined (type DataRecent has no field or method ProductID)
I don't know what to do and stuck with these for a week. I am new programmer for golang. Help me pleae, Thank you

Just remove the array when you create the struct
type DataRecent struct {
TransID string `json:"transID"`
ProductID []string `json:"productID"`
}
and do
var dataRecent []DataRecent
it will works for you.

looks like dataRecent isn't initialized. I suggest you use dataRecent := DataRecent{} instead of var dataRecent DataRecent.
some other insights:
I'm not sure if you've omitted the usage of make of the recent string array, or you're not aware you need to make() it. Anyway, arrays are values in Go, and if you're new to Go I strongly suggest you use slices instead. https://blog.golang.org/go-slices-usage-and-internals
Also, I'm not sure why you have to panic() in case you've found an error (in the words of Dave Cheney, panic means "game over man" - https://dave.cheney.net/tag/panic)

Related

Add root element to existing Json in Go lang

I'm trying to add string "Employee" to my existing JSON response. Also, we need to be able to generate this version of json based on an user condition. Only if the user condition is met, I need to generate second version of json with string "Employee" added. If not the first version without string "Employee" should be generated. How can I achieve it with out updating the existing struct and how can I check this with if clause to check for the condition and then generate json based on it?
Below is my existing json response in go
[
{
"EmpId":{
"String":"ABCD",
"Valid":true
},
"Department":{
"Float64":0,
"Valid":true
}
}
]
How can I get my json response like below with out changing existing struct based on input parameter?
{
"Employee":[
{
"EmpId":{
"String":"ABCD",
"Valid":true
},
"Department":{
"Float64":0,
"Valid":true
}
}
]
}
Below is my code:
Step 1: model folder
type EmployeeWithRoot struct {
Employee []Employee
}
type Employee struct {
EmpNbr sql.NullString `json:"EmpNbr"`
DateofJoin sql.NullString `json:"DateofJoin"`
DeptId sql.NullString `json:"DeptId"`
DeptName sql.NullString `json:"DeptName"`
}
Step 2: code folder
func GetEmpDetails(logRequestId string, logNestedLevel int, EmpNbr string, DateofJoin string) ([]model.EmployeeWithRoot, error) {
logFunctionFunctionName := "code.GetEmpDetails"
logStartTime := time.Now()
logNestedLevel++
defer configurations.TimeTrack(logFunctionFunctionName, logRequestId, logStartTime, logNestedLevel)
rows, err := db.Query(utils.SELECT_OF_EMP_AGGR, EmpNbr, DateofJoin, DeptId, DeptName)
if err != nil {
return nil, err
}
defer rows.Close()
var e []model.EmployeeWithRoot
for rows.Next() {
var x model.EmployeeWithRoot
err := rows.Scan(&x.Employee.EmpNbr, &x.Employee.DateofJoin, &x.Employee.DeptId,&x.Employee.DeptName)
if err != nil {
return nil, err
}
e = append(e, x)
}
err = rows.Err()
if err != nil {
return nil, err
}
return e, nil
}
STEP 3: API folder
Employee, err := code.GetEmpDetails(logRequestId, logNestedLevel, EmpNbr, DateofJoin)
if err != nil {
log.Panic(err)
}
marshalDataForRequestContentType(logRequestId, logNestedLevel, w, r, Employee)
I'm getting the below error.
x.Employee.EmpNbr undefined (type []model.Employee has no field or method EmpNbr)
x.Employee.DateofJoin undefined (type []model.Employee has no field or method DateofJoin)enter code here
x.Employee.DeptId undefined (type []model.Employee has no field or method DeptId)
x.Employee.DeptName undefined (type []model.Employee has no field or method DeptName)
Considering you're just wrapping it it an outer object, I don't see any reason you'd need to change the existing struct, just wrap it in a new one. I'll have to make some guesses/assumptions here since you've only shown the JSON and not the Go code that produces it, but assuming your existing JSON is produced by marshaling something like var response []Employee, the desired JSON could be produced in your condition by marshaling instead:
json.Marshal(struct{Employee []Employee}{response})
Working example: https://go.dev/play/p/vwDvxnQ96G_2
Use string concatenation:
func addRoot(json string) string {
return `{ "Employee":` + json + `}`
}
Run an example on the GoLang playground.
Here's the code if you are working with []byte instead of string:
func addRoot(json []byte) []byte {
const prefix = `{ "Employee":`
const suffix = `}`
result := make([]byte, 0, len(prefix)+len(json)+len(suffix))
return append(append(append(result, prefix...), json...), suffix...)
}
Run this example on the GoLang playground.
If you have some JSON in a byte slice ([]byte) then you can just add the outer element directly - e.g. (playground):
existingJSON := []byte(`[
{
"EmpId":{
"String":"ABCD",
"Valid":true
},
"Department":{
"Float64":0,
"Valid":true
}
}
]`)
b := bytes.NewBufferString(`{"Employee":`)
b.Write(existingJSON)
b.WriteString(`}`)
fmt.Println(string(b.Bytes()))
If this is not what you are looking for please add further details to your question (ideally your attempt as a minimal, reproducible, example)

Golang Pointer Understanding

I am learning go and i need to understand something. I am getting few errors. I have created a Product struct and attached a func with it. I also got a product lists as a slice. Actually I am following one example. I was just trying add different endpoints to it.
I have added question in comment in code. Please explain. I need to return the json single object as a response to user. Please guide me.
package data
type Product struct {
ID int `json:"id"`
Name string `json:"name"`
Description string `json:"description"`
Price float32 `json:"price"`
SKU string `json:"sku"`
CreatedOn string `json:"-"`
UpdatedOn string `json:"-"`
DeletedOn string `json:"-"`
}
type Products []*Product
func (p *Products) ToJSON(w io.Writer) error {
e := json.NewEncoder(w)
return e.Encode(p)
}
func (p *Product) FromJSON(r io.Reader) error {
d := json.NewDecoder(r)
return d.Decode(p)
}
var ErrProductNotFound = fmt.Errorf("Product not found")
func GetProduct(id int) (*Product, error) { // this is returning *Product & err. When I use this in GetProduct in handler func it is giving error
for _, p := range productList {
if p.ID == id {
fmt.Println(p)
return p, nil
}
}
return nil, ErrProductNotFound
}
var productList = []*Product{ **// Why in example the teacher doing it like this.** []*Product{&Product{}, &Product{}} **what it the reason? Please explain.
&Product{ // this gives warning : redundant type from array, slice, or map composite literal. need to understand why**
ID: 1,
Name: "Latte",
Description: "chai",
Price: 2.45,
SKU: "abc123",
CreatedOn: time.Now().UTC().String(),
UpdatedOn: time.Now().UTC().String(),
},
&Product{
ID: 2,
Name: "Tea",
Description: "chai",
Price: 1.45,
SKU: "abc1234",
CreatedOn: time.Now().UTC().String(),
UpdatedOn: time.Now().UTC().String(),
},
}
package handlers
func (p *Product) GetProduct(rw http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
id, _ := strconv.Atoi(vars["id"])
p, errr := data.GetProduct(id) **// cannot use data.GetProduct(id) (value of type *data.Product) as *Product value in assignment**
errr = p.ToJSON(rw) // **p.ToJSON undefined (type *Product has no field or method ToJSON)**
if errr != nil {
http.Error(rw, "could not locate the product", http.StatusBadGateway)
}
}
cannot use data.GetProduct(id) (value of type *data.Product) as *Product value in assignment
p.ToJSON undefined (type *Product has no field or method ToJSON)
The problem here is that inside the GetProduct handler the variable p already has a type (*handlers.Product) that is different from the one returned by the data.GetProduct function (*data.Product). So what you can do is to use a different name for the variable that will store the result of data.GetProduct.
Why in example the teacher doing it like this. []*Product{&Product{}, &Product{}} what it the reason? Please explain.
In general because that's one of the available methods for how to initialize a slice of structs, as per the language spec. Why the teacher used this method specifically? Unless the teacher confided the reason to someone, then no one would know, I certainly don't.
this gives warning : redundant type from array, slice, or map composite literal. need to understand why
Because it's true, it is redundant, as per the language spec, in a composite literal expression you can elide the types of the elements and keys.
For example a non-redundant version of the following composite literal:
[]*Product{&Product{}, &Product{}}
would look like this:
[]*Product{{}, {}}
and the result of these two expressions would be the same.

Gorm query returning only a single row

We're trying to use Gorm with mysql 8 to much frustration.
I have the following tables (simplified for brevity here)
type StoragePool struct {
gorm.Model
PoolId string `json:"id" gorm:"column:poolid;size:40;unique;not null"`
Volumes []Volume `json:"volumes" gorm:"foreignkey:StorageId;association_foreignkey:PoolId"`
}
type Volume struct {
gorm.Model
StorageId string `json:"storageid" gorm:"column:storageid;size:40"`
}
Data insertions seem to work fine. Both tables get populated and no constraints are violated.
A query that expects a single record seems to work fine:
poolRecord := &StoragePool{}
if err := tx.Where("poolid = ?", pool.PoolId).First(&StoragePool{}).Scan(poolRecord).Error; err != nil {
return err
}
This query only returns a single row. When I perform this exact query as raw SQL outside of go, it returns all 31 records I expect.
var poolVolumes []Volume
if err := tx.Where("storageid = ?", pool.PoolId).Find(&Volume{}).Scan(&poolVolumes).Error; err != nil {
return err
}
log.Debugf("found %d volumes belonging to %q [%s]", len(poolVolumes), pool.Name, pool.PoolId)
According to the docs, that second sql statement is the equivalent of "SELECT * FROM VOLUMES WHERE STORAGEID = 'poolid'". That is not the behavior I am getting.
Anyone have any ideas what I'm doing wrong here?
I rarely use an ORM while coding with go, but following the doc from gorm, it seems like you are doing it the wrong way.
Scan is used for scanning result into another struct, like this:
type Result struct {
Name string
Age int
}
var result Result
db.Table("users").Select("name, age").Where("name = ?", 3).Scan(&result)
The correct way to get query results into a slice of structs should be:
var poolVolumes []Volume
if err := tx.Where("storageid = ?", pool.PoolId).Find(&poolVolumes).Error; err != nil {
return err
}

Idiomatic way to update with GORM and echo-framework

Hi new in golang and trying to make rest API
I want to updating entity passing only needed datas with GORM and golang echo framework.
And I get this error :
"strconv.ParseUint: parsing \"ea78944c-a2a9-4813-86e7-10b199d0f002\": invalid syntax"
I use echo.Bind() func to bind formdata (I use postman) with my Club struct.
PS : I use xid to external id and keep my int id to interne work.
Expected :
I'm gonna find Club in my database by id (get with url param) with GORM and set it up in a new club struct.
After that, I bind my club struct with my formdata.
And finally save it with Save() GORM func. => get error
Reality :
I'm gonna find Club in my database by id (write hardcoding string id) with GORM and set it up in a new club struct.
After that, I bind my club struct with my formdata.
And finally save it with Save() GORM func. => works
Conclusion :
I don't know how to pass url parameter AND formdata in PUT method using echo.Bind()
Here my Club struct :
// Club struct
type Club struct {
Base
Name string `json:"name;" gorm:"type:varchar(255);not null" validate:"required"`
Slug string `json:"slug;" gorm:"type:varchar(255);unique;not null"`
Website string `json:"website;" gorm:"type:varchar(255)"`
Lat float32 `json:"lat;" gorm:"lat;" sql:"type:decimal(8,6);" validate:"required,numeric"`
Lng float32 `json:"lng;" gorm:"lng;" sql:"type:decimal(9,6);" validate:"required,numeric"`
Logo string `json:"logo;" gorm:"type:varchar(100)" validate:"required"`
Phone string `json:"phone;" gorm:"type:varchar(20)" validate:"required"`
}
My FindById func :
// GetClubByXID get club by xid
func GetClubByXID(c echo.Context, xid string) (*schemas.Club, error) {
club := new(schemas.Club)
if result := db.Where("xid = ?", xid).First(&club); result.Error != nil {
return nil, result.Error
}
return club, nil
}
And here my updating func :
func UpdateClub(c echo.Context) error {
xid := c.Param("id") // => doesn't work
// xid := "ea78944c-a2a9-4813-86e7-10b199d0f002" // => work
club, err := models.GetClubByXID(c, xid)
if err != nil {
return c.JSON(http.StatusNotFound, err)
}
if err := c.Bind(club); err != nil {
return c.JSON(http.StatusInternalServerError, err)
}
db.Save(&club)
return c.JSON(http.StatusOK, echo.Map{
"club": &club,
})
}
My updating route :
API.PUT("/updateClub/:id", handlers.UpdateClub) // => doesn't work
// API.PUT("/updateClub", handlers.UpdateClub) // => work
When I write with hardcoding my xid ea78944c-a2a9-4813-86e7-10b199d0f002 in my updating func it's work like a charm but I can't combine my url and my formdata with echo.Bind()
my err if I try http://localhost:1323/api/updateClub/ea78944c-a2a9-4813-86e7-10b199d0f002:
"strconv.ParseUint: parsing \"ea78944c-a2a9-4813-86e7-10b199d0f002\": invalid syntax"
Thanks for reading hope someone can help me :)
Can you try to c.Param("xid") instead of c.Param("id") and change the routing parameter as API.PUT("/updateClub/:xid", handlers.UpdateClub)

Select and Get usage with sqlx

I'm using go 1.10.3 and I'm trying to use the sqlx package to get one row and enter it to a struct with Get(), or get several rows and enter them to a slice with Select().
lets start with getting one row into a struct.
I created the following struct:
type PsqlProduct struct {
Id int64 `db:"product_id"`
Name string `db:"product_name"`
Desc sql.NullString `db:"product_desc"`
YearManufacture sql.NullInt64 `db:"year_manufacture"`
Quantity sql.NullInt64 `db:"quantity"`
}
for the query:
QUERY_SELECT_PRODUCT = `select wd.product.id as product_id,
trans_p_name.text as product_name,
trans_p_desc.text as product_desc,
wd.product.year_manufacture, wd.product.quantity
from wd.product
join wd.text_translation as trans_p_name
on trans_p_name.text_id = wd.product.product_name_trans_id and trans_p_name.lang_id=1
left join wd.text_translation as trans_p_desc
on trans_p_desc.text_id = wd.product.product_desc_trans_id and trans_p_desc.lang_id=1
where wd.product.id = $1
`
and I created the following function to get product by id:
func PsqlGetProductById(productId int) *Product {
product := new(PsqlProduct)
err := Psqldb.Get(&product, QUERY_SELECT_PRODUCT,productId)
if err != nil {
log.Fatalf("error: %v",err)
return nil
} else {
newp := Product{
ID: uint(product.Id),
Name: product.Name,
}
if product.Quantity.Valid {
newp.Quantity = uint16(product.Quantity.Int64)
}
if product.YearManufacture.Valid {
newp.YearManufacture = uint16(product.YearManufacture.Int64)
}
if product.Desc.Valid {
newp.Desc = product.Desc.String
}
return &newp
}
}
and I got the error
error: scannable dest type ptr with >1 columns (5) in result
it's as if Get() function is only for one column.. but the documentation clearly states it's not!
if I change the Get() function call to Psqldb.QueryRowx(QUERY_SELECT_PRODUCT, productId).StructScan(product)
then it does work.. but still.. trying to find out why Get() doesn't work.
next.. Select()
so this is the struct
type PsqlCategory struct {
Id int64 `db:"category_id"`
Name string `db:"category_name"`
ParentCategoryId sql.NullInt64 `db:"parent_category_id"`
}
sql query:
QUERY_SELECT_CATEGORIES = `
select category.id as category_id,
text_translation.text as category_name,
category.parent_category_id
from category
join text_translation on text_translation.text_id=category.category_name_trans_id
and text_translation.lang_id = 1`
and the function
func PsqlGetCategories() []Category {
categories := []PsqlCategory{}
err := Psqldb.Select(&categories, QUERY_SELECT_CATEGORIES)
if err != nil {
log.Fatalf("could not parse categories: %v", err)
return nil
}
var nCategories []Category
for _, cat := range categories {
newCat := Category{
Id: cat.Id,
Name: cat.Name,
}
if cat.ParentCategoryId.Valid {
newCat.ParentCategoryId = cat.ParentCategoryId.Int64
}
nCategories = append(nCategories, newCat)
}
return nCategories
}
and this is the error
could not parse categories: pq: relation "category" does not exist
it's like I totally misunderstood the usage of the sqlx library or I'm missing something..
any information regarding the issue would be greatly appreciated.
The problem arises because you're passing **PsqlProduct to Get which thinks that you want to scan the query result into the pointed to pointer, hence "... dest type ptr with >1 columns ...".
Just change:
err := Psqldb.Get(&product, QUERY_SELECT_PRODUCT,productId)
to:
err := Psqldb.Get(product, QUERY_SELECT_PRODUCT,productId)

Resources