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パッケージを取得するにはどうすればよいですか?

回答:


137

ドキュメントが言うように、「任意の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 */ }

2
マットを祝福してください、これが私が探していたものです
Venkata SSKMChaitanya19年

@Matt、それ&MyStruct{ /* values */ }はnilポインタとしてカウントされますか?値はnilではありません。
Shuzheng

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

@nemoわかりました、言い回しを変更しました。それは効率的ではないので、それはそれをしません。json.Marshalerただし、ケースバイケースで行うことができます。
ルーク

2
on自体を実装することによって、空であるかどうか判断することはできません。証明:play.golang.org/p/UEC8A3JGvxMyStructjson.MarshalerMyStruct
chakrit 2014

そのためにはjson.Marshaler、包含Result型自体に実装する必要があり、非常に不便になる可能性があります。
chakrit 2014

3

この機能については、4年以上にわたって有効になっている優れたGolangの提案があります。したがって、現時点では、すぐに標準ライブラリに組み込まれることはないと考えて差し支えありません。@Mattが指摘したように、従来のアプローチは、構造体構造体へのポインターに変換することです。このアプローチが実行不可能(または非実用的)である場合、代替手段は、ゼロ値構造体の省略をサポートする代替のjsonエンコーダーを使用することです。

Golang jsonライブラリ(clarketm / json)のミラーを作成し、タグが適用されるときにゼロ値の構造体省略するためのサポートを追加しましたomitempty。このライブラリ検出はzeroness人気のYAMLエンコーダと同様に、外出先、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"
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.