Skip to content

Instantly share code, notes, and snippets.

@destel

destel/json.go Secret

Last active May 7, 2024 09:27
Show Gist options
  • Save destel/fdcf10bb9a153f2dcaa57487898b52d7 to your computer and use it in GitHub Desktop.
Save destel/fdcf10bb9a153f2dcaa57487898b52d7 to your computer and use it in GitHub Desktop.
Automatic JSON (un)marshalling for database/sql in Go
package dbkit
import (
"bytes"
"database/sql/driver"
"encoding/json"
"errors"
)
// JSON is a wrapper around any type that can be saved to database as JSON.
type JSON[T any] struct {
V T
}
// MakeJSON creates a new JSON wrapper around the value.
// It's more convenient than using JSON{V: value} directly, because it allows to infer the type.
func MakeJSON[T any](v T) JSON[T] {
return JSON[T]{V: v}
}
var emptyJSONS = [][]byte{
[]byte("null"),
//[]byte("{}"),
//[]byte("[]"),
//[]byte(""),
}
// Value implements the driver.Valuer interface
func (v JSON[T]) Value() (driver.Value, error) {
res, err := json.Marshal(v.V)
if err != nil {
return nil, err
}
for _, empty := range emptyJSONS {
if bytes.Equal(res, empty) {
return nil, nil
}
}
return res, nil
}
// Scan implements the sql.Scanner interface
func (v *JSON[T]) Scan(value interface{}) error {
var err error
switch t := value.(type) {
case []byte:
if len(t) == 0 {
return nil
}
err = json.Unmarshal(t, &v.V)
case string:
if len(t) == 0 {
return nil
}
err = json.Unmarshal([]byte(t), &v.V)
default:
err = errors.New("dbkit:json: unsupported column type")
}
// If we can't unmarshal, we just set the value to zero value
if err != nil {
var zero T
v.V = zero
}
return nil
}
func (v JSON[T]) GormDataType() string {
// This one is required, otherwise gorm2 may break and return very hard to debug errors.
// It may return any non-empty string
return "json"
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment