web-dev-qa-db-ja.com

Goで空の構造体をJSONにマーシャリングしない方法

私はこのような構造を持っています:

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パッケージを取得して、空の構造体であるフィールドをnotマーシャリングするにはどうすればよいですか?

66
Matt

ああ!簡単な修正:「任意の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にマーシャリングされなくなりました。

107
Matt

@chakritがコメントで言及したように、MyStructjson.Marshalerを実装し、それを使用するすべての構造体にカスタム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を使用しているため、混乱を避けるためにインラインを使用します。

8
Leylandski

Dataは初期化された構造体です。したがって、encoding/jsonは構造体内のフィールドではなく即値のみを参照するため、空とは見なされません。

残念ながら、json.Marhslerからnilを返すことは現在機能していません。

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

Resultにマーシャラーを与えることもできますが、努力する価値はありません。

Mattが示唆している唯一のオプションは、Dataをポインターにして、値をnilに設定することです。

7
Luke