web-dev-qa-db-ja.com

Golangは、空の配列をnullとしてシリアル化/逆シリアル化します

構造体の空の配列属性(nullではない)をシリアル化し、それを空の配列(再びnullではない)に逆シリアル化する方法はありますか?

空の配列が実際にはnullへのポインターであることを考えると、空の配列とnullへのポインターの最初の違いは、シリアル化/逆シリアル化後に完全に失われますか?

最悪の実際的なシナリオは、空の配列属性を自分のRESTクライアントに、json "att":[]として最初に表示し、キャッシュレジスタの後にredisして回復することです。それは、同じ属性が「att」:nullとしてクライアントに表示され、契約が破られ、多くの混乱を引き起こします。

まとめ:シリアル化/逆シリアル化した後、jsonの空の配列のように顧客2のアドレスを表示することができます=> https:// play .golang.org/p/TVwvTWDyHZ

15
DLopes

私はあなたがそれをすることができる最も簡単な方法はあなたのラインを変えることであるとかなり確信しています

var cust1_recovered Customer

cust1_recovered := Customer{Addresses: []Address{}}

私があなたの質問を間違って読んでいない限り、これがあなたの望ましい出力であると私は信じています:

ORIGINAL  Customer 2 {
  "Name": "Customer number 2",
  "Addresses": []
}
RECOVERED Customer 2 {
  "Name": "Customer number 2",
  "Addresses": []
}

確認する遊び場は次のとおりです。 https://play.golang.org/p/T9K1VSTAM

@mikeが指摘したように、ここでの制限は、エンコードする前にAddressesが本当にnilである場合、デコードすると、jsonに相当するnullを取得せず、代わりに取得することです。空のリストになってしまいます。

12
sberry

いいえ、できません。その理由を理解するために、Goの仕様を見てみましょう。空とnilの2つの異なる結果を出力するには、どのシリアル化方法でも2つの違いを識別できる必要があります。ただし、Goの仕様によると、

2つの配列タイプは、要素タイプが同じで配列長が同じ場合、同一です。

どちらにも要素が含まれておらず、同じ要素タイプであるため、長さが異なるだけである可能性がありますが、

Nilスライス、マップ、またはチャネルの長さは0です。

したがって、比較すると、わかりません。もちろん、比較以外の方法もあるので、実際に棺桶に釘を入れるために、ここにそれらが同じ基底表現を持っていることを示す部分があります。仕様はまたそれを保証します

サイズがゼロより大きいフィールド(または要素)が含まれていない場合、構造体または配列タイプのサイズはゼロになります。

したがって、長さがゼロの配列に実際に割り当てられる構造は、サイズがゼロである必要があります。サイズがゼロの場合、空であるかnilであるかに関する情報を格納できないため、オブジェクト自体もそれを知ることができません。つまり、nil配列と長さゼロの配列の間に違いはありません。

「空の配列とnullへのポインタの知覚可能な初期の違い」は、シリアル化/逆シリアル化中に失われることはなく、最初の割り当てが完了した瞬間から失われます。

9
Mike Precup

別の解決策として、MarshalSafeCollections()という新しいメソッドを追加する forked encoding/json があります。このメソッドは、スライス/配列/マップをそれぞれの空の値としてマーシャリングします([]/{})。インスタンス化のほとんどはデータレイヤーで行われるため、httpレスポンスレイヤーの問題を修正するコードを追加したくありませんでした。ライブラリへの変更は最小限であり、goリリースに従います。

2
cbelsole