API要求/応答で空の文字列、nullを使用するか、空のプロパティを削除します


25

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

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

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

nullの使用を避ける簡単な方法として、空の文字列を使用するというアイデアが好きです。空の文字列に対してnullを使用することについて聞いた1つの議論は、空の文字列はプロパティが存在することを意味するということです。私は違いを理解していますが、それが単なる実装の詳細なのか、nullまたは空の文字列を使用して実際の違いが生じるのかどうかも疑問に思います。また、空の文字列は空の配列に似ているのだろうかと思います。

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


2
また、Oracleは空の文字列とNULL文字列を同じ方法で処理することに注意してください。そして、まさにそこにあります:紙のアンケートで、答えが与えられなかった場合と空の文字列から成る答えをどうやって区別できますか?
ベルンハルトヒラー

継承を使用する場合、言うのは簡単if( value === null) { use parent value; } ですが、子の値を空の文字列に設定した場合(たとえば、デフォルトの親の値を空白で上書きした場合)、値をどのように「再継承」しますか?私にとって、nullに設定することは、「この値を設定解除して、親値を使用することを知っている」ことを意味します。
フランクフォルテ

「空のプロパティを削除する」こともaを「回避」する理由なのでnull(そのnullように回避されるのは本当です)、質問者は「null以外を返す」[オブジェクト](つまり:空の文字列、空の配列など)を意味します「避ける」と書く。
チェレポ

回答:


18

TLDR; nullプロパティを削除

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

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

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


3
ほとんどの最新のシリアライザーにはオプションのフィールドがありますが、応答からnullを省略することは常に良い考えと限りません。これは、null許容フィールドを処理するためにさらに複雑になる可能性があるためです。したがって、シリアライゼーションライブラリがnullableを処理する方法と、それらのnullableを処理する(潜在的な)追加の複雑さが本当にリクエストごとに数バイトを節約する価値があるかどうかに応じて、それは本当にケース依存です。ビジネスケースを分析するために一生懸命働く必要があります。
クリスクレフィス

@ChrisCireficeはい、最後の段落がそれをカバーしていると思います。異なる戦略を採用する方が良い場合があります。
ケイシースピークマン

JSONは送信形式としてのみ使用され、CORBAのようなワイヤを介してオブジェクトを渡すのではないことに同意します。また、プロパティを追加および削除できることに同意します。特にWeb上では、表現が変化し、コントロールが変化する可能性があります。
imel96

15

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


