CSVの国際標準 RFC 4180 と JSONの国際標準 RFC 8259 をいまさら読みなおしてみた(後編)

こんにちは。システム開発グループの小田切と申します。

 前回、CSVについてつらつら書いていて、気づいたら長くなっていました。JSON RFC 8259の方が長いので、今回はうまく要点だけ伝えられるように頑張ります!!

 CSVについては、公開後同じような苦労をした知り合いからコメントをいくつか頂きました。レスポンスありがとうございます。先にお礼を申し上げます。

 JSONは、JavaScript Object Notationの略です。

JSONの国際標準 RFC 8259 (CSVと同期してRFCについて書きます。)

  JSONの最初のRFC公開は、2006年、RFC 4627でした。前回のCSVのRFC 4180は、2005年10月でしたから、あまり時間的な隔たりはありませんね。JSONは、JavaScriptと関連が強くて、JavaScriptの標準化を進めているECMA*1。その後仕様策定は、難航したようですが、2017年12月に、ECMAとの統一仕様がまとまり、事実上最後のJSON仕様 RFC 8259となりました。ECMAは、ECMA-404 2nd Edition としています*2

 RFCでの変遷を見てみると

 RFC 4627(2006.06) ⇒ RFC 7158(2013.03) ⇒ RFC 7159(2014.03) ⇒ RFC 8259(2017.12)

となっています。以前、半導体産業に関わっていた時に、各社が集まり仕様を決める会議に参加させて頂いたことがありますが、共通の仕様を決めるのは大変だと思いました。仕様の変遷の比較も面白いかもしれませんが、今回は歴史的な話ではなく、ほぼ確定したRFC 8259について書きたいと思います。

RFC 8259の内容とは

 今回も、気になったところだけ抜粋して、気付いた事を書きたいと思います。前回と同じく、

原文の和訳/意訳の箇所はこのように枠で囲みます。私の言葉で書いたので誤訳していたらごめんなさい。うまく訳せないところは[  ]で囲み原文の英語をイタリック書式で入れています。

トップページ

 RFC 4627はDouglas Crockford氏が、RFC 7158からRFC 8259までの3本は Tim Bray 氏が書いています。Tim Bray氏の7158, 7159での所属は、Google Inc.  RFC 8259記述時の所属は、Textuality Service, Inc.*3になっていました。トップページの上には、RFC 8259という番号の他に、RFC 7159は廃止[Obsoletes]という記述もあります。

 まずは概要の一部から。

このドキュメントは、他のJSONの仕様との矛盾を解消し、仕様の誤りを直し、経験に基づく相互運用性のガイダンスを提供する。

 調整の苦労が滲んできますね。経験に基づく相互運用性のガイダンスは、私にとって役立つことがありました。その点も注意してご覧になると良いと思います。

 次は、このメモの位置付けについてです。

このドキュメントは、IETF(Internet Engineering Task Force)の成果物である。IETFコミュニティの合意事項を表す。本ドキュメントは公開レビューを受け、IESG(Internet Engineering Steering Group)により公開が承認されている。

 前回のCSVの国際標準RFC 4180では、「インターネットの標準仕様を何ら定めるものではない。」でしたから、結構ニュアンスが異なりますね。また、「このメモの配布に制限はない。」ではなく、「公開が承認されている。」と表現が変わって時代を感じます*4

著作権表示についての記述は、そのまま転記します。

Copyright Notice
   Copyright (C) The Internet Society (2005).
