如何避免在可空的JSON列上扫描对:<nil> * json.RawMessage错误?</nil>

时间:2015-04-17 18:23:19

标签: sql json go

几个星期前,我开始学习go并尝试构建一个简单的博客应用程序,同时学习基础知识。

目前,我正在尝试使用database/sqlgithub.com/lib/pq软件包获取和保留博客帖子。我不喜欢使用第三方软件包,例如sqlxgorm,而不完全了解go的原生行为和基础知识。

我的Post结构是这样的:

type Post struct {
    Id        int
    Title     string
    Body      string
    Tags      json.RawMessage
}

在保留帖子时,我的save()功能可以正常运行:

func (p *Post) save() (int, error) {
    const query = `INSERT INTO post (title, body, tags) VALUES($1, $2, $3) RETURNING id`
    db := GetDB()
    var postid int
    err := db.QueryRow(query, p.Title, p.Body, p.Tags).Scan(&postid)
    return postid, err
}

并阅读最新帖子,我写了一个小函数:

func getLatestPosts(page int) (*[]Post, error) {
    const query = `SELECT id, title, body, tags FROM posts ORDER BY id DESC LIMIT 10`
    var items []Post
    db := GetDB()
    rows, err := db.Query(query)
    if err != nil {
        return nil, err
    }

    for rows.Next() {
        var item Post
        if err := rows.Scan(&item.Id, &item.Title, &item.Body, &item.Tags); err != nil {        
            log.Fatal(err)
        }
        items = append(items, item)
    }
    return &items, err
}

这也适用于点击标记列为空的帖子行,此时我收到以下错误:

  

2015/04/16 21:53:04 sql:列索引4上的扫描错误:不支持   司机 - &gt;扫描对: - &gt; * json.RawMessage

我的问题是,扫描结果集时处理nullable json列的正确方法是什么?此错误与lib/pq

有关

我的架构是:

CREATE TABLE post (
    "id" int4 NOT NULL DEFAULT nextval('post_id_seq'::regclass),
    "title" varchar(255) NOT NULL COLLATE "default",
    "body" text COLLATE "default",
    "tags" json,
    PRIMARY KEY ("id") NOT DEFERRABLE INITIALLY IMMEDIATE
);

这是一个已经存在的(非空)tags字段内容:

 {
     "black-and-white" : "Black and White",
     "the-godfather" : "The Godfather"
 }

有什么想法吗?

1 个答案:

答案 0 :(得分:3)

TL; DR:将您的结构更改为Tags *json.RawMessage或使用该类型的临时结构。

请注意,您可以search the Go source获取错误消息,以便在您感兴趣时更好地了解幕后发生的事情(标准软件包主要是写得好的好来源)去代码)。

我使用下表对新的PostgreSQL-9.4.1服务器进行了测试:

CREATE TABLE post (
        "id"    serial          PRIMARY KEY,
        "title" varchar(255)    NOT NULL,
        "body"  text,
        "tags"  json
);

(顺便说一句,它可能更好地给出重新创建的命令,或者用于创建表的地方,而不是直接使用的表格用途。此外,当我熟悉PostgreSQL时,我记得varchar列不是错误而不是仅仅使用text,可能是长度约束,这是非常罕见的。)

将该表与您的结构类型一起使用:

converting Exec argument #2's type: unsupported type json.RawMessage, a slice

在插页上。更改为[]byte(p.Tags)使其消失(但请参阅下文),然后查询已按原样运行。

当我将NULL值放入表中时,我只遇到了与查询相同的错误。解决方法是将struct字段更改为Tags *json.RawMessage。然后我可以删除我在插入时添加的强制转换,并且查询工作正常,或者根据需要将字段设置为nil

如果您这样做,请在使用之前忘记检查item.Tags是否为nil。或者,创建数据库字段NOT NULL

我不太熟悉Go的数据库支持,知道是否需要指向切片的指针来处理NULL是合理的;我不希望因为Go已经区分了空切片和零切片。

或者,您可以按原样保留类型,并使用如下临时类型:

    var item post
    var tmp *json.RawMessage
    if err := rows.Scan(&item.Id, &item.Title, &item.Body, &tmp); err != nil {
        log.Fatal(err)
    }
    if tmp != nil {
        item.Tags = *tmp
    }

使用NULL主体测试时,您可能会遇到类似的问题。您可以在您的类型中创建数据库列NOT NULL或使用sql.NullString,也可以使用Valid,如上所述(使用NullString的{​​{1}}字段来查看是否应该复制字符串)。


其他一些小调:

  • golint建议使用ID代替Id
  • 您没有提供GetDB实施,我希望它只是获得共享/全球*sql.DB。您不想反复拨打sql.Open
  • 在两个函数中,您可以使用pre-prepared语句(在这些函数之外创建一次)。如果你经常调用这些函数,那可能会有所帮助。
  • 您的查询尝试使用&#34;帖子&#34; table / view而不是&#34; post&#34 ;;我猜错了,同时贴上了这个问题?
  • 您的getLatestPosts函数重新*[]Post;不要这样做。只需返回[]Post。您几乎从不想使用指向切片的指针,当然也不想使用返回类型。