简体   繁体   English

[]string to jsonb with Gorm and postgres

[英][]string to jsonb with Gorm and postgres

I have a Go struct which contains a slice of strings which I'd like to save as a jsonB object in Postgres with GORM.我有一个 Go 结构,它包含一段字符串,我想用 GORM 在 Postgres 中将其保存为 jsonB 对象。

I've come accross a solution which requires to use the GORM specific type (postgres.Jsonb) which I'd like to avoid.我遇到了一个需要使用我想避免的 GORM 特定类型 (postgres.Jsonb) 的解决方案。

When I try to run the AutoMigrate with a slice in my model, it panics and won't start although when I wrap this slice in a struct (which I'm okay doing) it will run without error but won't create the column in postgres.当我尝试在我的模型中使用切片运行 AutoMigrate 时,它​​会出现恐慌并且不会启动,尽管当我将此切片包装在一个结构中(我可以这样做)时,它会正常运行但不会创建列在 postgres 中。

type User struct {
        gorm.Model
        Data []string `sql:"type:"jsonb"; json:"data"`
} //Panics

type User struct {
        gorm.Model
        Data struct {
            NestedData []string
        } `sql:"type:"jsonb"; json:"data"`
} //Doesn't crash but doesn't create my column

Has anyone been able to manipulate jsonb with GORM without using the postgres.Jsonb type in models ?有没有人能够在不使用模型中的 postgres.Jsonb 类型的情况下使用 GORM 操作 jsonb ?

Maybe:也许:

type DataJSONB []string

func (dj DataJSONB) Value() (driver.Value, error) {
    return json.Marshal(dj)
}

func (dj *DataJSONB) Scan(value interface{}) error {
    b, ok := value.([]byte)
    if !ok {
        return fmt.Errorf("[]byte assertion failed")
    }

    return json.Unmarshal(b, dj)
}

// Your bit
type User struct {
    gorm.Model
    Data DataJSONB `sql:"type:"jsonb"; json:"data"`
}

Define a new type:定义一个新类型:

type Data map[string]interface{}

And implement the Valuer and Scanner interfaces onto them, which allows the field to be converted to a value for the database, and scanned back into the field, respectively:并在它们上实现ValuerScanner接口,这允许将字段转换为数据库的,并分别扫描回字段:

// Value converts into []byte
func (d Data) Value() (driver.Value, error) {
  j, err := json.Marshal(d)
  return j, err
}

// Scan puts the []byte back into Data
func (d *Data) Scan(src interface{}) error {
  source, ok := src.([]byte)
  if !ok {
    return errors.New("Type assertion .([]byte) failed.")
  }

  var i interface{}
  if err := json.Unmarshal(source, &i); err != nil {
    return err
  }

  *d, ok = i.(map[string]interface{})
  if !ok {
    return errors.New("Type assertion .(map[string]interface{}) failed.")
  }

  return nil
}

Then you can define your field in your model like this:然后你可以在你的模型中定义你的字段,如下所示:

type User struct {
        gorm.Model
        Data Data `type: jsonb not null default '{}'::jsonb`
}

Using the underlying map[string]interface{} type is nice too, as you can Unmarshal / Marshal any JSON to/from it.使用底层的map[string]interface{}类型也很好,因为您可以Unmarshal / Marshal任何 JSON 到/从它。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM