developer tip

Go를 사용하여 빈 구조체를 JSON으로 마샬링하지 않는 방법은 무엇입니까?

optionbox 2020. 11. 11. 19:58
반응형

Go를 사용하여 빈 구조체를 JSON으로 마샬링하지 않는 방법은 무엇입니까?


다음과 같은 구조체가 있습니다.

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

그러나 MyStruct의 인스턴스가 완전히 비어 있어도 (즉, 모든 값이 기본값 임) 다음과 같이 직렬화됩니다.

"data":{}

인코딩 / json 문서에 "빈"필드가 다음과 같이 지정되어 있음을 알고 있습니다.

false, 0, 모든 nil 포인터 또는 인터페이스 값 및 길이가 0 인 모든 배열, 슬라이스, 맵 또는 문자열

그러나 모든 비어있는 / 기본값을 가진 구조체는 고려하지 않습니다. 모든 필드에도로 태그가 지정되어 omitempty있지만 아무 효과가 없습니다.

빈 구조체 인 필드를 마샬링 하지 않도록 JSON 패키지를 얻으려면 어떻게 해야합니까?


오! 쉬운 수정 : "모든 nil 포인터" -구조체를 포인터로 만듭니다.

고치다:

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

를 주목하라 *MyStruct- 나는를 만들 때 MyStruct지금, 나는 단순히 참조하여 수행 :

myStruct := &MyStruct{ /* values */ }

이제 "빈" MyStruct은 더 이상 원하는대로 JSON으로 마샬링되지 않습니다.


@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
}

If you have huge structs with many fields this can become tedious, especially changing a struct's implementation later, but short of rewriting the whole json package to suit your needs (not a good idea), this is pretty much the only way I can think of getting this done while still keeping a non-pointer MyStruct in there.

Also, you don't have to use inline structs, you can create named ones. I use LiteIDE with code completion though, so I prefer inline to avoid clutter.


Data is an initialized struct, so it isn't considered empty because encoding/json only looks at the immediate value, not the fields inside the struct.

Unfortunately returning nil from json.Marhsler currently doesn't work:

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

You could give Result a marshaler as well, but it's not worth the effort.

The only option, as Matt suggests, is to make Data a pointer and set the value to nil.


There is an outstanding Golang proposal for this feature which has been active for over 4 years, so at this point, it is safe to assume that it will not make it into the standard library anytime soon. As @Matt pointed out, the traditional approach is to convert the structs to pointers-to-structs. If this approach is infeasible (or impractical), then an alternative is to use an alternate json encoder which does support omitting zero value structs.

I created a mirror of the Golang json library (clarketm/json) with added support for omitting zero value structs when the omitempty tag is applied. This library detects zeroness in a similar manner to the popular YAML encoder go-yaml by recursively checking the public struct fields.

e.g.

$ 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"
}

참고URL : https://stackoverflow.com/questions/18088294/how-to-not-marshal-an-empty-struct-into-json-with-go

반응형