空の文字列で行くことは決定的なノーです。空の文字列は依然として値であり、空です。何も表さない構造を使用して値を示すことはできませんnull

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

  • 必須(これらは特定のタイプの値を持たなければならず、空になってはいけません)
  • オプション(これらは特定のタイプの値を含むことができますが、を含むこともできますnull

これにより、プロパティが必須の場合、つまり 必須ですnull

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

クライアントの追加条件を含む、応答フォースからのフィールドの動的な包含/除去。


あなたが選択したいずれかの方法のいずれかの方法は、あなたがそれを維持することを確認し一貫して十分に文書化。その方法は、本当にあなたがいる限り行動は予測可能であるとして、あなたのAPIで使用するかは重要ではありません。


はい、空の文字列は値でありnull、参照に使用します。参照と値を混在させることは、私の懸念の1つです。値nullを持つオプションの文字列フィールドとオプションのフィールドをどのように区別しnullますか?再解析すると、プロパティの存在をテストしないとパーサーが脆弱になりませんか?
imel96

2
@ imel96非オプションのフィールドは決してnullにできません。何かがオプションではない場合、それは常にその特定のタイプの値を含まなければなりません。
アンディ

3
この。APIを頻繁に使用するため、省略されたオプションフィールドの形式であっても、「動的な」構造が返ってきた場合に対処しなければならないのは嫌です。(ZLSとNullには大きな違いがあることにも大いに同意しました)。終日ヌル値を喜んで受け入れます。APIの作成者としての私の目標の1つは、クライアントの消費を可能な限り簡単にすることです。つまり、常に期待されるデータ構造を持つことです。
-jleach

@DavidPackerですので、正しく理解できればnull、値がオプションであることを示すために使用します。そのため、オプションではない文字列プロパティを持つオブジェクトを定義し、コンシューマがこのプロパティを持っていない場合、そのプロパティに空の文字列を送信する必要があります。そうですか?
imel96

2
@GregoryNisbetしないでください、お願いします。それは意味がありません。
アンディ

3

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

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

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

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

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

これらの質問への回答に応じて、おそらくnullインターフェイスに適しているかどうかに注目することができます。

例1

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

回答:nullおそらく適切ではない

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

例2

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

回答:NULL適切かもしれません

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

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

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

一般的なアドバイス

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

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

1

ここで、これらの質問の私の個人的な分析。本、論文、研究など何にも裏付けられていません。私の個人的な経験です。

空の文字列として null

これは私には行きません。空の文字列のセマンティクスと未定義のセマンティクスを混在させないでください。多くの場合、それらは完全に互換性がありますが、定義されていないものと定義されているが空の場合は何か異なることを意味する場合があります。

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

未定義vs null

これは、黒または白のトピックではありません。両方のアプローチには長所と短所があります。

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

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

存在しないキーが次のセマンティクスに等しいと仮定することを支持しますnull

  • 一部の変更は簡単に対応できます。たとえば、メッセージスキーマの新しいバージョンに新しいキーが含まれている場合、メッセージのプロデューサーが更新されておらず、この情報をまだ提供していない場合でも、この将来のキーで機能するように情報のコンシューマーをコーディングできます。
  • メッセージはより冗長または短くすることができます

APIが何らかの形で安定しており、完全に文書化されている場合、存在しないキーがの意味と等しいと述べることはまったく問題ないと思いますnull。しかし、(かなり頻繁にそうであるように)より乱雑で混oticとした場合、すべてのメッセージのすべての値を明示的に定義すれば、頭痛を避けることができると思います。すなわち、疑わしい場合、私は冗長アプローチに従う傾向があります。

もっとも重要なことは、意図を明確に述べ、一貫性を保つことです。ここで1つのことをしてはいけません。予測可能なソフトウェアは優れたソフトウェアです。


空の文字列を使用する例は、実装の詳細、つまりAPIを使用してデータベース行を公開することを意味します。データベースが含まれておらず、オブジェクト表現を転送するだけの場合、違いはありますか?
imel96

実装の詳細である必要はありません。私の例では、実際にはDB関連のPKについて説明していますが、説明しようとしたのは、空の文字列がnil / nothing /ではないということnullです。別の例:ゲームにはキャラクターオブジェクトがあり、「パートナー」属性があります。nullパートナーは明らかに全く相手が存在しないことを意味するが、""その名前であるパートナーがあるとして理解することができます""
-bgusach

nullのパートナー参照で問題ありません。パートナーが存在せず、参照も文字列ではありません。しかし、パートナー名は文字列です。パートナー名としてnullを許可していても、ある時点でそのnullをキャッチして空の文字列に置き換えませんか?
imel96

パートナーがいない場合null、空の文字列を変更しません。おそらくフォームでレンダリングしますが、データモデルではレンダリングしません。
-bgusach

私はパートナーがいないことを意味しませんでした、パートナーはオブジェクトになるでしょう。それはパートナーのname私が話していたことを、あなたは相手の名前がnullにすることが可能でしょうか?
imel96

1

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

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

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

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


「クライアントによって区別される必要がある場合」のあなたのポイントが好きです。
imel96

0

最善のことは言うことができませんが、ほとんど確実に単純な実装の詳細ではありませんが、その変数とやり取りする方法の構造が変わります。

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

常に同じ方法文字列を操作する場合は、おそらく機能がとどまりやすいでしょう。

それで、「何が最高か」という質問と同様に、答えは残されています。ワークフローを分割し、何かが設定されていないときにより明示的にキャプチャする場合は、nullを使用します。むしろ、プログラムがそのまま実行する場合は、空の文字列を使用します。重要なことは、あなたが一貫していること、共通のリターンを選択し、それに固執することです。

APIを作成していることを考慮すると、API のユーザーとして、あなたのAPIがnull値を与える可能性のあるすべての理由がわからないので、ユーザーが補償する必要がないため、空の文字列に固執することをお勧めします非常によく文書化されており、一部のユーザーはとにかく読みません。


「ワークフローを分割する」ことは悪いことです。プロデューサー側ですべてがきれいだとしましょう。文字列型のメソッドはstrings のみを返し、nullは返しません。APIがnullを使用する場合、ある時点で、プロデューサーはこれnullを作成してAPIに準拠する必要があります。次に、消費者nullも処理する必要があります。しかし、私はあなたが言っていることを得たと思います、ちょうど権限でAPIを決定して定義しますか?それは、それらのどれにも問題がないという意味ですか?
imel96

はい、APIで行うことは、ユーザーがコードを構造化する方法に影響を与えるため、APIのユーザーの観点から設計を考慮すると、どの方法が最適であるかを定義できるはずです。最終的にはあなたのAPIです。ただ一貫してください。あなただけがアプローチの長所と短所を決めることができます。
エルドリックアイアンローズ

0

資料!

TL; DR>

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

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

  • ヌル
  • 空白(空)
  • 欠落(削除)

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

  • 失敗(例外など)、検証に失敗する可能性もあり(チェック例外の可能性もあります)、状況を正しく処理できません(NullPointerException)。
  • 適切なデフォルトを提供する
  • コードの動作が異なる

それに加えて、一貫して動作し、場合によっては独自のベストプラクティスを採用するかどうかはユーザー次第です。その一貫した動作を文書化します。例:

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

問題は、懸念に対処せずに、非常に頻繁に意見の相違が発生したことです。チーム環境では、多くの場合、議論があることを意味する決定はチームの決定でなければなりません。複数のチームがある場合、すべてのチームには独自の決定権があります。私は、互いに同意しない異なるチームによって実装されていると推測できるAPIを見てきました。誰かが1つのことに同意できる場合、それを文書化するのは簡単です。
imel96

0

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

を含めた場合null、どういう意味ですか?意味のあるものの宇宙があります。1つの値は、欠損値または未知の値を表すのに十分ではありません(これらは無数の可能性のうちの2つにすぎません。たとえば、欠損-測定されましたが、まだわかりません。不明-測定しようとしませんでした。それ。)

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

開かれた世界の仮定(あなたが単に述べられていないことを知らない)で、あなたはそれをただ残すだろうし、それは何でもあり得る。閉じた世界の仮定(SQLのように、記述されていないことは間違っています)を使用nullすると、意味を明確にし、その定義とできる限り一貫性を持たせることができます...

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.