为了账号安全,请及时绑定邮箱和手机立即绑定

如何将 jsonb 对象从 Postgresql 扫描到 golang 结构

如何将 jsonb 对象从 Postgresql 扫描到 golang 结构

Go
手掌心 2023-02-06 19:41:46
首先,我尝试了过去 stackoverflow 答案中的解决方案,这些答案的问题与我的问题相关,但没有任何效果,这就是为什么我将其作为一个单独的问题提出。我在 golang 中有两个结构type otherPayments struct {    DebitTo     int    `json:"debit_To" binding:"required"`    CreditFrom  int    `json:"credit_from" binding:"required"`    OverallType string `json:"overall_type" binding:"required"`}type advanceAndRoomPayment struct {    PmID        int    `json:"pm_id" binding:"required"`        //Payment method id    PmName      string `json:"pm_name" binding:"required"`      //Payment method name    DebitTo     int    `json:"debit_To" binding:"required"`     //The ledger to debit from    CreditFrom  int    `json:"credit_from" binding:"required"`  //The ledger to credit from    OverallType string `json:"overall_type" binding:"required"` //Overall transaction type}我的postgresql 表中有5SQL 列booking_settingsinitial列,类型 = otherPayments,JSONBcancellation, 输入 = otherPayments,JSONBupdation, 输入 = otherPayments,JSONBadvance_payment输入 = advanceAndRoomPayment,JSONB []room_payment, 输入 = advanceAndRoomPayment,JSONB []查询SELECT如下SELECT initial, cancellation, updation advance_payment, room_payment FROM booking_settings WHERE hotel_id = $1我使用的 sql 包是https://jmoiron.github.io/sqlx/我正在尝试将上面的列扫描到它们适当的结构变量中,到目前为止我只能设法扫描initial, cancellation and updation而不是JSONB [] advance_payment and room_payment非常感谢任何帮助,谢谢
查看完整描述

1 回答

?
慕虎7371278

TA贡献1802条经验 获得超4个赞

以防万一您不知道,jsonb[]这是一个PostgreSQL 数组类型,其元素类型为jsonb. 它不是“json 数组”类型。


如果要将 JSON 数据存储在列中,无论您希望该数据包含标量、对象还是数组JSON 值,都应使用json/类型。jsonb


因此,除非您选择 PostgreSQL 数组类型时考虑了一些特定的用例,否则最好将列的类型从更改jsonb[]为jsonb.


如果您不能或不想更改列类型,那么您仍然可以在 SELECT 查询中将 PostgreSQL 数组转换为 JSON 数组,然后,在您的自定义 Gosql.Scanner实现中,用于json.Unmarshal解码数据库数据.


SELECT to_jsonb(advance_payment) FROM booking_settings WHERE hotel_id = $1

-- or

SELECT array_to_json(advance_payment)::jsonb FROM booking_settings WHERE hotel_id = $1

type advanceAndRoomPaymentList []advanceAndRoomPayment


func (ls *advanceAndRoomPaymentList) Scan(src any) error {

    var data []byte

    switch v := src.(type) {

    case string:

        data = []byte(v)

    case []byte:

        data = v

    }

    return json.Unmarshal(data, ls)

}

如果您有许多查询引用 PostgreSQL 数组列并且您不想更新每个查询以进行转换,您可以自己解析 PostgreSQL 数组然后解组各个元素,或者您可以将这项工作委托给某些人第 3 方实施。


这是一个未经测试的示例,使用pq.GenericArray:


// I haven't tested the following but I'd assume it ought to work,

// if not, then perhaps maybe small tweaks are needed here and there...

type advanceAndRoomPaymentList []advanceAndRoomPayment


func (ls *advanceAndRoomPaymentList) Scan(src any) error {

    return pq.GenericArray{ls}.Scan(src)

}


// implement Scanner for the element type of the slice

func (a *advanceAndRoomPayment) Scan(src any) error {

    var data []byte

    switch v := src.(type) {

    case string:

        data = []byte(v)

    case []byte:

        data = v

    }

    return json.Unmarshal(data, a)

}

如果您想自己解析 PostgreSQL 数组,那么您需要了解用于表示此类数组的语法。您可以在此处找到相关文档:


数组值的外部文本表示由根据数组元素类型的 I/O 转换规则解释的项以及指示数组结构的装饰组成。装饰由围绕数组值的大括号({ 和 })加上相邻项之间的定界符组成。分隔符通常是逗号 (,),但也可以是其他字符:它由数组元素类型的 typdelim 设置决定。在 PostgreSQL 发行版提供的标准数据类型中,除了 type box 使用分号(;)外,都使用逗号。


因此,例如,如果您有包含一个 json 对象、一个 json 数组、一个 json 字符串和一个 json 布尔的 pg 数组,并且您选择了它,那么将传递给sql.Scanner实现的数组表示将看起来像:


{"{\"foo\": \"bar\"}","[\"foo\", \"bar\"]","\"foo bar\"",true}


查看完整回答
反对 回复 2023-02-06
  • 1 回答
  • 0 关注
  • 288 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
微信客服

购课补贴
联系客服咨询优惠详情

帮助反馈 APP下载

慕课网APP
您的移动学习伙伴

公众号

扫描二维码
关注慕课网微信公众号