This document is subject to BCP 78 and the IETF Trust’s Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/license-info) in effect on the date of publication of this document. Please review these documents carefully, as they describe your rights and restrictions with respect to this document. Code Components extracted from this document must include Simplified BSD License text as described in Section 4.e of the Trust Legal Provisions and are provided without warranty as described in the Simplified BSD License.
This document may contain material from IETF Documents or IETF Contributions published or made publicly available before November 10, 2008. The person(s) controlling the copyright in some of this material may not have granted the IETF Trust the right to allow modifications of such material outside the IETF Standards Process. Without obtaining an adequate license from the person(s) controlling the copyright in such materials, this document may not be modified outside the IETF Standards Process, and derivative works of it may not be created outside the IETF Standards Process, except to format it for publication as an RFC or to translate it into languages other than English.

 CSVの時とは大違いで長文です。法律に疎い私は、一応、社内の法務の部署に確認してみました。「全文を和訳するなどはともかく、出典を明示し、一部の和訳+それへの自分のコメントを記載しているのであれば日本法上は問題になるものでないと思います。」とコメントを頂きました*5。記述を続けられます。

1. Introduction

 ここでは、RFC 4627の発行から年月が経過しJSONは広く使われだした一方で、相互の運用性の問題が起きたこと、他のJSON標準との整合性、記載ミスを解決するために、このRFC 8259を策定したと書いてあります。更に、

 JSONは、4つのプリミティブな型(文字列、数値、ブール値、null)と、2つの構造体型(オブジェクトと配列)を記すことができる。
 文字列は、0文字以上のUnicode文字がつらなったものである。
 オブジェクトは、0個以上の名称/値のペアの順序性のない集まりである。ここで名称とは文字列、値とは文字列、数値、ブール値、null、オブジェクト、配列である。配列は、0個以上の値の順序性のあるひと続きになったものである。

CSVではその場限りで決めていた ブール値とnullが定義と、(私は)表現できなかった2つの構造体型(オブジェクトと配列)が入っています。次に、助動詞について言及しています。

1.1 このドキュメント内での規定
 このドキュメント中の「(MUST)しなければならない」、「(MUST NOT)してはならない」、「(REQUIRED)必須である」、「(SHALL)することになる」、「(SHALL NOT)することはない」、「(SHOULD)すべきである」、「(SHOULD NOT)すべきではない」、「(RECOMMENDED)推奨される」、「(NOT RECOMMENDED)推奨されない」、「(MAY)してもよい」、「(OPTIONAL)選択できる」というキーワードは、文中すべて大文字で表示されている場合にのみBCP 14(((Best Current Practiceの略です。)))[RFC2119] [RFC8174]が適用されると解釈すること。

 助動詞の解釈方法まで定義されています。とはいえ、CVSで解釈を間違えかけた may not が出てきません。でも、検索すると、RFC 8259の中に小文字の may not が使われています。「解釈すること」の部分も大文字の助動詞使っていないのでどう意訳するか迷いました。

  次の1.2 Specifications of JSON (JSONの仕様)では、RFCの他に、ECMA-404でも規定していることを記載しています。将来の変更は、双方を同期して修正することが書いてある程度なので、省略します。

 1.3 Introduction to This Revision は、この版を作成した理由の概略(他仕様との不整合、記載誤り、相互運用性の確保)を述べている程度なので、省略します。

 次は、JSONの文法について記載しています。

2. JSON Grammer

 JSONは、トークンが連なったものである。トークンのセットは、6種の構造を決める文字、文字列、数値、と3種類のリテラル名がある。
 JSONテキストは、直列化可能な値である。以前の仕様では、JSONテキストはオブジェクトまたは配列に制限されていた。JSONテキストが必要な個所でオブジェクトまたは配列を生成する実装もJSONテキストとしてなり、相互で運用可能となる。
JSON-text = ws value ws 
以下が6種類の構造を決める文字である。
  • begin-array = ws %x5B ws ;  [  配列開始文字 左角括弧
  • begin-object = ws %x7B ws ;  {  オブジェクト開始文字 左波括弧
  • end-array = ws %x5D ws ;  ]  配列終了文字 右角括弧
  • end-object = ws %x7D ws ;  }  オブジェクト終了文字 右波括弧
  • name-separator = ws %x3A ws ;  :  名称区切り文字 コロン
  • value-separator = ws %x2C ws ;  ,  値区切り文字 カンマ
また、6種類の構造を決める文字の前後に、意味を持たない空白を置くことができる。
 ws = *(
   %x20 / ;  半角スペース
   %x09 / ;  水平タブ
   %x0A / ;  Line feedまたはNew line
   %x0D ) ;  Carriage return

 括弧の名称は、JIS Z 8201-1981に合わせました。現在は、( ):丸括弧、[ ]:角括弧、{ }:波括弧というのがJIS規格でした。*6

 ”3種類のリテラル名” の正体は、3.Valueで記載されています。原文では実施していませんが、見やすくするために構造を決める文字の表現を太字にしています。

 2番目の文章はまだろっこしいですが、要は以前は、[ .... ]  または、{ .... } とオブジェクトまたは配列型になっているものだけをJSONとしていたのを、先頭に”[" , "{"  や末尾に "]"  , "}" が無いものもJSONとしたということです。わざわざ実装としているので、そのような実装が多かったのでしょうか。

 文中 ws ってなんだ?と思われた方がいらっしゃるかもしれませんが、ws = (White Space)余白の意味です。wsは何であるかを最後に定義しています。*なので、0個でもOKです。

 次は、Valueの定義です。

3. Value

JSONの値は、オブジェクト、配列、数値、文字列、または次の3種類のリテラル名のいずれかでなければならない(MUST)。
  • false
  • null
  • true
リテラル名は小文字でなければならない(MUST)。その他のリテラル名は禁じる。
 value = false / null / true / object / array / number / string
 false = %x66.61.6c.73.65 ;  false
 null = %x6e.75.6c.6c ;  null
 true = %x74.72.75.65 ;  true

  "MUST" 登場です。false も null も trueも小文字でなければならないは、再認識しました。過去、先頭だけ大文字にしたことがあったかもしれない。これまた懺悔です。valueは、記載の7種類という事ですね。false, null, trueの3種類のリテラル名も文字コードでも定義しています。

 次は、Objectの定義です。

4. Objects

 オブジェクトは、0組以上の名称/値のペア(言い換えると メンバー) を中括弧で囲んだものである。名称は文字列で定義する。各名称の後には1つのコロンを続け, 名称と値を区切る。1つのカンマで値と次の名称とを区切る。オブジェクト内の名称は一意にすべきである(SHOULD)。
 object = begin-object [ member *( value-separator member ) ] end-object
 member = string name-separator value
 名称がすべて一意になっているオブジェクトは、そのオブジェクトを受け取るすべてのソフトウェアの実装が、名称-値のマッピングに合致していると相互運用可能である。

  名称/値のペアをメンバーと定義されています。pairs (or members)と記載されているとペア(または、メンバー)と訳しそうですが、ペアの別名の定義です。メンバがユニークで内容が全部一致すればオブジェクト内の順序は問題ないと規定していますが、その記述を省略します。

 次は、Arrayの定義です。

5. Arrays

 配列は、0個以上の値(言い換えると (element) 要素) を大括弧で囲んだものである。要素はカンマで区切る。
 array = begin-array [ value *( value-separator value ) ] end-array
 配列内の値は同じ型である必要はない。

  配列の説明はカンマで区切る程度しかありませんでした。

 次は、数値についての定義です。

6. Numbers

 数値は10進数で表す。先頭にマイナス記号がついても良く、その後に小数部や指数部をつけることができる。但し、先頭0埋め(Leading Zero)は禁止です。
