英文:
Nested JSON result in Golang from Postgres
问题
SQL查询语句:
SELECT p.project_id, p.name,
COALESCE(NULLIF(json_agg(a.*)::TEXT, '[null]'), '[]')::JSON AS apps
FROM project p LEFT JOIN app a USING (project_id)
WHERE p.user_id=19
GROUP BY p.project_id, p.name ORDER BY project_id
Golang代码:
type Project struct {
ID int64 `db:"project_id, primarykey, autoincrement" json:"id"`
UserID int64 `db:"user_id" json:"user_id"`
Name string `db:"name" json:"name"`
Status int `db:"status" json:"status"`
UpdatedAt int64 `db:"updated_at" json:"updated_at"`
CreatedAt int64 `db:"created_at" json:"created_at"`
Apps json.RawMessage `json:"apps"`
}
func GetProjects(userID int64, page string) []Project {
var projects []Project
var err error
_, err = db.GetDB().Select(&projects, "SELECT p.project_id, p.name, COALESCE(NULLIF(json_agg(a.*)::TEXT, '[null]'), '[]')::JSON AS apps FROM project p LEFT JOIN app a USING (project_id) WHERE p.user_id=$1 GROUP BY p.project_id, p.name ORDER BY project_id LIMIT 10 OFFSET $2", userID, page)
fmt.Println("err", err)
return projects
}
并使用以下代码返回结果:c.JSON(200, gin.H{"data": projects})
如果只有一个项目,它可以正常工作。
但是,如果有多个项目,会出现以下错误:
错误信息:json: error calling MarshalJSON for type json.RawMessage: invalid character '"' after top-level value
有什么建议吗?
附注:我是Golang的新手。
英文:
The SQL:
SELECT p.project_id, p.name,
COALESCE(NULLIF(json_agg(a.*)::TEXT, '[null]'), '[]')::JSON AS apps
FROM project p LEFT JOIN app a USING (project_id)
WHERE p.user_id=19
GROUP BY p.project_id, p.name ORDER BY project_id
Golang
type Project struct {
ID int64 `db:"project_id, primarykey, autoincrement" json:"id"`
UserID int64 `db:"user_id" json:"user_id"`
Name string `db:"name" json:"name"`
Status int `db:"status" json:"status"`
UpdatedAt int64 `db:"updated_at" json:"updated_at"`
CreatedAt int64 `db:"created_at" json:"created_at"`
Apps json.RawMessage `json:"apps"`
}
func GetProjects(userID int64, page string) []Project {
var projects []Project
var err error
_, err = db.GetDB().Select(&projects, "SELECT p.project_id, p.name, COALESCE(NULLIF(json_agg(a.*)::TEXT, '[null]'), '[]')::JSON AS apps FROM project p LEFT JOIN app a USING (project_id) WHERE p.user_id=$1 GROUP BY p.project_id, p.name ORDER BY project_id LIMIT 10 OFFSET $2", userID, page)
fmt.Println("err", err)
return projects
}
And returning the results using: c.JSON(200, gin.H{"data": projects})
It works if there's only one project
But if there's more than one project it gives the below error:
The error: json: error calling MarshalJSON for type json.RawMessage: invalid character '"' after top-level value
Any suggestions?
P.S: I'm a newbie in Golang
答案1
得分: 4
你可以使用这个网站http://json2struct.mervine.net/根据结果获取正确的结构。只需复制选择的结果,然后生成你需要的结构。
或者你可以创建一个包含Project结构数组的新类型:
type Projects []Project
英文:
you can use this site http://json2struct.mervine.net/ to get right struct according to result. just copy select result, and generate your decent struct
or you can produce new type which have Project struct array:
type Projects []Project
答案2
得分: 0
我使用下面这个答案中的解决方案使其工作:
> 我不知道这个解决方案有多干净,但我最终创建了自己的数据类型JSONRaw
。数据库驱动程序将其视为[]byte
,但在Go代码中仍然可以像json.RawMessage
一样处理。
> 这是从encoding/json库中的MarshalJSON
和UnmarshalJSON
复制粘贴的重新实现。
//JSONRaw ...
type JSONRaw json.RawMessage
//Value ...
func (j JSONRaw) Value() (driver.Value, error) {
byteArr := []byte(j)
return driver.Value(byteArr), nil
}
//Scan ...
func (j *JSONRaw) Scan(src interface{}) error {
asBytes, ok := src.([]byte)
if !ok {
return error(errors.New("Scan source was not []bytes"))
}
err := json.Unmarshal(asBytes, &j)
if err != nil {
return error(errors.New("Scan could not unmarshal to []string"))
}
return nil
}
//MarshalJSON ...
func (j *JSONRaw) MarshalJSON() ([]byte, error) {
return *j, nil
}
//UnmarshalJSON ...
func (j *JSONRaw) UnmarshalJSON(data []byte) error {
if j == nil {
return errors.New("json.RawMessage: UnmarshalJSON on nil pointer")
}
*j = append((*j)[0:0], data...)
return nil
}
//Project ....
type Project struct {
ID int64 `db:"project_id, primarykey, autoincrement" json:"id"`
UserID int64 `db:"user_id" json:"user_id"`
Name string `db:"name" json:"name"`
Status int `db:"status" json:"status"`
UpdatedAt int64 `db:"updated_at" json:"updated_at"`
CreatedAt int64 `db:"created_at" json:"created_at"`
Apps JSONRaw `json:"apps"`
}
但我想知道是否有其他干净的方法?希望这也对其他人有所帮助。
英文:
I made it work using this solution below from this answer
> I don't know how clean of a solution this is but I ended up making my own data type JSONRaw
. The DB driver sees it as a []btye
but it can still be treated like a json.RawMessage
in the Go Code.
>
> This is a copy paste reimplementation of MarshalJSON
and UnmarshalJSON
from the encoding/json library.
//JSONRaw ...
type JSONRaw json.RawMessage
//Value ...
func (j JSONRaw) Value() (driver.Value, error) {
byteArr := []byte(j)
return driver.Value(byteArr), nil
}
//Scan ...
func (j *JSONRaw) Scan(src interface{}) error {
asBytes, ok := src.([]byte)
if !ok {
return error(errors.New("Scan source was not []bytes"))
}
err := json.Unmarshal(asBytes, &j)
if err != nil {
return error(errors.New("Scan could not unmarshal to []string"))
}
return nil
}
//MarshalJSON ...
func (j *JSONRaw) MarshalJSON() ([]byte, error) {
return *j, nil
}
//UnmarshalJSON ...
func (j *JSONRaw) UnmarshalJSON(data []byte) error {
if j == nil {
return errors.New("json.RawMessage: UnmarshalJSON on nil pointer")
}
*j = append((*j)[0:0], data...)
return nil
}
//Project ....
type Project struct {
ID int64 `db:"project_id, primarykey, autoincrement" json:"id"`
UserID int64 `db:"user_id" json:"user_id"`
Name string `db:"name" json:"name"`
Status int `db:"status" json:"status"`
UpdatedAt int64 `db:"updated_at" json:"updated_at"`
CreatedAt int64 `db:"created_at" json:"created_at"`
Apps JSONRaw `json:"apps"`
}
But I was wondering if there's a clean way other than this?
Hope this also help others.
答案3
得分: 0
我使用了以下链接来解决问题:
https://www.alexedwards.net/blog/using-postgresql-jsonb
和
http://json2struct.mervine.net/
我花了很长时间苦苦挣扎,然后才意识到结构体起了关键作用。
英文:
I used the below link
https://www.alexedwards.net/blog/using-postgresql-jsonb
and
http://json2struct.mervine.net/
to make it work .
I was struggling for long time and then realized that the struct did the trick.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论