web-dev-qa-db-ja.com

空の文字列、nullを使用するか、APIリクエスト/レスポンスで空のプロパティを削除します

スキーマレスJSON形式のように、APIを介してオブジェクトを転送する場合、存在しない文字列プロパティを返す理想的な方法は何ですか?以下のリンクにある例のように、これを行うにはさまざまな方法があることを知っています。

私は過去にnullを使用したと確信していますが、それを行う正当な理由はありません。データベースを扱う場合は、nullを使用するのは簡単です。しかし、データベースは、APIの反対側の当事者に関係するべきではない実装の詳細のように見えます。例えば。おそらく、値(null以外)を持つプロパティのみを格納するスキーマレスデータストアを使用します。

コードの観点からは、文字列関数を1つの型、つまりstring(nullではない)でのみ機能するように制限すると、証明が容易になります。 nullを回避することも、Optionオブジェクトを持つ理由です。したがって、要求/応答を生成するコードがnullを使用しない場合、私の推測では、APIの反対側にあるコードもnullを使用するように強制されることはありません。

Nullの使用を回避する簡単な方法として、空の文字列を使用するのが好きです。 nullを使用して空の文字列に対して私が聞いた1つの引数は、空の文字列はプロパティが存在することを意味するということです。私は違いを理解していますが、それが単なる実装の詳細であるかどうか、およびnullまたは空の文字列を使用すると実際の違いが生じるかどうかについても疑問に思います。また、空の文字列は空の配列に似ているのでしょうか。

では、これらの懸念に対処するための最良の方法はどれですか?転送されるオブジェクトの形式(スキーマ/スキーマレス)に依存しますか?

25
imel96

TLDR; nullプロパティを削除