小数部は小数点以下1つ以上の数字が続く。
指数部は大文字E又は小文字のeの後ろにプラス又はマイナスの符号が付けても良く、更に1つ以上の数字が続く。
次の文法で表現できない数値(InfinityやNaNなど)は禁止である。
  number = [ minus ] int [ frac ] [ exp ]
  decimal-point = %x2E  ;  .
  digit1-9 = %x31-39   ;  1-9
  e = %x65 / %x45    ;  e E
  exp = e [ minus / plus ] 1*DIGIT
  frac = decimal-point 1*DIGIT
  int = zero / ( digit1-9 *DIGIT )
  minus = %x2D     ;  -
  plus = %x2B      ;  +
  zero = %x30      ;  0
 この仕様により受け入れ可能な数値の範囲と精度に制限を設けられる。IEEE 754 binary64(倍精度)数値[IEEE754*7 ]を実装したソフトウェアが手に入りやすく広く使われているので、IEEE754並みの実装をしておけば、想定の精度内のJSON数値でほぼ正確という観点で、良好な相互運用性を実現できる。1E400 や 3.141592653589793238462643383279 のようなJSON数値は潜在的な相互運用性の問題を示唆しています。というのは、そのような数値を出すソフトウェアは、受信側のソフトウェアに一般的に受け入れ可能な数値の大きさや精度以上の性能を予想していることを暗示しているからである。
 このようなソフトウェアが使用されている時、[-(2**53)+1, (2**53)-1]の範囲にある整数は、実装上厳密に一致しているという点で相互運用可能であることは覚えておくと良い。
 InfinityやNaNはダメなんですね。送ろうとしてしまったことが。。。。
また実装はIEEE754準拠の数値を想定しておきましょうという事でしょうか。JavaScriptの実行環境が採用している演算方法はIEEE754 ですね。例にあるように 1E400 や 3.14.........の数値はJSONでは送付できてしまいますが、IEEE754程度の精度までを数値型にしておくと良いという事ですね。金融も含め様々な世界ではちょっとした差が不具合につながりますので、小数のある数値はいつも確認したくなります。みなさんも経験があるのではないでしょうか*8
 ここは誤差が出さない方法として、整数だったら[-(2**53)+1, (2**53)-1]の範囲で厳密に一致するよとヒントをくれています。JavaScriptやC++ではそれぞれの値を100倍~1000倍した値を内部で演算する実装を見ます。
 私は、この数値の箇所は勉強になりました。
 次は、文字列についての定義です。 

7. Strings

 文字列の表現はプログラミング言語のCファミリーで用いられている慣習に似ている。文字列は引用符(quotation mark)で始まり、引用符で終わる。エスケープが必須(MUST)文字[引用符、バックスラッシュ、および制御文字(U+0000からU+001F)] を除き、全Unicode文字はクォーテーションマーク内に置いて良い。
 どの文字もエスケープして良い。文字が基本多言語面(U+0000からU+FFFF)ならば、6文字のシーケンス(バックスラッシュの後に小文字のu、その後にその文字のコードポイントをエンコードした4桁の16進数が続く)として表すことができる。16芯を表すAからFは大文字でも小文字でも構わない。例えば、一つのバックスラッシュのみを含む文字列は、"\u005C"の様に表せる。
 あるいは、よく知られている2文字のシーケンス・エスケープ表現もある。例えば、1つのバックスラッシュ文字のみを含む文字列は、"\\"と、より簡潔に表すことができる。
 基本多言語面にない拡張文字をエスケープするために、UTF-16サロゲートペアをエンコードした12文字のシーケンスとして文字を表す。例えば、ト音記号(G clef character)(U+1D11E)1文字の文字列は、"\uD834\uDD1E"と表せる。
 string = quotation-mark  *char  quotation-mark

 char = unescaped /
  escape (
   %x22 /   ; "   quotation mark  U+0022
   %x5C /   ; \   reverse solidus  U+005C
   %x2F /   ; /   solidus  U+002F
   %x62 /   ; b  backspace  U+0008
   %x66 /   ; f   form feed  U+000C
   %x6E /   ; n  line feed  U+000A
   %x72 /   ; r  carriage return  U+000D
   %x74 /   ; t  tab  U+0009
   %x75 4HEXDIG  ) ;  uXXXX  U+XXXX
 escape = %x5C  ;   \
 quotation-mark = %x22   ; "
 unescaped = %x20-21 / %x23-5B / %x5D-10FFFF

 クォーテーションマークと書くか、引用符と書くか迷いました。クォーテーションマークは、シングルコーテーションとダブルクオーテーションの両方とも入るしどうしようかと考えたら、定義に引用符はダブルクオーテーションと定義がありました。基本多言語面*9のエスケープ方法の定義、UTF-16サロゲートペアの記述方法も定義されていました。ト音記号も勉強になりました。ト音記号は表現できるのだろうか?🎼やってみました。

