解组保留空值的JSON

解组保留空值的JSON,json,mongodb,go,Json,Mongodb,Go,我的设想如下: 我有一个Go制作的服务器/工人。在后台例程中,服务器接收JSON格式的消息,然后用该数据更新MongoDB数据库 其中一个问题是,某些MongoDB数据类型(如ObjectId和Date)在必须表示为JSON时通常会转换为字符串,因此在将该数据插入数据库之前,我先将JSON解组到结构中,然后将该结构发送给MongoDB驱动程序。该结构实现了UnmarshalJSON和MarshalBSONValue等方法,因此保留了它们的数据类型 太好了,一切都解决了。但是通过使用结构,我得到了

我的设想如下:

我有一个Go制作的服务器/工人。在后台例程中,服务器接收JSON格式的消息,然后用该数据更新MongoDB数据库

其中一个问题是,某些MongoDB数据类型(如ObjectId和Date)在必须表示为JSON时通常会转换为字符串,因此在将该数据插入数据库之前,我先将JSON解组到结构中,然后将该结构发送给MongoDB驱动程序。该结构实现了
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”值的每种情况编写一个函数。