最初に覚えておかなければならないのは、アプリケーションの エッジはオブジェクト指向ではない (そのパラダイムでプログラミングしている場合は機能しない)ことです。受け取るJSONはオブジェクトではないため、そのように扱うべきではありません。これは、オブジェクトに変換できる(または変換できない)構造化データです。一般に、着信JSONは、検証されるまでビジネスオブジェクトとして信頼されるべきではありません。 逆シリアル化されたという事実だけでは有効になりません。 JSONもバックエンド言語に比べてプリミティブが制限されているため、JSONに合わせて作成することはしばしば価値があります [〜# 〜] dto [〜#〜] 着信データ用。次に、DTOを使用して、API操作を実行するためのビジネスオブジェクト(またはエラーの試行)を作成します。

JSONを単なる送信フォーマットとして見る場合、設定されていないプロパティを省略する方が理にかなっています。回線を介して送信する方が少ないです。バックエンド言語がデフォルトでnullを使用しない場合、おそらくエラーを出すようにデシリアライザーを構成できます。たとえば、Newtonsoft.Jsonの一般的な設定では、null /欠落しているプロパティをF#option型との間でのみ変換し、それ以外の場合はエラーになります。これにより、オプションのフィールド(optionタイプのフィールド)が自然に表現されます。

いつものように、一般化は今のところあなたを得るだけです。デフォルトまたはnullプロパティの方が適している場合があります。ただし、重要なのは、システムのエッジにあるデータ構造をビジネスオブジェクトとして見ることではありません。ビジネスオブジェクトは、正常に作成されたときにビジネス保証(少なくとも3文字の名前など)を備えている必要があります。しかし、ネットワークから引き出されたデータ構造には、実際の保証はありません。

21
Kasey Speakman

pdate:混乱を招く可能性があるため、回答を少し編集しました。


空の文字列を使用することは、決定的なnoです。空の文字列はまだ値ですが、空です。何も表さない構造体nullを使用して値を示すことはできません。

API開発者の観点から見ると、プロパティは2種類しか存在しません。

  • 必須(これらは特定のタイプの値を持たなければならず、空であってはならない)、
  • オプション(これらは特定のタイプの値を含む場合がありますが、nullを含めることもできます(MAY)。

これにより、プロパティが必須の場合、つまり必須ですが、nullにすることはできません。

一方、オブジェクトのオプションのプロパティを設定せずに空のままにしておく場合は、いずれにしてもnullの値を使用して応答に含めておきます。私の経験から、APIクライアントは、プロパティが実際に存在するかどうかを確認する必要がないため、APIクライアントが解析を実装しやすくなっています。これは、プロパティが常に存在し、nullを処理するだけで応答をカスタムDTOに変換できるためです。オプションとしての値。

クライアントの追加条件を含む応答力からフィールドを動的に含めたり削除したりします。


いずれにせよ、どちらの方法を選択する場合でも、必ずconsistentを維持し、十分に文書化してください。この方法では、動作が予測可能である限り、APIに何を使用しても問題はありません。

15
Andy

null使用法はアプリケーション/言語に依存します

最終的に、nullを有効なアプリケーション値として使用するかどうかの選択は、主にアプリケーションとプログラミング言語/インターフェース/ Edgeによって決まります。

基本的なレベルでは、異なるクラスの値がある場合は、異なるタイプを使用することをお勧めします。 nullは、インターフェイスで許可されていて、表現しようとしているプロパティのクラスが2つしかない場合は、オプションになることがあります。インターフェースやフォーマットで許可されている場合は、プロパティを省略してもかまいません。新しい集約タイプ(クラス、オブジェクト、メッセージタイプ)は、別のオプションになる場合があります。

文字列の例として、これがプログラミング言語の場合は、いくつか質問します。

  1. 将来のタイプの値を追加する予定はありますか?もしそうなら、Optionはおそらくあなたのインターフェース設計にとってより良いでしょう。
  2. いつコンシューマーコールを検証する必要がありますか?静的に?動的に?前?あと?全然?プログラミング言語がサポートしている場合は、検証のために作成する必要のあるコードの量を回避できるため、静的型付けの利点を活用してください。 Optionは、文字列がnullにできない場合にこのケースを最もよく満たします。ただし、とにかくユーザー入力でnull文字列値を確認する必要があるので、おそらく質問の最初の行に戻ります。表現します。
  3. nullは私のプログラミング言語のプログラマエラーを示していますか?残念ながら、一部の言語では、nullが初期化されていない(または明示的に初期化されていない)ポインターまたは参照のデフォルト値であることがよくあります。 nullはデフォルト値として受け入れられる値ですか?デフォルト値としてsafeですか? nullは、割り当て解除された値を示す場合があります。インターフェイスのコンシューマーに、プログラムでのこれらの潜在的なメモリ管理または初期化の問題の兆候を提供する必要がありますか?そのような問題に直面した場合のそのような呼び出しの失敗モードは何ですか?呼び出し元は私のものと同じプロセスまたはスレッドにあるので、そのようなエラーは私のアプリケーションにとって高いリスクになりますか?

これらの質問に対する回答によっては、nullがインターフェースに適しているかどうかを判断することができるでしょう。

例1

  1. アプリケーションは安全が重要です
  2. 起動時に何らかのタイプのヒープ初期化を使用していて、nullは文字列にスペースを割り当てるのに失敗したときに返される可能性のある文字列値です。
  3. そのような文字列がインターフェイスにヒットする可能性があります

回答:nullはおそらく適切ではありません

根拠:この場合のnullは、実際には2つの異なるタイプの値を示すために使用されます。最初の値は、インターフェースのユーザーが設定したいデフォルト値である場合があります。残念ながら、2番目の値は、システムが正しく機能していないことを示すフラグです。そのような場合は、おそらく(システムにとってそれが何であれ)可能な限り安全に失敗したいと思うでしょう。

例2

  1. char *メンバーを持つC構造体を使用しています。
  2. システムはヒープ割り当てを使用せず、MISRAチェックを使用しています。
  3. インターフェイスはこの構造体をポインタとして受け入れ、構造体がNULLを指していないことを確認します
  4. APIのchar *メンバーのデフォルトで安全な値は、NULLの単一の値で示すことができます
  5. ユーザーの構造体の初期化時に、char *メンバーを明示的に初期化しない可能性をユーザーに提供する必要があります。

回答:NULLが適切な場合があります

理論的根拠:構造体がNULLチェックに合格する可能性はほとんどありませんが、初期化されていません。ただし、構造体の値に関する何らかのチェックサムや構造体のアドレスの範囲チェックがない限り、APIはこれを考慮できない場合があります。 MISRA-Cリンターは、初期化前に構造体の使用にフラグを立てることにより、APIのユーザーを支援する場合があります。ただし、char *メンバーの場合、structへのポインターが初期化されたstructを指す場合、NULLはstruct初期化子で指定されていないメンバーのデフォルト値です。したがって、NULLは、アプリケーションのchar *構造体メンバーの安全なデフォルト値として機能する場合があります。

それがシリアル化インターフェイス上にある場合は、文字列でnullを使用するかどうかについて、次の質問をします。

  1. nullは潜在的なクライアント側エラーを示していますか? JavaScriptでのJSONの場合、nullは必ずしも割り当て失敗の指標として使用されるわけではないため、これはおそらく「いいえ」です。 JavaScriptでは、問題として設定される参照からのオブジェクトの不在を明示的に示すために使用されます。ただし、JSON nullをネイティブのnull型にマップするJavaScript以外のパーサーとシリアライザーがあります。これが事実である場合、ネイティブnullの使用が特定の言語、パーサー、シリアライザーの組み合わせで問題ないかどうかの議論が始まります。
  2. プロパティ値の明示的な欠如は、単一のプロパティ値以上に影響しますか?時々nullは、実際には新しいメッセージタイプが完全に存在することを示しています。完全に異なるメッセージタイプを指定するだけで、シリアライゼーションフォーマットのコンシューマにとっては、よりクリーンになる場合があります。これにより、Webインターフェースが提供する2つのメッセージの違いを、検証とアプリケーションロジックで明確に分離できます。

一般的なアドバイス

nullは、それをサポートしないエッジまたはインターフェイスの値にはできません。プロパティの値の入力に非常に緩いもの(JSONなど)を使用している場合は、コンシューマEdgeソフトウェアで何らかの形式のスキーマまたは検証をプッシュしてみてください(例 JSON Schema )ifあなたはできる。それがプログラミング言語APIの場合は、可能な場合は(入力を介して)静的に、または実行時に感知できる程度に大声で(つまり、消費者向けのインターフェースでは 防御的プログラミング )、ユーザー入力を検証します。同様に重要なこととして、Edgeを文書化または定義して、次のことについて疑問がないようにします。

  • 特定のプロパティが受け入れる値のタイプ
  • 特定のプロパティに有効な値の範囲。
  • 集計タイプをどのように構成するか。集計タイプにはどのプロパティを含める必要がありますか?
  • あるタイプのコンテナーである場合、コンテナーが保持できる、または保持する必要があるアイテムの数、およびコンテナーが保持する値のタイプは何ですか?
  • コンテナーまたは集計タイプのプロパティまたはインスタンスが返される場合、どのような順序で返されますか?
  • 特定の値を設定するとどのような副作用があり、それらの値を読み取るとどのような副作用がありますか?
3
backscattered

文字列が存在し、たまたま空の文字列である状況では、空の文字列を指定します。 「いいえ、このデータはありません」と明示的に伝えたい状況ではnullを指定します。そして、「そこにデータがない、気にしないでください」と言うキーを省略します。

あなたはこれらの状況のどれが起こり得るかを判断します。アプリケーションが空の文字列を持つことは理にかなっていますか? nullを使用して明示的に「データなし」と言うことと、値がないことによって暗黙的に区別することを区別しますか?クライアントが両方を区別する必要がある場合は、両方の可能性(nullとキーなし)のみが必要です。

これがすべてデータの送信に関することであることに注意してください。受信者がデータを処理するのは彼らのビジネスであり、彼らは彼らにとって最も便利なことをします。レシーバーすべきクラッシュすることなく、(おそらくデータを拒否することによって)そこに投げたあらゆるものを処理できます。

他に考慮事項がない場合は、送信者にとって最も便利なものを送信しますdocumentそれを送信します。 JSONのエンコード、送信、解析の速度が向上する可能性があるため、存在しない値を送信しないことをお勧めします。

1
gnasher729

ここでこれらの質問の私の個人的な分析。本、紙、研究など何でもないので、私の個人的な経験だけです。

空の文字列はnullとして

これは私にとっては立ち入り禁止です。空の文字列のセマンティクスと未定義のセマンティクスを混在させないでください。多くの場合、それらは完全に交換可能ですが、未定義と定義済みだが空であるという意味が異なる場合があります。

一種の愚かな例:外部キーを格納する属性があり、その属性が定義されていないかnullであるとします。これは、関係が定義されていないことを意味しますが、空の文字列""は定義された関係として理解でき、外部レコードのIDはその空の文字列です。

未定義vs null

これは白黒のトピックではありません。どちらのアプローチにも長所と短所があります。

null値を明示的に定義することを支持して、これらの長所があります:

  • メッセージは、メッセージを見ただけですべてのキーを知ることができるので、より自己記述的です
  • 前のポイントに関連して、データのコンシューマーでエラーをコーディングして検出する方が簡単です。間違ったキーをフェッチした場合(スペルの間違い、APIの変更など)、エラーを検出する方が簡単です。

存在しないキーがnullのセマンティクスに等しいと仮定すると、次のようになります。

  • いくつかの変更は収容しやすいです。たとえば、メッセージスキーマの新しいバージョンに新しいキーが含まれている場合は、メッセージのプロデューサが更新されておらず、この情報をまだ提供していない場合でも、情報のコンシューマをコーディングして、この将来のキーを処理できます。
  • メッセージの冗長度を下げるか短くすることができます

APIがなんらか安定していて、完全に文書化している場合は、存在しないキーがnullの意味と等しいことを述べてもまったく問題ないと思います。しかし、それがより乱雑で無秩序である場合(かなり頻繁にそうであるように)、すべてのメッセージのすべての値を明示的に定義すれば、頭痛の種を回避できると思います。つまり疑わしい場合は、詳細なアプローチを採用する傾向があります。

とはいえ、何よりも重要なことは、意図を明確に述べ、一貫性を保つことです。ここで1つのことを行って、もう1つのことを行わないでください。予測可能なソフトウェアは、より優れたソフトウェアです。

1
bgusach

資料!

TL; DR>

必要に応じて行います-場合によっては、それが使用されるコンテキストが重要になります。例:Oracle SQLへの変数のバインド:空の文字列はNULLとして解釈されます。

単に私は言うでしょう-言及された各シナリオを文書化することを確認してください

  • ヌル
  • ブランク(空)
  • 欠落(削除)

コードはさまざまな方法で動作する可能性があります-コードがそれにどのように反応するかを文書化します。

  • 失敗(例外など)、おそらく検証(おそらくチェックされた例外)に失敗するだけでなく、状況を正しく処理できない(NullPointerException)。
  • 適切なデフォルトを提供する
  • コードの動作が異なる

次に、それに加えて-一貫して行動し、おそらく独自のベストプラクティスを採用するかどうかは、あなた次第です。その一貫した動作を文書化します。例:

  • ヌルを扱い、同じを逃す
  • 空の文字列はそのとおりに扱います。 SQLバインドの場合のみ、空白と見なされる場合があります。 SQLが一貫して期待どおりに動作することを確認してください。
0
YoYo

tl; dr-使用する場合:意味が一貫している。

nullを含めた場合、それはどういう意味ですか?それが何を意味するのかという事柄の宇宙があります。 1つの値では、欠けている値や不明な値を表すのに十分ではありません(これらは無数の可能性の2つにすぎません。例:欠落-測定されましたが、まだわかりません。不明-測定しようとしませんでした。それ。)

私が最近出くわした例では、誰かのプライバシーを保護するために報告されなかったため、フィールドが空である可能性がありますが、フィールドは送信者側で知られている、送信者側では知られていませんが、元のレポーターには知られているか、両方には不明です。そして、これらすべてがレシーバにとって重要でした。したがって、通常は1つの値では不十分です。

オープンワールドの前提条件(記述されていないことについて単に知らない)の場合、それを除外するだけで、何でもかまいません。閉じた世界の仮定(たとえば、SQLで述べられていないことが誤っている)では、nullが何を意味するかを明確にし、できるだけその定義と一致するようにします...

0
Grimaldi

bestとは言えませんが、ほぼ間違いなく単純な実装の詳細ではありませんですが、その変数と対話する方法の構造が変わります。

何かがある場合nullにすることができます常にそれを扱っている必要がありますある時点でnullになりますしたがって、常に2つのワークフロー、1つになりますnullの場合、有効な文字列の場合。スプリットワークフローは、悪い処理とは限りませんが、利用できる可能性のあるエラー処理や特殊なケースでの使用はありますが、コードを難読化します。

常に同じ方法で文字列と対話するの場合、機能が頭に留まるのほうがおそらく簡単です。

したがって、「何が最善か」という質問と同様に、答えはそれは場合によって異なりますです。ワークフローを分割し、何かが設定されていないときにより明示的にキャプチャする場合は、nullを使用します。プログラムがそのまま実行し続けたい場合は、空の文字列を使用します。重要なことは、あなたがconsistentであるということです。共通のリターンを選び、それを守ってください。

APIを作成していることを考慮して、空の文字列に固執するをお勧めします。これは、ユーザーが補正する必要が少ないためです。APIのユーザーとして、すべての理由を知ることはできませんあなたのAPIは私にnull値を与える可能性がありますあなたが非常によく文書化されていない限り、何人かのユーザーはとにかく読まないでしょう。

0
Erdrik Ironrose