将 SQL 查询结果集存储在嵌套结构中,并在为空时省略。

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

Storing Sql result sets in a nested struct and omit if empty

问题

我正在尝试将 SQL 查询结果存储在一个结构体中,并以 JSON 格式显示出来。我已经基本完成了。现在的问题是,如果嵌套结构的结果集为空,我不想显示它。

在这里指定的问题中有相同的问题(链接:https://stackoverflow.com/questions/33447334/golang-json-marshal-how-to-omit-empty-nested-struct),但是使用指针会导致恐慌,可能是因为我使用了 &user.Profile.Firstname

>2015/11/01 16:42:16 恐慌恢复 -> 运行时错误:无效的内存地址或空指针解引用

如果我移除指针,那么一切都正常工作,只是空字段仍然存在。我不知道如何解决这个问题。

package main

import (
	"database/sql"
	"github.com/gin-gonic/gin"
	_ "github.com/go-sql-driver/mysql"
	"log"
)

type User struct {
	Id       int64
	Username string
	Email    string
	Profile  Profile `json:",omitempty"`
}

type Profile struct {
	Id        int64  `json:",omitempty"`
	UserId    int64  `json:",omitempty"`
	Firstname *string `json:",omitempty"`
	Lastname  *string `json:",omitempty"`
}

var DB *sql.DB

func checkErr(err error, msg string) {
	if err != nil {
		log.Fatal(msg, err)
	}
}

func main() {
	DB, _ = sql.Open("mysql", "username:secrect@/database")
	defer DB.Close()

	r := gin.Default()
	v1 := r.Group("api/v1/")
	{
		v1.GET("users", GetUsers)
	}
	r.Run(":8080")
}


func GetUsers(c *gin.Context) {
	stmt, err := DB.Query("Select users.id, username , email , firstname , lastname from users left join profiles on users.id = profiles.user_id ")
	if err != nil {
		panic(err.Error())
	}
	defer stmt.Close()

	users := []User{}

	for stmt.Next() {
		var user User
		err := stmt.Scan(&user.Id, &user.Username, &user.Email, &user.Profile.Firstname, &user.Profile.Lastname)
		if err != nil {
			panic(err.Error())
		}

		users = append(users, user)
	}

	c.JSON(200, &users)
}

输出:

{
  "Id": 1,
  "Username": "test1",
  "Email": "test1@example.com",
  "Profile": {
    "Firstname": "John",
    "Lastname": "Doe"
  }
},
{
  "Id": 2,
  "Username": "test2",
  "Email": "test2@example.com",
  "Profile": {
  }
}
英文:

I am trying to store SQL query dataset in a struct and display in JSON. I have almost done it. Now the problem is if the result set of nested structs is empty then I would not like to display it.

Same problem specified here but using a pointer ends in a panic when scanning may be because I am using &user.Profile.Firstname

>2015/11/01 16:42:16 Panic recovery -> runtime error: invalid memory address or nil pointer dereference

If I remove the pointer then everything works fine just the empty field remains. I am confused how to achieve this.

package main
import (
"database/sql"
"github.com/gin-gonic/gin"
_ "github.com/go-sql-driver/mysql"
"log"
)
type User struct {
Id       int64
Username string
Email    string
Profile  Profile `json:",omitempty"`
}
type Profile struct {
Id        int64  `json:",omitempty"`
UserId    int64  `json:",omitempty"`
Firstname *string `json:",omitempty"`
Lastname  *string `json:",omitempty"`
}
var DB *sql.DB
func checkErr(err error, msg string) {
if err != nil {
log.Fatal(msg, err)
}
}
func main() {
DB, _ = sql.Open("mysql", "username:secrect@/database")
defer DB.Close()
r := gin.Default()
v1 := r.Group("api/v1/")
{
v1.GET("users", GetUsers)
}
r.Run(":8080")
}
func GetUsers(c *gin.Context) {
stmt, err := DB.Query("Select users.id, username , email , firstname , lastname from users left join profiles on users.id = profiles.user_id ")
if err != nil {
panic(err.Error())
}
defer stmt.Close()
users := []User{}
for stmt.Next() {
var user User
err := stmt.Scan(&user.Id, &user.Username, &user.Email, &user.Profile.Firstname, &user.Profile.Lastname)
if err != nil {
panic(err.Error())
}
users = append(users, user)
}
c.JSON(200, &users)
}

Output:

{
"Id": 1,
"Username": "test1",
"Email": "test1@example.com",
"Profile": {
"Firstname": "John",
"Lastname": "Doe"
}
},
{
"Id": 2,
"Username": "test2",
"Email": "test2@example.com",
"Profile": {
}
},

答案1

得分: 3

假设gin在内部使用encoding/json,如果你想要忽略一个空的Profile,我认为你需要在User结构体中将其声明为指针类型:

type User struct {
    ....
    Profile *Profile `json:",omitempty"`
}

请注意文档中的说明:

> "omitempty"选项指定如果字段具有空值(定义为false、0、nil指针、nil接口值以及任何空数组、切片、映射或字符串),则应从编码中省略该字段。

文档中并没有提到会检测结构体的零值。

这样做会导致在从数据库中扫描数据时出现一些问题。你可以声明一个Profile变量,将数据扫描到其中,然后将其与零值的Profile进行比较,并将其赋值给User结构体,如果它包含数据的话:

var user User
var profile Profile
_ = stmt.Scan(&user.Id, &user.Username, &user.Email, &profile.Firstname, &profile.Lastname)
if profile != Profile{} {
    user.Profile = &profile
}

我怀疑这不是最优雅的解决方案,但我希望它能指导你朝着正确的方向前进。

英文:

Assuming gin uses encoding/json under the covers, if you want to omit an empty Profile, I believe you need it to be a pointer in User:

type User struct {
....
Profile *Profile `json:",omitempty"`
}

Note the documentation:

> The "omitempty" option specifies that the field should be omitted from the encoding if the field has an empty value, defined as false, 0, a nil pointer, a nil interface value, and any empty array, slice, map, or string.

It doesn't say that the zero value of a struct will be detected.

That will leave you with some issues scanning your data from the db. You could declare a Profile, scan into it, compare to the zero profile and assign it to the User if it has data:

var user User
var profile Profile
_ = stmt.Scan(&user.Id, &user.Username, &user.Email, &profile.Firstname, &profile.Lastname)
if profile != Profile{} {
user.Profile = &profile
}

I suspect that's not the most elegant solution, but I hope it's the right direction.

huangapple
  • 本文由 发表于 2015年11月1日 19:04:02
  • 转载请务必保留本文链接:https://go.coder-hub.com/33461486.html
匿名

发表评论

匿名网友

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

确定