Golang从PostgreSQL中获取JSON数据时遇到问题

Golang从PostgreSQL中获取JSON数据时遇到问题 大家好 😊 我正在做一个好玩的API,但在尝试从数据库获取测试条目时遇到了问题。可能是我数据填充错了,也可能是我全做错了……我也不确定。

以下是我试图检索的内容:

type Config struct {
	ID        int       `json:"id"`
	Name      string    `json:"name"`
	CreatedAt time.Time `json:"created_at"`
	UpdatedAt time.Time `json:"updated_at"`
	Plan      Plan      `json:"plan"`
}

type Plan struct {
	Name  string        `json:"name"`
	Steps []Instruction `json:"steps"`
}

从Go得到的错误是:Scan error on column index 3, name “plan”: pq: destination *data.Plan is not a pointer to array or slice

以下是查询语句:

q := `select id, created_at, updated_at, plan from configurations`
	rows, err := db.QueryContext(ctx, q)
	if err != nil {
		return nil, err
	}

	defer rows.Close()
	var configs []*Config

	for rows.Next() {
		var cfg Config
		err := rows.Scan(
			&cfg.ID,
			&cfg.CreatedAt,
			&cfg.UpdatedAt,
			// &cfg.Plan,
			pq.Array(&cfg.Plan),
		)
		if err != nil {
			return nil, err
		}
		configs = append(configs, &cfg)
	}

以下是我尝试以JSONB格式存储在数据库中的JSON:

1 {                                                                                               
  2     "name": "jabber",                                                                           
  3     "steps": [{                                                                                 
  4         "id": 1,                                                                                
  5         "name": "first",                                                                        
  6         "hosts": ["fake1", "fake2"],                                                            
  7         "command": "putup",                                                                     
  8         "fatal": false,                                                                         
  9         "timeout": 62                                                                           
 10     }]                                                                                          
 11                                                                                                 
 12 }

非常感谢!


更多关于Golang从PostgreSQL中获取JSON数据时遇到问题的实战教程也可以访问 https://www.itying.com/category-94-b0.html

3 回复

我认为这能解决问题,非常感谢。

更多关于Golang从PostgreSQL中获取JSON数据时遇到问题的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


你好 @rexlx

数据库的 plan 列似乎包含了你发布的 JSON 字符串,对吗?代码试图将这个 JSON 字符串扫描到类型为 Plan(一个结构体类型)的字段中。

我建议尝试将 plan 列扫描到一个字符串中,然后将该字符串解组到 PlanInstructions 结构体中(使用 encoding/json)。

问题出在数据库中的plan列是JSONB类型,而你在Go代码中试图使用pq.Array()来扫描它。pq.Array()只适用于PostgreSQL的数组类型,不适用于JSONB类型。

你需要将plan列作为JSON字符串扫描,然后手动解析到Plan结构体。以下是修改后的代码:

import (
    "database/sql"
    "encoding/json"
    "time"
)

type Config struct {
    ID        int       `json:"id"`
    Name      string    `json:"name"`
    CreatedAt time.Time `json:"created_at"`
    UpdatedAt time.Time `json:"updated_at"`
    Plan      Plan      `json:"plan"`
}

type Plan struct {
    Name  string        `json:"name"`
    Steps []Instruction `json:"steps"`
}

type Instruction struct {
    ID      int      `json:"id"`
    Name    string   `json:"name"`
    Hosts   []string `json:"hosts"`
    Command string   `json:"command"`
    Fatal   bool     `json:"fatal"`
    Timeout int      `json:"timeout"`
}

func GetConfigs(ctx context.Context, db *sql.DB) ([]*Config, error) {
    q := `SELECT id, created_at, updated_at, plan FROM configurations`
    rows, err := db.QueryContext(ctx, q)
    if err != nil {
        return nil, err
    }
    defer rows.Close()

    var configs []*Config

    for rows.Next() {
        var cfg Config
        var planJSON []byte
        
        err := rows.Scan(
            &cfg.ID,
            &cfg.CreatedAt,
            &cfg.UpdatedAt,
            &planJSON,
        )
        if err != nil {
            return nil, err
        }

        // 解析JSON到Plan结构体
        var plan Plan
        if err := json.Unmarshal(planJSON, &plan); err != nil {
            return nil, err
        }
        cfg.Plan = plan

        configs = append(configs, &cfg)
    }

    return configs, nil
}

如果你使用的是PostgreSQL 14+,也可以使用jsonb_populate_record函数直接在查询中处理:

func GetConfigsWithJSONB(ctx context.Context, db *sql.DB) ([]*Config, error) {
    q := `
        SELECT 
            id, 
            created_at, 
            updated_at,
            jsonb_populate_record(null::record, plan)::jsonb as plan
        FROM configurations
    `
    rows, err := db.QueryContext(ctx, q)
    if err != nil {
        return nil, err
    }
    defer rows.Close()

    var configs []*Config

    for rows.Next() {
        var cfg Config
        var planJSON []byte
        
        err := rows.Scan(
            &cfg.ID,
            &cfg.CreatedAt,
            &cfg.UpdatedAt,
            &planJSON,
        )
        if err != nil {
            return nil, err
        }

        var plan Plan
        if err := json.Unmarshal(planJSON, &plan); err != nil {
            return nil, err
        }
        cfg.Plan = plan

        configs = append(configs, &cfg)
    }

    return configs, nil
}

如果你使用sqlx库,处理会更简单:

import "github.com/jmoiron/sqlx"

func GetConfigsWithSqlx(ctx context.Context, db *sqlx.DB) ([]*Config, error) {
    var configs []*Config
    err := db.SelectContext(ctx, &configs, 
        `SELECT id, created_at, updated_at, plan FROM configurations`)
    return configs, err
}

确保你的数据库表结构正确创建了JSONB列:

CREATE TABLE configurations (
    id SERIAL PRIMARY KEY,
    created_at TIMESTAMP NOT NULL DEFAULT NOW(),
    updated_at TIMESTAMP NOT NULL DEFAULT NOW(),
    plan JSONB NOT NULL
);
回到顶部