다음과 같은 구조체가 있습니다.
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 포인터”라고 말합니다. -구조체를 포인터로 만듭니다. 포인터에는 명백한 “빈”값이 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 */ }
답변
@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를 사용하므로 혼란을 피하기 위해 인라인을 선호합니다.
답변
Data
초기화 된 구조체이므로 encoding/json
구조체 내부의 필드가 아닌 즉치 값만 확인 하므로 비어있는 것으로 간주 되지 않습니다.
현재 nil
에서 돌아 오는 json.Marhsler
것은 작동하지 않습니다.
func (_ MyStruct) MarshalJSON() ([]byte, error) {
if empty {
return nil, nil // unexpected end of JSON input
}
// ...
}
Result
마샬 러에게도 줄 수 있지만 노력할 가치가 없습니다.
Matt가 제안한 유일한 옵션 Data
은 포인터 를 만들고 값을로 설정하는 것 nil
입니다.
답변
4 년 넘게 활성화 된이 기능에 대한 뛰어난 Golang 제안 이 있으므로이 시점에서 곧 표준 라이브러리에 포함되지 않을 것이라고 가정하는 것이 안전합니다. @Matt가 지적했듯이 전통적인 접근 방식은 구조체 를 포인터 대 구조체 로 변환하는 것 입니다. 이 접근 방식이 실행 불가능하거나 비실용적이라면 대안은 0 값 구조체 생략을 지원하는 대체 json 인코더를 사용하는 것 입니다.
태그가 적용될 때 0 값 구조체 를 생략하는 지원이 추가 된 Golang json 라이브러리 ( clarketm / json ) 의 미러를 만들었습니다 . 이 라이브러리를 검출합니다 zeroness 인기 YAML 인코더와 유사한 방식으로 이동-YAML 에 의해 반복적으로 확인하는 공공 구조체 필드를 .omitempty
예 :
$ 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"
}