I combined some properties common to all objects into a struct.
type Document struct {
ID string `json:"_id,omitempty"`
UpdatedAt time.Time `json:"updatedat"`
CreatedAt time.Time `json:"createdat"`
}
I also have an address struct, which is not a document.
type Address struct {
AddressLine string `json:"addressline,omitempty"`
City string `json:"city,omitempty"`
Country string `json:"country,omitempty"`
CityCode int `json:"citycode,omitempty"`
}
My customer struct is a document. It also has an address property.
type Customer struct {
Document `json:"document"`
Address Address `json:"address"`
Name string `json:"name,omitempty"`
Email string `json:"email,omitempty"`
Valid bool `json:"valid,omitempty"`
}
The JSON object from MongoDB is as follows;
[
{
"_id": "6186b4556971a9dbae117333",
"address": {
"addressline": "Foo Address",
"city": "Foo City",
"citycode": 0,
"country": "Foo Country"
},
"document": {
"createdat": "0001-01-01T03:00:00+03:00",
"updatedat": "0001-01-01T03:00:00+03:00"
},
"email": "foo#mail.com",
"name": "Foo Fooster",
"valid": false
}
]
I am using the following code to unmarshal this.
var customerEntity Entities.Customer
json.Unmarshal(customerEntityBytes, &customerEntity)
But all I can get is the following line. Most fields are empty.
{{ 0001-01-01 00:00:00 +0000 UTC 0001-01-01 00:00:00 +0000 UTC} { 0} false}
As I thought this was due to the mixed nested structure, I created another customer struct for testing purposes;
import "time"
type AutoGenerated []struct {
ID string `json:"_id"`
Address struct {
Addressline string `json:"addressline"`
City string `json:"city"`
Citycode int `json:"citycode"`
Country string `json:"country"`
} `json:"address"`
Document struct {
Createdat time.Time `json:"createdat"`
Updatedat time.Time `json:"updatedat"`
} `json:"document"`
Email string `json:"email"`
Name string `json:"name"`
Valid bool `json:"valid"`
}
All of a sudden the whole problem was fixed and I was able to access it with all fields filled.
In summary, I cannot unmarshal the Custumer struct I want to use. Do I need to override the unmarshall method for this? I've also reviewed the override examples but the codes are very subjective. A change I will make in base classes will cause me to change the unmarshall method. What is the clean way to this?
Always check errors.
err = json.Unmarshal(customerEntityBytes, &customerEntity)
if err != nil {
// json: cannot unmarshal array into Go value of type Entities.Customer
}
and the reason is, as #mkopriva pointed out - your JSON is an array - and you are unmarshaling to a single struct. To fix:
var customerEntity []Entities.Customer // use slice to capture JSON array
err = json.Unmarshal(customerEntityBytes, &customerEntity)
if err != nil { /* ... */ }
You can certainly use your custom types, but you are losing the _id tag by nesting it in your Document struct. To fix, promote it to Customer:
type Document struct {
//ID string `json:"_id,omitempty"`
UpdatedAt time.Time `json:"updatedat"`
CreatedAt time.Time `json:"createdat"`
}
type Customer struct {
ID string `json:"_id,omitempty"`
// ...
}
Working example: https://play.golang.org/p/EMcC0d1xOLf
Related
I have the following structs as relational models:
type Product struct {
ID uint32 `gorm:"primary_key;auto_increment" json:"id"`
Name string `gorm:"size:100;not null" json:"name"`
Description string `gorm:"size:512" json:"description"`
Price float64 `gorm:"default:0" json:"price"`
Shop Shop `json:"shop"`
ShopID uint32 `gorm:"not null" json:"shop_id"`
Categories []Category `gorm:"many2many:product_categories;" json:"categories"`
CreatedAt time.Time `gorm:"default:CURRENT_TIMESTAMP" json:"created_at"`
UpdatedAt time.Time `gorm:"default:CURRENT_TIMESTAMP" json:"updated_at"`
}
type Category struct {
ID uint32 `gorm:"primary_key;auto_increment" json:"id"`
Name string `gorm:"size:100;not null" json:"name"`
CreatedAt time.Time `gorm:"default:CURRENT_TIMESTAMP" json:"created_at"`
UpdatedAt time.Time `gorm:"default:CURRENT_TIMESTAMP" json:"updated_at"`
}
Now, on my controller I want to create a Product with multiple categories from a JSON object but I can't find a way on how to do it; thus, it returns the message Unable to bind data to product. Here is the code:
func (s *Server) createProduct(c *gin.Context) {
uid, err := auth.ExtractTokenID(c.Request)
if err != nil {
c.IndentedJSON(http.StatusUnauthorized, gin.H{"message": "Unauthorized"})
return
}
var product = models.Product{}
var shop = models.Shop{}
if err := c.BindJSON(&product); err != nil {
fmt.Println(err)
c.IndentedJSON(http.StatusInternalServerError, gin.H{"message": "Unable to bind data to product"})
return
}
Is there a solution or a simple way on how to achieve this? As I haven't been able to find anything on gorm documentation.
Edit; Here is the JSON object I'm sending:
{
"name": "Soap",
"description": "Smelly Soap",
"Price": 12.99,
"categories": [1]
}
I am trying to add a custom attribute to my Golang struct just like how I usually add custom attribute on a Laravel model using the $appends variable.
This is the code:
package models
import (
"time"
)
type Dummy struct {
ID string `json:"id" gorm:"primary_key"`
Description string `json:"description"`
Image string `json:"image"`
ImageUrl ImageUrlDummy
Number int `json:"number"`
UserId string `json:"user_id"`
User User `json:"user"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
func ImageUrlDummy() string {
return "test"
}
However, the ImageUrlDummy inside the struct does not work, it return error such as:
ImageUrlDummy (value of type func() string) is not a type
How do I achieve this same code from Laravel to Golang?
class Dummy extends Model
{
protected $appends = ['image_url'];
public function getImageUrlAttribute(){
return "this is the image url";
}
}
Please pardon me I am still learning Golang, thank you
You are not far off..
Change your struct to (remove ImageUrlDummy, fix json tag for Image):
type Dummy struct {
ID string `json:"id" gorm:"primary_key"`
Description string `json:"description"`
Image string `json:"image"`
Number int `json:"number"`
UserId string `json:"user_id"`
User User `json:"user"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
Then define a method with a receiver of type Dummy pointer
func (d *Dummy) ImageUrl() string {
return "test"
}
Example with a few more things: https://play.golang.com/p/olGSFhBgqkG
I am playing with gin-gonic+sqlx. Now I wonder what is the best way to dynamically bind database records to the struct?
Saying I have a struct and a query like this.
type Project struct {
ID string `db:"id"`
CreatedAt time.Time `db:"created_at"`
UpdatedAt time.Time `db:"updated_at"`
DeletedAt NullTime `db:"deleted_at"`
Name string
Status string
OpenDate NullTime `db:"open_date"`
Number uint
}
func (p Project) List() ([]Project, error) {
var pp []Project
err := db.Select(&pp, "SELECT * FROM Project")
return pp, err
}
Now I want to add field-level permissions to this struct. For example, an admin can view and edit all fields; User A can view CreatedAt and Name; User B can view and edit Name Status and view Number.
I was writing multiply structs for each use case, but obviously, I did choose the silliest way.
The other method I can think of is to implement Struct Tag. Am I on the right track or is there a better way to do this?
We can create multiple structs and embed among each other.
Example:
type ProjectSummary struct {
ID string `db:"id"`
Name string
}
type ProjectUser struct {
ProjectSummary
CreatedAt time.Time `db:"created_at"`
}
type ProjectSupport struct {
ProjectSummary
Status string
Number uint
}
type ProjectAdmin struct {
ProjectSupport
CreatedAt time.Time `db:"created_at"`
UpdatedAt time.Time `db:"updated_at"`
DeletedAt NullTime `db:"deleted_at"`
OpenDate NullTime `db:"open_date"`
}
I am trying to unmarshall multidimensional JSON. My JSON is contains dynamic key therefore I can't do it.
JSON
{
"id":"3",
"datetime":"2019-06-08",
"metadata":[{"a":"A"},{"b":"B"}]
}
Go file
type Chats struct {
Id string json:"id"
Datetime string json:"date"
Metadata string json:"metadata"
}
chat := models.Chats{}
err := c.BindJSON(&chat)
if err != nil {
c.Error(err)
return
}
fmt.Println(chat)
If metadata is dynamic then treat is as an interface{}. If you know it's always going to be a JSON container then you could do map[string]interface{} for convenience. There is also json.RawMessage if you don't necessarily want to use type assertions to see what's inside of it, but just want to preserve the JSON (I'm guessing this is what you were hoping to do by setting it to string).
type Chats struct {
Id string `json:"id"`
Datetime string `json:"date"`
Metadata interface{} `json:"metadata"`
}
type Chats struct {
Id string `json:"id"`
Datetime string `json:"date"`
Metadata map[string]interface{} `json:"metadata"`
}
type Chats struct {
Id string `json:"id"`
Datetime string `json:"date"`
Metadata json.RawMessage `json:"metadata"`
}
I've two structs:
type UpdateableUser struct {
FirstName string
LastName string
Email string
Tlm float64
Dob time.Time
}
type User struct {
Id string
FirstName string
LastName string
Email string
DOB time.Time
Tlm float64
created time.Time
updated time.Time
}
Through a binder I bind request data to the updateableUser struct, therefore I might have an updateableUser with only one "real" value, like uu here:
uu := UpdateableUser{Lastname: "Smith"}
Now I want to set only the not "emtpy" values from UpdateableUser to User. Can you please give me a hint or more?
I would recommend embedding the Updateable struct into the larger struct:
type UpdateableUser struct {
FirstName string
LastName string
Email string
Tlm float64
Dob time.Time
}
type User struct {
UpdateableUser
ID string
created time.Time
updated time.Time
}
func (u *User) UpdateFrom(src *UpdateableUser) {
if src.FirstName != "" {
u.FirstName = src.FirstName
}
if src.LastName != "" {
u.LastName = src.LastName
}
// ... And other properties. Tedious, but simple and avoids Reflection
}
This allows you to use UpdateableUser as an interface to make it explicit which properties can be updated.