JSONストリーミングは、下位レベルのストリーム指向プロトコル( TCPなど)に基づいて構築されたJSONオブジェクトを区切る通信プロトコルで構成されており、サーバーとクライアントが同じJSONオブジェクトを使用する場合(例えば、暗黙的にコード化されている場合)、個々のJSONオブジェクトが認識されることを保証します。これは、JSONが非連結プロトコルであるため(2つのJSONオブジェクトを連結しても有効なJSONオブジェクトは生成されないため)、必要となります。
JSONは、システム間でオブジェクトデータを交換するための一般的なフォーマットです。株価ティッカーやアプリケーションログレコードなど、単一の接続を介してオブジェクトのストリームを送信する必要がある場合がよくあります。[ 1 ]このような場合、1つのJSONエンコードされたオブジェクトがどこで終わり、次のオブジェクトがどこから始まるかを識別する必要があります。技術的には、これはフレーミングとして知られています。
これを実現するには、一般的に4つの方法があります。
行区切りJSONは、従来の行指向ツールと非常に相性が良いです。
連結JSONは整形済みJSONと互換性がありますが、解析にはより多くの労力と複雑さを要します。従来の行指向ツールとの相性は良くありません。連結JSONストリーミングは、行区切りJSONストリーミングの上位互換です。
長さプレフィックス付きJSONは、整形済みJSONと互換性があります。従来の行指向ツールとは相性が良くありませんが、行区切りまたは連結ストリーミングよりもパフォーマンス上の利点がある場合があります。また、解析も容易になることがあります。
行区切りJSONの同等の形式を表す2つの用語は次のとおりです。
ストリーミングでは、JSON形式ではプリミティブ値内に改行文字(リターンとニューライン)を含めることができないこと(文字列ではそれぞれエスケープ処理が必要)と、ほとんどのJSONフォーマッタがデフォルトで改行文字を含む空白文字を含めないことを利用しています。これらの機能により、改行文字、または改行文字とリターン文字のシーケンスを区切り文字として使用できます。\r\n
この例では、2つのJSONオブジェクトを示しています(各行末尾の暗黙的な改行文字は表示されていません)。
{ "some" : "thing\n" } { "may" :{ "include" : "nested" , "objects" :[ "and" , "arrays" ]}}改行を区切り文字として使用することで、このフォーマットは従来の行指向のUnixツールと非常にうまく連携します。
例えば、ログファイルは次のようになります。
{ "ts" : "2020-06-18T10:44:12" , "started" :{ "pid" : 45678 }}{ "ts" : "2020-06-18T10:44:13" , "logged_in" :{ "username" : "foo" }, "connection" :{ "addr" : "1.2.3.4" , "port" : 5678 }}{ "ts" : "2020-06-18T10:44:15" , "registered" :{ "username" : "bar" , "email" : "bar@example.com" }, "connection" :{ "addr" : "2.3.4.5" , "port" : 6789 }}{ "ts" : "2020-06-18T10:44:16" , "logged_out" :{ "username" : "foo" }, "connection" :{ "addr" : "1.2.3.4" , "port" : 5678 }}日付順に並べ替えたり、ユーザー名、アクション、IPアドレスなどをgrepで検索したりするのが非常に重要です。
行区切りJSONは、連結JSONを処理できるパーサーで読み取ることができます。JSONオブジェクト内に改行を含む連結JSONは、行区切りJSONパーサーでは読み取ることができません。
「行区切りJSON」と「改行区切りJSON」という用語は、埋め込み改行がサポートされているかどうかを明確にせずに使われることが多い。
以前の改行区切りJSON仕様[ 9 ]では、特定の行の最初の2文字が「//」であればコメントを埋め込むことができました。しかし、コメントが含まれている場合、標準のJSONパーサーでは使用できませんでした。現在の仕様バージョン(「NDJSON - 改行区切りJSON」)[ 10 ]では、コメントは含まれなくなりました。
連結されたJSONは、 jqなどの適切なJSONユーティリティを使用して、行区切りのJSONに変換できます。例:
jq --compact-output . < concatenated.json > lines.json レコード区切り文字で区切られたJSONストリーミングでは、JSONフォーマッタが空白文字を除外する必要なく、JSONテキストシーケンスを区切ることができます。JSONテキストシーケンスには制御文字を含めることができないため、レコード区切り文字を使用してシーケンスを区切ることができます。さらに、自己区切り文字を持たないトップレベルのJSONオブジェクト(数値、true、false、nullなど)を適切に処理できるように、各JSONテキストシーケンスの後に改行文字を付けることをお勧めします。
この形式はJSONテキストシーケンスまたはMIMEタイプ とも呼ばれapplication/json-seq、IETF RFC 7464で正式に記述されています。
以下の例は、レコード区切り文字を ␞、改行文字を ␊ で表した 2 つの JSON オブジェクトを示しています。
␞ { "some" : "thing\n" } ␊ ␞ { "may" : { "include" : "nested" , "objects" : [ "and" , "arrays" ] } } ␊連結型JSONストリーミングでは、送信側は区切り文字なしで各JSONオブジェクトをストリームに書き込むだけで済みます。受信側は、終端文字を解析して各JSONオブジェクトを認識し、出力できるパーサーを使用する必要があります。連結型JSONは新しいフォーマットではなく、区切り文字なしで複数のJSONオブジェクトをストリーミングするための単なる名称です。
この形式の利点は、埋め込み改行文字でフォーマットされたJSONオブジェクト(例えば、人間が読みやすいように整形されたオブジェクト)を処理できることです。例えば、次の2つの入力はどちらも有効で、同じ出力を生成します。
{ "some" : "thing\n" }{ "may" :{ "include" : "nested" , "objects" :[ "and" , "arrays" ]}}{ "some" : "thing\n" } { "may" : { "include" : "nested" , "objects" : [ "and" , "arrays" ] } }行ベースの入力に依存する実装では、パーサーがオブジェクトを適切なタイミングで出力するためには、各JSONオブジェクトの後に改行文字が必要になる場合があります。(そうしないと、行が入力バッファに残ったままパーサーに渡されない可能性があります。)JSONオブジェクトを改行文字で終了させることは非常に一般的であるため、これは問題として認識されることはほとんどありません。
長さプレフィックス付きまたはフレーム付きJSONストリーミングでは、送信側が各メッセージの長さを明示的に指定できます。受信側は、各長さnを認識し、それに続くnバイトを読み取ってJSONとして解析できるパーサーを使用する必要があります。
この形式の利点は、各メッセージの正確な長さが明示的に指定されているため、パーサーが区切り文字を探す必要がなくなり、解析速度が向上することです。長さプレフィックス付きJSONは、単一の「メッセージ」を任意のチャンクに分割できるTCPアプリケーションにも適しています。プレフィックス付きの長さによって、JSON文字列の解析を試みる前に、パーサーが想定するバイト数を正確に把握できるからです。
この例では、長さがプレフィックスとして付加された 2 つの JSON オブジェクトを示します (それぞれの長さは、次の JSON 文字列のバイト長です)。
18 { "some" : "thing\n" } 55 { "may" :{ "include" : "nested" , "objects" :[ "and" , "arrays" ]}}