将空的map[string]interface{}进行编组时,结果是”null”而不是nil。

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

Marshalling empty map[string]interface{} results in "null" instead of nil

问题

我有一个问题,当我尝试执行以下操作时,Go会将"null"插入到我的PostgreSQL数据库的jsonb列中,而且结构体的属性(在这种情况下是map[string]interface{}类型)为空:

accessMembers, _ := json.Marshal(c.AccessMembers)

在进行测试打印时,输出与存储到数据库中的值相同:

fmt.Println(string(accessMembers)) // 输出字符串"null"

问题是,我需要它是nil(不是字符串,而是Golang的nil),所以当我在下面的Exec函数中使用它时:

sqlStr := `UPDATE my_table SET access_members=$1 WHERE id=$2;`

_, err := db.Exec(sqlStr, accessMembers, c.Id)

它应该等同于(有效!!)执行以下操作:

_, err := db.Exec(sqlStr, nil, c.Id)

我尝试了很多不同的解决方法,我认为它们应该有效,但实际上它们并没有。

例如:

var accessMembers []byte

am, _ := json.Marshal(c.AccessMembers)

if string(am) != "null"{
    accessMembers = am
}

sqlStr := `UPDATE my_table SET access_members=$1 WHERE id=$2;`
    
_, err := db.Exec(sqlStr, accessMembers, c.Id)

这导致了以下错误:"pq: invalid input syntax for type json"

我无法理解为什么这不同于在Exec函数的参数中明确写入nil,因为[]byte显然必须是nil,对吗?

我在这里做错了什么?这是我希望它能工作的方式:http://play.golang.org/p/UoLAGfhhRl

答案:

var accessMembers interface{} = nil

if c.AccessMembers != nil {
    j, _ := json.Marshal(c.AccessMembers)
    accessMembers = j
}

感谢thwd提供了解决方案,并指出数据库驱动程序如何以不同的方式编组nil,建议使用interface{}而不是[]byte。

感谢DonSeba提醒我在使用资源进行编组、字符串转换和比较之前检查结构体字段是否存在,这应该更加昂贵。

英文:

I have a problem where Go is inserting "null" into my PostgreSQL database's jsonb columns if I try to do the following, and the structs property (in this case of type map[string]interface{}) is empty:

accessMembers, _ := json.Marshal(c.AccessMembers)

Doing a test print it outputs the same as the value which get stored into the database:

fmt.Println(string(accessMembers)) // equals the string "null"

The problem is that I need it to be - nil (not a string, but the Golang nil), so when I use it in the Exec function below:

sqlStr := `UPDATE my_table SET access_members=$1 WHERE id=$2;`

_, err := db.Exec(sqlStr, accessMembers, c.Id)

It should be the equivalent (which works!!) of doing:

_, err := db.Exec(sqlStr, nil, c.Id)

I tried a lot of different workarounds, which I thought had to work, but they didn't.

eg.

var accessMembers []byte

am, _ := json.Marshal(c.AccessMembers)

if string(am) != "null"{
	accessMembers = am
}

sqlStr := `UPDATE my_table SET access_members=$1 WHERE id=$2;`
    
_, err := db.Exec(sqlStr, accessMembers, c.Id)

Which results in the following error: "pq: invalid input syntax for type json"

I can't understand why this is not the same, as explicitly writing nil in the Exec functions parameter, since the []byte clearly must be nil, right?

What am I doing wrong here? Here's how I had hoped it would work: http://play.golang.org/p/UoLAGfhhRl

Answer:

var accessMembers interface{} = nil

if c.AccessMembers != nil {
    j, _ := json.Marshal(c.AccessMembers)
    accessMembers = j
}

Thanks to thwd for providing the solution basically, and for pointing out how the database driver marshals nil differently, suggesting interface{} instead of []byte.

Thanks to DonSeba for reminding me to check if the struct field exists before using resources to marshal, do string conversion and comparison, which ought to be more expensive.

答案1

得分: 3

我无法理解为什么这两者不同,明确在Exec函数的参数中写入nil,与将[]byte设置为nil是一样的,因为[]byte明显必须是nil,对吗?

nil是有类型的。对于类型为map[string]interface{}[]byte的情况,数据库驱动程序对nil的处理方式与对interface{}(即db.Exec的第二个参数的类型)不同。你可以这样做:

var i interface{} = nil

j, _ := json.Marshal(c.AccessMembers)

if string(j) != "null" {
     i = j
}

sqlStr := `UPDATE content SET access_members=$1 WHERE id=$2;`

_, err := db.Exec(sqlStr, i, c.Id)
英文:

> I can't understand why this is not the same, as explicitly writing nil in the Exec functions parameter, since the []byte clearly must be nil, right?

nil has a type. The database driver marshals nil differently for type map[string]interface{} or []byte than for interface{} (which is the type of the second argument of db.Exec). You should be able to do:

var i interface{} = nil

j, _ := json.Marshal(c.AccessMembers)

if string(j) != "null" {
     i = j
}

sqlStr := `UPDATE content SET access_members=$1 WHERE id=$2;`

_, err := db.Exec(sqlStr, i, c.Id)

答案2

得分: 1

你还没有初始化你的AccessMembers

在你决定编组值之前,你应该检查它是否存在。

if c.AccessMembers == nil {
    c.AccessMembers = make(map[string]interface{})
}

如果你按照上述方式操作,那么以下代码的结果:

accessMembers, err := json.Marshal(c.AccessMembers)

将会是一个有效的空 JSON 值{},可以将其存储到数据库中,并在以后检索该值。

更新:

关于"NULL"值,这里有一些讨论:
https://code.google.com/p/go/issues/detail?id=2278

英文:

You have not initialized your AccessMembers.

Before you decide to marshal the value you should check if it exists.

if c.AccessMembers == nil {
    c.AccessMembers = make(map[string]interface{})
}

If you do the above, the result of :

accessMembers, err := json.Marshal(c.AccessMembers)

will be an valid empty json value {}, and valid for storing into the db and retrieving the value later.

UPDATE :

there has been some discussion about the "NULL" value here :
https://code.google.com/p/go/issues/detail?id=2278

huangapple
  • 本文由 发表于 2015年6月15日 15:25:13
  • 转载请务必保留本文链接:https://go.coder-hub.com/30839476.html
匿名

发表评论

匿名网友

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

确定