如何不使用Go将封送的空结构封送至JSON?


88

我有一个这样的结构:

type Result struct {
    Data       MyStruct  `json:"data,omitempty"`
    Status     string    `json:"status,omitempty"`
    Reason     string    `json:"reason,omitempty"`
}

但是,即使MyStruct实例完全为空(意味着所有值都是默认值),它也会被序列化为:

"data":{}

我知道encoding / json文档指定“空”字段为:

false,0,任何nil指针或接口值以及任何长度为零的数组,切片,映射或字符串

但不考虑具有所有空/默认值的结构。它的所有字段也都标有omitempty,但这无效。

如何获取JSON包以封送我的字段为空结构?

Answers:


137

正如文档所说,“任何零指针”。-使结构成为指针。指针具有明显的“空”值:nil

修复-使用结构指针字段定义类型:

type Result struct {
    Data       *MyStruct `json:"data,omitempty"`
    Status     string    `json:"status,omitempty"`
    Reason     string    `json:"reason,omitempty"`
}

然后是一个像这样的值:

result := Result{}

将封送为:

{}

说明:请注意*MyStruct我们类型定义中的。JSON序列化并不关心它是否是指针-这是运行时详细信息。因此,使结构字段成为指针仅对编译和运行时有影响。

请注意,如果确实将字段类型从更改MyStruct*MyStruct,则将需要指针来构造值以填充它,如下所示:

Data: &MyStruct{ /* values */ }

2
祝福您,Matt,这就是我想要的
Venkata SSKM Chaitanya

@Matt,您确定&MyStruct{ /* values */ }算作一个零指针吗?该值不为nil。

@Matt是否可以使此默认行为?我想永远作呕。(基本上不要在所有结构的每个字段中都使用标记)
Mohit Singh

17

正如@chakrit在评论中提到的那样,您无法通过json.Marshaler在上MyStruct实现来使它正常工作,并且在使用它的每个结构上实现自定义JSON编组函数可能需要做更多的工作。是否值得进行额外的工作,或者是否准备好在JSON中使用空结构,这实际上取决于您的用例,但这是我使用的模式Result

type Result struct {
    Data       MyStruct
    Status     string   
    Reason     string    
}

func (r Result) MarshalJSON() ([]byte, error) {
    return json.Marshal(struct {
        Data     *MyStruct   `json:"data,omitempty"`
        Status   string      `json:"status,omitempty"`
        Reason   string      `json:"reason,omitempty"`
    }{
        Data:   &r.Data,
        Status: r.Status,
        Reason: r.Reason,
    })        
}

func (r *Result) UnmarshalJSON(b []byte) error {
    decoded := new(struct {
        Data     *MyStruct   `json:"data,omitempty"`
        Status   string      `json:"status,omitempty"`
        Reason   string      `json:"reason,omitempty"`
    })
    err := json.Unmarshal(b, decoded)
    if err == nil {
        r.Data = decoded.Data
        r.Status = decoded.Status
        r.Reason = decoded.Reason
    }
    return err
}

如果您在许多领域拥有庞大的结构,这可能会变得乏味,尤其是稍后更改结构的实现,但是由于缺少重写整个json程序包以满足您的需求的方法(这不是一个好主意),这几乎是我想到的唯一方法这样做的同时仍然保持非指针MyStruct在其中。

另外,您不必使用内联结构,可以创建命名结构。虽然我将LiteIDE与代码完成一起使用,所以我更喜欢内联以避免混乱。


9

Data是已初始化的结构,因此不会将其视为空,因为它encoding/json只会查看立即数,而不是结构中的字段。

不幸的是,niljson.Marhsler当前返回不起作用:

func (_ MyStruct) MarshalJSON() ([]byte, error) {
    if empty {
        return nil, nil // unexpected end of JSON input
    }
    // ...
}

您也可以提供Result封送处理程序,但这是不值得的。

正如Matt所建议的,唯一的选择是制作Data一个指针并将其值设置为nil


1
我不明白为什么encoding/json 不能检查该结构的子字段。效率不是很高,是的。但这当然不是不可能的。
nemo

@nemo我明白你的意思,我改变了措辞。它没有这样做,因为它效率不高。不过,可以根据json.Marshaler具体情况进行处理。
路加福音

2
这是不是可以据此决定是否或不MyStruct为空,通过实施json.MarshalerMyStruct本身。证明:play.golang.org/p/UEC8A3JGvx
chakrit 2014年

为此,您必须json.Marshaler在包含Result类型本身上实现,这可能非常不方便。
chakrit 2014年

3

有一项出色的Golang提案已投入使用,该提案已使用了4年以上,因此,可以肯定地说,它不会很快进入标准库。正如@Matt指出的那样,传统方法是将结构转换为指针到结构。如果这种方法不可行(或不切实际),那么另一种方法是使用备用json编码器,该编码器确实支持省略零值结构

我创建了Golang json库(clarketm / json)的镜像,并添加了对在应用标记时省略零值结构的支持omitempty。该库通过递归检查公共结构字段,以类似于流行的YAML编码器go-yaml的方式检测零度

例如

$ go get -u "github.com/clarketm/json"
import (
    "fmt"
    "github.com/clarketm/json" // drop-in replacement for `encoding/json`
)

type Result struct {
    Data   MyStruct `json:"data,omitempty"`
    Status string   `json:"status,omitempty"`
    Reason string   `json:"reason,omitempty"`
}

j, _ := json.Marshal(&Result{
    Status: "204",
    Reason: "No Content",
})

fmt.Println(string(j))
// Note: `data` is omitted from the resultant json.
{
  "status": "204"
  "reason": "No Content"
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.