从Postgres中获取的嵌套JSON结果在Golang中的处理方式。

huangapple go评论74阅读模式
英文:

Nested JSON result in Golang from Postgres

问题

我正在使用gingorp

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

查询结果:
从Postgres中获取的嵌套JSON结果在Golang中的处理方式。

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})

如果只有一个项目,它可以正常工作。

从Postgres中获取的嵌套JSON结果在Golang中的处理方式。

但是,如果有多个项目,会出现以下错误:

错误信息:json: error calling MarshalJSON for type json.RawMessage: invalid character '"' after top-level value

有什么建议吗?

附注:我是Golang的新手。

英文:

I'm using gin and gorp

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

The results:
从Postgres中获取的嵌套JSON结果在Golang中的处理方式。

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

从Postgres中获取的嵌套JSON结果在Golang中的处理方式。

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库中的MarshalJSONUnmarshalJSON复制粘贴的重新实现。

//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"`
}

从Postgres中获取的嵌套JSON结果在Golang中的处理方式。

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.

huangapple
  • 本文由 发表于 2016年3月17日 14:37:33
  • 转载请务必保留本文链接:https://go.coder-hub.com/36053251.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定