0
votes

I have a Postgres database which stores JSON as one of its fields. The structure of the table is:

type Table struct {
    ID int
    VehicleType string
    Vehicle     Vehicle `gorm:"type:jsonb"`
//  ......  
}

Now, Vehicle is an interface

type Vehicle interface {
     GetEngineModel() string
}

It has many implementation, I will share one of them - Car

type Car struct {
     CarEngineModel  string //the attributes will be different for different 
                            //implementation
}
func (car Car) GetEngineModel() string {
    return car.CarEngineModel
}

In order to parse the attributes in a specific struct i.e. Car, Bike, .., I can implement Scan and Value interface of all the implementations something like this :-

func (car *Car) Scan(value interface{}) error {
   //Want to use this implementation of Scan based on VehicleType
   //attribute of struct table
   b, ok := value.([]byte)
   if !ok {
      return errors.New("type assertion to []byte failed")
   }
   *car = json.Unmarshal(b, &car)
}


Is there a way to tell which implementation of Scan to use based on other table columns or an alternate way to do the same using GORM? I only want one table(genetic json type) so don't want to use different tables for different implementations using polymorphic association.

1
Go does not allow for methods to be declared on interface, or pointer-to-interface types. So func (*Vehicle) Scan(value interface{}) will not compile. - mkopriva
Hi mkopriva, so is there a way I can somehow implement scan function in all the implemtations of the interface Vehicle, and then somehow able to tell that which implemtation of scan to use based on attribute Vehicletype of Table struct - Pulkit Kamboj
I have edited my question based on comment by mkopriva - Pulkit Kamboj

1 Answers

1
votes

You can add a separate field that will hold the raw JSON data and then implement gorm specific hooks to marshal/unmarshal that data.

type Table struct {
    ID          int
    VehicleType string
    Vehicle     Vehicle `gorm:"-"`
    // ...

    VehicleRaw []byte `gorm:"column:vehicle"`
}

func (t *Table) BeforeSave(tx *gorm.DB) (err error) {
    raw, err := json.Marshal(t.Vehicle)
    if err != nil {
        return err
    }
    
    t.VehicleRaw = raw
    return nil
}

func (t *Table) AfterFind(tx *gorm.DB) (err error) {
    switch t.VehicleType {
    case "CAR":
        t.Vehicle = &Car{}
    case "BIKE":
        t.Vehicle = &Bike{}
    }
    return json.Unmarshal(t.VehicleRaw, t.Vehicle)
}