英文:
Why JSONB is returning as a string?
问题
我已经开始了一个“Instagram克隆”后端,试图学习一些关于Golang的知识,但我遇到了一个我不知道如何解决的问题。
我使用Fiber构建了一个简单的API来获取一些帖子:
package server
import (
"fmt"
"instagram/internal/psql"
queries "instagram/internal/sql"
"net/http"
"github.com/gofiber/fiber/v2"
)
type Like struct {
Username string
Name string
Picture string
}
type Post struct {
Id string `json:"id"`
Description string `json:"description"`
Media string `json:"media"`
Type string `json:"type"`
Likes string `json:"likes"`
User_id string `json:"user_id"`
Created_at string `json:"created_at"`
Updated_at string `json:"updated_at"`
}
func ListPosts(c *fiber.Ctx) error {
rows, err := psql.DB().Query(queries.GetAllPosts)
if err != nil {
c.SendStatus(400)
return nil
}
defer rows.Close()
var data []Post
for rows.Next() {
var post Post
err := rows.Scan(&post.Id, &post.Description, &post.Media, &post.Type, &post.Created_at, &post.Updated_at, &post.Likes, &post.User_id)
if err != nil {
fmt.Print(err)
c.SendStatus(400)
return nil
}
data = append(data, post)
}
return c.Status(http.StatusOK).JSON(data)
}
问题在于likes属性以字符串形式返回:
[
{
"id": "...",
"description": "...",
"media": "...",
"type": "...",
"likes": "[]",
"user_id": "...",
"created_at": "...",
"updated_at": "..."
}
]
我尝试了一些方法,比如使用json.Marshal(data),我还编写了Like结构体,但是我无法使其工作,因为当我将Likes的类型从string更改为[]Like时,调用Scan时会出现以下消息:
sql: Scan error on column index 6, name "likes": unsupported Scan, storing driver.Value type []uint8 into type *[]server.Likesql: Scan error on column index 6, name "likes": unsupported Scan, storing driver.Value type []uint8 into type *[]server.Like
我正在使用PostgreSQL数据库,并且我是第一次尝试使用jsonb列,所以likes在数据库中是一个jsonb列。
我理想的有效负载应该是这样的:
[
{
"id": "...",
"description": "...",
"media": "...",
"type": "...",
"likes": [],
"user_id": "...",
"created_at": "...",
"updated_at": "..."
}
]
请注意,likes现在是一个数组,而不是一个字符串。所以,有人知道如何解决这个问题吗?
英文:
I have started a "instagram clone" backend to try learn somethings about golang, but I'm having a problem that I do not know how to solve.
I have build a simple API with Fiber that get some posts:
package server
import (
"fmt"
"instagram/internal/psql"
queries "instagram/internal/sql"
"net/http"
"github.com/gofiber/fiber/v2"
)
type Like struct {
Username string
Name string
picture string
}
type Post struct {
Id string `json:"id"`
Description string `json:"description"`
Media string `json:"media"`
Type string `json:"type"`
Likes string `json:"likes"`
User_id string `json:"user_id"`
Created_at string `json:"created_at"`
Updated_at string `json:"updated_at"`
}
func ListPosts(c *fiber.Ctx) error {
rows, err := psql.DB().Query(queries.GetAllPosts)
if err != nil {
c.SendStatus(400)
return nil
}
defer rows.Close()
var data []Post
for rows.Next() {
var post Post
err := rows.Scan(&post.Id, &post.Description, &post.Media, &post.Type, &post.Created_at, &post.Updated_at, &post.Likes, &post.User_id)
if err != nil {
fmt.Print(err)
c.SendStatus(400)
return nil
}
data = append(data, post)
}
return c.Status(http.StatusOK).JSON(data)
}
The problem is that likes attribute is returning as a string:
[
{
"id": "...",
"description": "...",
"media": "...",
"type": "...",
"likes": "[]",
"user_id": "...",
"created_at": "...",
"updated_at": "..."
}
]
I have tried some things, like use json.Marshal(data), I also wrote that Like struct, but I could not make it work, because when I change the type of Likes from string to []Like the following message appears when Scan is called
>sql: Scan error on column index 6, name "likes": unsupported Scan, storing driver.Value type []uint8 into type *[]server.Likesql: Scan error on column index 6, name "likes": unsupported Scan, storing driver.Value type []uint8 into type *[]server.Like
I'm using a postgresql database, and I'm trying to work with jsonb columns for the first time, so likes is in the database a jsonb column.
My ideal payload would be like:
[
{
"id": "...",
"description": "...",
"media": "...",
"type": "...",
"likes": [],
"user_id": "...",
"created_at": "...",
"updated_at": "..."
}
]
Note that likes is now a array instead of a string. So anybody knows how to solve this?
答案1
得分: 2
你很可能需要实现 sql.Driver 接口来“了解”如何将数据扫描到你的结构体中,可能是这样的:
type Like struct {
Username string
Name string
Picture string
}
type Likes []Like
func (l Likes) Value() (driver.Value, error) {
return json.Marshal(l)
}
// 让 Likes 实现 sql.Scanner 接口。
func (l *Likes) Scan(value interface{}) error {
b, ok := value.([]byte)
if !ok {
return errors.New("类型断言为 []byte 失败")
}
return json.Unmarshal(b, &l)
}
这段代码定义了一个 Like 结构体和一个 Likes 切片,Likes 切片实现了 Value() 和 Scan() 方法,用于在数据库和结构体之间进行转换。Value() 方法将 Likes 切片转换为数据库可以存储的值,而 Scan() 方法将数据库中的值转换为 Likes 切片。
英文:
You most likely need to implement the interface for sql.Driver to "know" how to scan into your struct, probably something like
type Like struct {
Username string
Name string
picture string
}
type Likes []Like
func (l Likes) Value() (driver.Value, error) {
return json.Marshal(l)
}
// Make the Likes implement the sql.Scanner interface.
func (l *Likes) Scan(value interface{}) error {
b, ok := value.([]byte)
if !ok {
return errors.New("type assertion to []byte failed")
}
return json.Unmarshal(b, &l)
}
答案2
得分: 1
也许这只是因为你使用的数据库驱动程序不支持jsonb数据类型。
我已经测试了github.com/jackc/pgx/v5包,它可以正确解码jsonb字段。请参考下面的示例:
package main
import (
"context"
"log"
"github.com/jackc/pgx/v5"
)
func main() {
// 记得修改下面的 connString。
connString := "postgres://username:password@localhost:5432/database_name"
conn, err := pgx.Connect(context.Background(), connString)
if err != nil {
log.Fatalf("无法连接到数据库:%v\n", err)
}
defer conn.Close(context.Background())
type Like struct {
Name string
Picture string
}
type Post struct {
Id string
Likes []Like
}
var post Post
err = conn.QueryRow(context.Background(), `select 'the_id', '[{"name":"a","picture":"a.png"},{"name":"b","picture":"b.png"}]'::jsonb`).Scan(&post.Id, &post.Likes)
if err != nil {
log.Fatalf("QueryRow 失败:%v\n", err)
}
log.Printf("%#v", post)
}
输出:
main.Post{Id:"the_id", Likes:[]main.Like{main.Like{Name:"a", Picture:"a.png"}, main.Like{Name:"b", Picture:"b.png"}}}
英文:
Maybe that's just because the database driver you use does not support the data type jsonb.
I have tested the package github.com/jackc/pgx/v5, and it can decode the jsonb field correctly. See the example below:
package main
import (
"context"
"log"
"github.com/jackc/pgx/v5"
)
func main() {
// remember to modify the connString below.
connString := "postgres://username:password@localhost:5432/database_name"
conn, err := pgx.Connect(context.Background(), connString)
if err != nil {
log.Fatalf("Unable to connect to database: %v\n", err)
}
defer conn.Close(context.Background())
type Like struct {
Name string
Picture string
}
type Post struct {
Id string
Likes []Like
}
var post Post
err = conn.QueryRow(context.Background(), `select 'the_id', '[{"name":"a","picture":"a.png"},{"name":"b","picture":"b.png"}]'::jsonb`).Scan(&post.Id, &post.Likes)
if err != nil {
log.Fatalf("QueryRow failed: %v\n", err)
}
log.Printf("%#v", post)
}
Output:
main.Post{Id:"the_id", Likes:[]main.Like{main.Like{Name:"a", Picture:"a.png"}, main.Like{Name:"b", Picture:"b.png"}}}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。


评论