解组保留空值的JSON
我的设想如下: 我有一个Go制作的服务器/工人。在后台例程中,服务器接收JSON格式的消息,然后用该数据更新MongoDB数据库 其中一个问题是,某些MongoDB数据类型(如ObjectId和Date)在必须表示为JSON时通常会转换为字符串,因此在将该数据插入数据库之前,我先将JSON解组到结构中,然后将该结构发送给MongoDB驱动程序。该结构实现了解组保留空值的JSON,json,mongodb,go,Json,Mongodb,Go,我的设想如下: 我有一个Go制作的服务器/工人。在后台例程中,服务器接收JSON格式的消息,然后用该数据更新MongoDB数据库 其中一个问题是,某些MongoDB数据类型(如ObjectId和Date)在必须表示为JSON时通常会转换为字符串,因此在将该数据插入数据库之前,我先将JSON解组到结构中,然后将该结构发送给MongoDB驱动程序。该结构实现了UnmarshalJSON和MarshalBSONValue等方法,因此保留了它们的数据类型 太好了,一切都解决了。但是通过使用结构,我得到了
UnmarshalJSON
和MarshalBSONValue
等方法,因此保留了它们的数据类型
太好了,一切都解决了。但是通过使用结构,我得到了另一个问题,假设我有以下结构:
type Integers struct {
Foo *int `json:"foo" bson:"foo"`
Bar *int `json:"bar" bson:"foo"`
Baz *int `json:"baz" bson:"foo"`
}
type Integers struct {
Foo SmartassInt `json:"foo,omitempty" bson:"foo,omitempty"`
Bar SmartassInt `json:"bar,omitempty" bson:"bar,omitempty"`
Baz SmartassInt `json:"baz,omitempty" bson:"baz,omitempty"`
}
然后我收到以下JSON:
{"foo": 0, "bar": null}
有了这个JSON,我应该用foo=0
,bar=null
,并忽略baz
来更新我的数据库。但是,如果我在我的结构中解组这个JSON,我将得到等效的:
Integers{
Foo: 1,
Bar: nil,
Baz: nil,
}
但是有了这个,我无法判断我是否收到了bar
和baz
,或者他们只是默认为nil
,因此我无法正确更新数据库
我如何相信它能被解决: 通过具有以下结构:
type Integers struct {
Foo *int `json:"foo" bson:"foo"`
Bar *int `json:"bar" bson:"foo"`
Baz *int `json:"baz" bson:"foo"`
}
type Integers struct {
Foo SmartassInt `json:"foo,omitempty" bson:"foo,omitempty"`
Bar SmartassInt `json:"bar,omitempty" bson:"bar,omitempty"`
Baz SmartassInt `json:"baz,omitempty" bson:"baz,omitempty"`
}
我能够区分null
和未接收的值,如下例所示:
var foo int = 0
var fooPointer *int = &foo
var barPointer *int = nil
integers := Integers{
Foo: &fooPointer,
Bar: &barPointer,
Baz: nil,
}
使用此结构,baz
将不会插入数据库,因为它的值是nil
,并且nil
由于标记ommitempty
而被忽略<但是,code>bar不是nil
,但它指向nil
,这与为空不同,因此它被正确地插入数据库中作为null
但是,如何使用接收到的JSON实现此初始化呢
标准JSON解组器将bar
和baz
初始化为nil
实现我自己的marshaller方法,例如
type NullableInt **int
func (i NullableInt) MarshalJSON() ([]byte, error) {
}
func (i NullableInt) UnmarshalJSON(data []byte) error {
}
也不可能,因为NullableInt
是指针,我不能在指针上实现方法
那么,我可以使用哪种方法来解决这个问题呢?在解码端,您可以为自定义类型编写自定义解组器:
type MaybeInt struct {
Present bool
Null bool
Value int64
}
func (m *MaybeInt) UnmarshalJSON(data []byte) error {
s := string(data)
m.Present = true
if s == "null" {
m.Null = true
return nil
}
v, err := strconv.ParseInt(s, 10, 64)
m.Value = v
return err
}
完整的例子。不幸的是,这在编码端不起作用:MarshalJSON
处理程序无法指示字段为空。显而易见的方法是从封送拆收器返回nil,nil
,但这不起作用。返回[]字节{},nil
,也不会返回
您可能会想:好吧,让我们使用一个指针,当我们想说应该忽略该字段时,将其设置为nil
。这在解码端工作,但现在编码端失败,因为编码器看到文本null
,根本不调用我们的编码器
最终,我们可以将这两种技术结合起来:读入
MaybeInt
,从*MaybeInt
编码(写入)。我们需要并行结构类型。我们可以根据输入类型设置输出类型。我并不认为这很漂亮,其中的reflect
代码很糟糕(您也可以看到我所有的调试跟踪),但这实际上似乎很有效:。实际上,如果您确实需要区分不存在的字段和具有零值的字段,那么最好使用map[string]接口{}
而不是结构,而不是使用reflect
,您可以只为“maybe”值的每种情况编写一个函数。