8. String and Character Issues

8.1 Character Encoding
 閉じたエコシステムの一部ではないシステム間で交換されるJSONテキストは、UTF-8[RFC3629]を用いてエンコードしなければならない(MUST)。
 以前のJSON仕様では、JSONテキストの送信時にUTF-8の使用は必須ではなかった。しかし、JSONベースのソフトウェア実装の大半は、UTF-8が相互運用性を実現する唯一のエンコーディングであるということで、UTF-8エンコーディングの使用を選択している。
 実装ではネットワーク送信するJSONテキストの先頭にバイト・オーダー・マーク(U+FEFF)を追加してはならない(MUST NOT)。相互運用性のため、JSONテキストを解析する実装は、バイト・オーダー・マークの存在をエラーとして扱うのではなく、無視してよい(MAY)。

 他の方が書かれるRFC 8259の解説でも、この版でUTF-8が必須になったと解説されています。バイト・オーダー・マーク(BOM)の付与は禁止だが、検出してもエラーとせず無視するのが国際標準となりました。前回書けませんでしたがCSVのような文字コードが混在するところではBOMがないと面倒でしたが、JSONでは国際標準としてBOMは付けず、文字コードを定義しています。

8.2 Unicode Characters

 このセクションは原文の翻訳は省略し、簡潔に要点だけ書きます。RFC 8259のABNFでは、一文字単位では完全なUnicodeでも文字列を解析するとエンコードできない文字列が送付可能であり、そのような文字列を送付した場合受信側の動作は予測できない。変な値を戻すどころか致命的なランタイムエラーを起こすかもしれないと注意を促しています。

8.3 String Comparison 

 このセクションも簡潔に要点だけ書きます。文字列の表現でエスケープする方法があることを記載しましたが、エスケープ文字を変換せずに文字を比較すると同じ文字を同じではないと判断することがあるとあります。例として"a\\b" と"a\u005Cb"を挙げています。使い慣れない言語を採用する場合は、いつも以上にこのような文字列への対応を気にする必要があります。
 この後はパーサーについての記述になります。

9. Parsers

 JSONパーサは、JSONテキストを別の表現に変換する。JSONパーサは、JSON文法に準拠するすべてのテキストを受付けなければならない(MUST)。JSONパーサは、JSON形式ではない書式や拡張機能を受入れても良い(MAY)。
 実装では、受付けるテキストのサイズに制限を設定しても良い。ネストする深度の最大を制限しても良い。数値の範囲と精度を制限しても良い。文字列の長さと文字の内容を制限しても良い。

 実装する場合、JSON文法に準拠するすべてのテキストを受付ける必要はあるものの、各アプリ内では拡張や制限に自由度はあるようです。8.3で書いた文字列の比較が重要になると思います。できるだけ自作よりもオープンソースの適用を考えた方が良いかもしれません。
 次はJSONを作るジェネレータについての記述です。

10. Generators

 JSONジェネレーターはJSONテキストを生成する。生成されるテキストは、JSON文法に厳密に準拠しなければならない(MUST)。

 パーサはJSON形式以外の書式や拡張機能を受付ても良いとあるのですが、それができるジェネレータはJSON文法に準拠しないと思うのですが。。。よく判らないです。

