如何不使用Go将空结构编组为JSON?

Mat*_*att 70 json go

我有这样的结构:

type Result struct {
    Data       MyStruct  `json:"data,omitempty"`
    Status     string    `json:"status,omitempty"`
    Reason     string    `json:"reason,omitempty"`
}
Run Code Online (Sandbox Code Playgroud)

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

"data":{}
Run Code Online (Sandbox Code Playgroud)

我知道encoding/json docs指定"empty"字段是:

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

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

如何让JSON包不能封送我的空结构字段?

Mat*_*att 111

哦! 轻松修复:"任何无指针." - 使结构成为指针.

固定:

type Result struct {
    Data       *MyStruct `json:"data,omitempty"`
    Status     string    `json:"status,omitempty"`
    Reason     string    `json:"reason,omitempty"`
}
Run Code Online (Sandbox Code Playgroud)

注意*MyStruct- 当我创建一个MyStructnow时,我只是通过引用这样做:

myStruct := &MyStruct{ /* values */ }
Run Code Online (Sandbox Code Playgroud)

现在,"空" MyStruct不再按需要编组到JSON中.

  • 祝福你,马特,这就是我一直在寻找的 (2认同)

Ley*_*ski 8

作为@chakrit在评论中提到,你不能让这种通过实现工作json.MarshalerMyStruct,并实施对使用它可以是一个大量的工作,每一个结构的自定义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
}
Run Code Online (Sandbox Code Playgroud)

如果你有很多领域的巨大结构,这可能会变得乏味,特别是稍后更改结构的实现,但是不能重写整个json包以满足你的需要(不是一个好主意),这几乎是我能想到的唯一方法这样做仍然保持非指针MyStruct在那里.

此外,您不必使用内联结构,您可以创建命名结构.我使用LiteIDE代码完成,所以我更喜欢内联以避免混乱.


Tra*_*rke 8

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

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

例如

$ go get -u "github.com/clarketm/json"
Run Code Online (Sandbox Code Playgroud)
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))
Run Code Online (Sandbox Code Playgroud)
// Note: `data` is omitted from the resultant json.
{
  "status": "204"
  "reason": "No Content"
}
Run Code Online (Sandbox Code Playgroud)


Luk*_*uke 7

Data是一个初始化的结构,因此它不被视为空,因为encoding/json只查看立即值,而不是结构内的字段.

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

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

你也可以给Result一个封送者,但这不值得努力.

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

  • 我不明白为什么 `encoding/json` **不能**检查结构的子字段。是的,这不会很有效率。但这当然不是不可能的。 (2认同)
  • 通过在`MyStruct`本身上实现`json.Marshaler`,可以_decide wet或者不是'MyStruct`是空的.证明:http://play.golang.org/p/UEC8A3JGvx (2认同)