从PostgreSQL获取的JSON对象数组解组。

4

我在Postgres中有一张Jsonb格式的表

Create Table Business(
    id serial not null primary key,
    id_category integer not null,
    name varchar(50) not null,
    owner varchar(200) not null,
    coordinates jsonb not null,
    reason varchar(300) not null,
    foreign key(id_category) references Category(id)
);

正如您所看到的,我将坐标存储为jsonb格式。

例如:

Insert into Business(id_category, name, owner, coordinates, reason) 
values 
(1,'MyName','Owner', [{"latitude": 12.1268142, "longitude": -86.2754}]','Description')

我提取数据和分配数据的方式如下。
type Business struct {
    ID int `json:"id,omitempty"`
    Name string `json:"name,omitempty"`
    Owner string `json:"owner,omitempty"`
    Category string `json:"category,omitempty"`
    Departments []string `json:"departments,omitempty"`
    Location []Coordinates `json:"location,omitempty"`
    Reason string `json:"reason,omitempty"`
}

type Coordinates struct {
    Latitude float64 `json:"latitude,omitempty"`
    Longitude float64 `json:"longitude,omitempty"`
}

func (a Coordinates) Value() (driver.Value, error) {
    return json.Marshal(a)
}

func (a *Coordinates) Scan(value []interface{}) error {
    b, ok := value.([]byte)
    if !ok {
        return errors.New("type assertion to []byte failed")
    }
    return json.Unmarshal(b, &a)
}

然而,我一直收到这个消息。

在列索引3“coordinates”上扫描错误:不支持的Scan操作,将driver.Value类型[]uint8存储到类型* models.Coordinates中

我用于提取信息的控制器是这个。

func (b *BusinessRepoImpl) Select() ([]models.Business, error) {
    business_list := make([]models.Business, 0)
    rows, err := b.Db.Query("SELECT business.id, business.name, business.owner, business.coordinates, business.reason_froggy, category.category FROM business INNER JOIN category on category.id = business.id_category group by business.id, business.name, business.owner, business.coordinates, business.reason_froggy, category.category")
    if err != nil {
        return business_list, err
    }
    for rows.Next() {
        business := models.Business{}
        err := rows.Scan(&business.ID, &business.Name, &business.Owner, &business.Location, &business.Reason, &business.Category)
        if err !=  nil {
            break
        }
        business_list = append(business_list, business)
    }
    err = rows.Err()
    if err != nil {
        return business_list, err
    }
    return business_list, nil
}

请问有人能告诉我如何解决这个问题吗?获取对象的json数组并将其赋值给Business中的coordinates字段。

1
这就是我使用的所有代码,还需要更具体的内容吗? - Luis Cardoza Bird
2个回答

4

1.

正如您从文档中所看到的Scanner接口,要满足它的要求,需要实现方法:

Scan(src interface{}) error

但是你的*Coordinates类型实现了一个不同的方法

Scan(value []interface{}) error

类型interface{}[]interface{}是两个非常不同的东西。

2.

扫描仪接口必须在您想要作为参数传递给rows.Scan的字段类型上实现。也就是说,您已经在*Coordinates上实现了您的Scan方法,但Location字段的类型是[]Coordinates

同样的事情,类型*Coordinates[]Coordinates是两个非常不同的东西。


因此,解决方案是正确地在适当的类型上实现接口。

请注意,由于Go不允许向未命名类型添加方法,并且[]Coordinates是一个未命名类型,因此您需要声明一个新类型,然后在其位置使用[]Coordinates

type CoordinatesSlice []Coordinates

func (s *CoordinatesSlice) Scan(src interface{}) error {
    switch v := src.(type) {
    case []byte:
        return json.Unmarshal(v, s)
    case string:
        return json.Unmarshal([]byte(v), s)
    }
    return errors.New("type assertion failed")
}

// ...

type Business struct {
    // ...
    Location CoordinatesSlice `json:"location,omitempty"`
    // ...
}

注意

如果业务地点将始终只有一个坐标对存储为jsonb对象,并将Location类型从CoordinatesSlice更改为Coordinates,并相应地将Scanner实现从* CoordinatesSlice 移至*Coordinates


0

我知道这个解决方案真的不够优化,但这是唯一能工作的方法。

基本上,我必须获取JSON,然后对Location属性进行反序列化。

var location string = ""

if err := json.Unmarshal([]byte(location), &business.Location); err != nil { panic(err) }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接