11. IANA Consideratins

 気になったところだけ書きます。

 このメディア・タイプを使用するアプリケーション:
JSONは、ActionScript、C、C#、Clojure、ColdFusion、Common Lisp、E、Erlang、Go、Java、JavaScript、Lua、Objective CAML、Perl、PHP、Python、Rebol、Ruby、Scala、Schemeのすべてのプログラミング言語で記述されたアプリケーション間でデータを交換するために用いる。

 と、プログラミング言語を挙げています。プログラミング言語の列挙がたくさんありますが、上記で列挙されていない VBA、C++ も結構使われていそうなのですが、列挙から外れているのはなんででしょうか。

12. Security Considerations

 一般的に、スクリプト言語にはセキュリティ上の問題が存在している。JSONはJavaScriptのサブセットであるが、代入(assignment)と呼出し(invocation)を除外している。
 JSONの構文はJavaScriptから借用しているため、"eval()"関数を用いてほとんどのJSONテキストをパースできる。テキストにはデータ宣言を伴った実行可能なコードが含まれている可能性があるため、これは一般的に許容できないセキュリティ上のリスクとなる。他のプログラミング言語におけるeval()などの関数の使用にも、JSONテキストがその言語の構文に準拠している場合は、同じ留意点が適用される。

 どこでも起きそうな問題です。

13. Examples

  JSONのサンプルが出ています。JSONオブジェクト、配列が例示され、最後に、値のみの3つのJSONテキストが表示されています。以下もJSONなのですね。

  "Hello world!"
  42
  true

  これも、JSONとは気づきませんでした。

以降、14. 参考文献、付録A. RFC 7159からの変更点、貢献者、連絡先と続きますが、省略します。

 やっと最後までたどり着きました。ちょっと長すぎました。これじゃぁ読まれませんかね。

 実は、

 このブログを書こうと思ったきっかけは、デバッグ用のテキストログにJSON形式で出力をした時です。必要な個所のデータを取得する際に、JSONのViewer やBeautifierで成形すると所望のデータを得やすいと思ったのです。しかし、欲を出してプログラム上で情報を追加してみると、Beautifierさんに「これ、JSONじゃない!!成形できません。」と叱られてしまいました。生兵法は怪我の元、最新仕様を再確認してみようと思いました。

 改めて、RFC 8259を読んでみて勉強になりました。本件に限らず、仕様を決めるのは大変ですね。

 yamlも使う頻度がそれなりにあるのですが、いつも見様見真似で記述しています。勉強が必要です。お薦めのサイトがありましたら、マネックス証券に応募のついでに、ご連絡お願い致します。

 では、また何処かでお会いしましょう。

f:id:money_order:20210329092741p:plain

 

小田切 貴秀システム開発推進部 GXグループ エンジニア

*1:欧州電子計算機工業会(ECMA: European Computer Manufacturers Association)))も標準化を進めていました((ISOxxxxとか、PMPとか認証してお墨付きするビジネスは、海外の方々お得意ですね。

*2:Webに関する開発をしている私は404という数字にちょっと恐怖を感じます。。。。私だけでしょうか???

*3:こちらの会社は、2021.3現在、Webサイトを見つけることができました。Timさんの紹介ページもありました。

*4:私一人だけ?

*5:法務関連も社内で容易に聴けるのはマネックス証券にいる利点の一つです

*6:私の小学校の頃習った( ):小括弧、[ ]:大括弧、{ }:中括弧とは言わないようです。最近知りました。

*7: IEEE, "IEEE Standard for Floating-Point Arithmetic", IEEE 754.

*8:とはいえ、最近でも、①金融関係のE-Learningソフトが提供するブラウザ上の電卓機能や②数値計算とは異なるものの提供機能を例示するGOで書かれたAWS提供のサンプル内の金額計算でも、IEEE754で許容する誤差をそのままにしているのを目にしました。

*9:この用語初めて知りました