modelRT/orm/jsonb_serializer.go

87 lines
2.1 KiB
Go

// Package orm define database data struct
package orm
import (
"database/sql/driver"
"encoding/json"
"errors"
"fmt"
"github.com/jackc/pgx/v5/pgtype"
)
// JSONMap define struct of implements the sql.Scanner and driver.Valuer interfaces for handling JSONB fields
type JSONMap map[string]any
// Value define func to convert the JSONMap to driver.Value([]byte) for writing to the database
func (j JSONMap) Value() (driver.Value, error) {
if j == nil {
return nil, nil
}
return json.Marshal(j)
}
// Scan define to scanned values([]bytes) in the database and parsed into a JSONMap for data retrieval
func (j *JSONMap) Scan(value any) error {
if value == nil {
*j = nil
return nil
}
var source []byte
switch v := value.(type) {
case []byte:
source = v
case string:
source = []byte(v)
default:
return errors.New("unsupported data type for JSONMap Scan")
}
return json.Unmarshal(source, j)
}
// JSONMapArray represents a PostgreSQL jsonb[] column.
type JSONMapArray []JSONMap
// Value encodes the slice as a PostgreSQL jsonb array.
func (j JSONMapArray) Value() (driver.Value, error) {
items := make(pgtype.FlatArray[map[string]any], len(j))
for index, item := range j {
items[index] = map[string]any(item)
}
encoded, err := pgtype.NewMap().Encode(pgtype.JSONBArrayOID, pgtype.TextFormatCode, items, nil)
if err != nil {
return nil, fmt.Errorf("encode JSONMapArray: %w", err)
}
return string(encoded), nil
}
// Scan decodes a PostgreSQL jsonb array.
func (j *JSONMapArray) Scan(value any) error {
if value == nil {
*j = nil
return nil
}
var source []byte
switch typedValue := value.(type) {
case []byte:
source = typedValue
case string:
source = []byte(typedValue)
default:
return fmt.Errorf("unsupported data type %T for JSONMapArray Scan", value)
}
var items pgtype.FlatArray[map[string]any]
if err := pgtype.NewMap().Scan(pgtype.JSONBArrayOID, pgtype.TextFormatCode, source, &items); err != nil {
return fmt.Errorf("decode JSONMapArray: %w", err)
}
result := make(JSONMapArray, len(items))
for index, item := range items {
result[index] = JSONMap(item)
}
*j = result
return nil
}