英文:
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"}}}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论