I have a database table called http_requests
. I have modelled following struct to represent rows in this table.
type Map map[string]interface{}
type HTTPRequest struct {
ID int64 `json:"id" gorm:"id"`
RequestURL string `json:"request_url,omitempty" gorm:"request_url"`
RequestParams *RequestParams `json:"request_params,omitempty" gorm:"request_params"`
}
// RequestParams is another struct that holds params from body and URL query
type RequestParams struct {
FromBody Map `json:"body,omitempty"`
FromQuery Map `json:"query,omitempty"`
}
Code to save HTTPRequest:
request := &HTTPRequest{
RequestURL: "dummy/url",
RequestParams: &RequestParams{FromBody: Map{"param1": "value1"}},
}
if err := gorm.DB.Create(request).Error; err != nil {
return err
}
When I try to save this HTTPRequest it results in error:
sql: Scan error on column index 9, name "request_params": unsupported Scan, storing driver.Value type []uint8 into type *RequestParams
I would like to have request_params
column to store JSON like this:
{"body":{"param1":"value1"}, "query": {"param2" : "value2"} }
or
{"body":{"param1":"value1"}}
or
{"query": {"param2" : "value2"} }
And this should get parsed into RequestParams struct when reading from database.
As suggested by @mkopriva, I implemented Scan() and Value() methods for my RequestParams type. See code below.
import (
"database/sql/driver"
"encoding/json"
"strings"
)
// Value converts RequestParams to a map
func (reqParams RequestParams) Value() (driver.Value, error) {
reqMap, err := reqParams.ToMap()
if err != nil {
return nil, err
}
return reqMap.ForceJSON(), nil
}
// Scan converts value to RequestParams
func (reqParams *RequestParams) Scan(value interface{}) error {
// set empty struct by default
*reqParams = RequestParams{}
if value == nil {
return nil
}
if s, ok := value.([]byte); ok {
d := json.NewDecoder(strings.NewReader(string(s)))
d.UseNumber()
rp := &RequestParams{}
if err := d.Decode(rp); err == nil {
*reqParams = *rp
}
}
return nil
}