2014年5月2日
作成 大坂 哲司
XMLとJSON、これからはJSONだ
1.XMLを通して見たJSON
Webサービスのデータ交換において送受信のデータ形式は、厳密にデータを記述できるXMLを採用してきた。しかしAjax以後のWeb APIでは、コストがかかるXMLより、ポータブルで利用し易いJSONが主に用いられるようになった。今や、JSON(JavaScript Object Notation)は標準のデータ形式となっている。このような中で、XMLを通して見たJSONについて、再考する。
XMLはデータや文書に意味を持った構造(タグと属性)を付加したもので、XMLで記述したデータは、他のシステムでも同一の意味で解釈できることが保証され、Webサービスのデータ形式として今も多用されている。
一方、JSONは、キーと値のペアをコロンで区切り{}で包んだJavascriptのオブジェクトである。{}内にカンマ区切りで複数のキー値のペアを入れることも、また値にオブジェクトや配列を入れることもできる。キーは必ずダブルクォートで囲まなければならず、値は文字列型、数値型、論理型のデータ、それにオブジェクトを入れることができる。JSONオブジェクトは、XMLでのタグで値を囲むのと似ている。
{ "a" : "A" } { "a" : "A", "b" : "B" } { "a" : { "b" : "B" } } { "a" : [ { "b1" : "B1" }, { "b2" : "B2" } ] }
これまでのXMLデータを利用のため、XMLからJSONへ変換するツールも利用されている。XMLとJSONにどのような差異があるか、XMLとJSONの互換性を含め、見てみよう。
コード1(XML)コード1(JSON) { "fruits":[ "fruit":{"name":"すいか", "price":3000}, "fruit":{"name":"いちご", "price":700} ] } すいか 3000 いちご 700
コード1は”fruits”内に”fruit”を記述する簡単なサンプルで、XMLとJSONを比較する。”fruit”では名称と価格を記述しているが、両者とも見た目が少し異なるものの、同じ意味として可読できる。コード1の例では、XML-JSON相互に互換で、それぞれへの変換が可能であることを暗示している。
一方で、XMLの要素が属性を持つ場合、要素の中にテキストノードと子要素を持つ場合は相互互換性が成り立たなくなる。
コード2(XML)700 コード2(JSON) {"price":{"unit":"JPY", "$text":700}}
XMLでは要素のプロパティを属性としてタグの中に記述する。価格の通貨を“unit”で“JPY”と定義し、値”700”が700円であることを定義している。XMLの要素が属性を持たなければ、{"price":700}と言うJSON式になるが、要素が属性を持つとちょっと違った表現となる。{"price":{"unit":"JPY", 700}}としたいところである。しかしJSONの文法に違反するため、コード2では”$text”と言うキーを用い、その値を”700”としている。”700”はXMLでテキストノードと言われるため、”$text”をキーとした。
コード3(XML)700 コード3(JSON) {"price":{"unit":"JPY", "$text":700, "trade":"取引条件・・・"}}取引条件・・・
更に子要素が加わると、コード3のJSON式となる。この式ではXMLの属性も、子要素も同列表記となり、XMLの階層構造を暗示しなくなっている。XMLとの互換を維持しようとするのであれば、属性であることが分かるように、”unit”を”$attr_unit”とすると($attr_は属性を暗示させるため)、JSONをパースする中で、XMLへの変換が可能となる。ただし、JSONからXMLへ変換が、果たして有効な手段であるかは別とする。
参考サイト:
“Webサービスドキュメント - APIソリューション - 共通の仕様・その他”
XMLの仕様では、要素は記述された順番に読み込まれるとは限らないと言われている(これまでいろいろなXMLパーサーを利用し、すべて順番通りの結果を得ている)。XMLデータの場合、要素の並び順が処理に影響されることはない。JSONも同様に並び順に処理は影響されない。しかしXMLデータでなく、XHTMLのようなXML文書となると話は別である。記述した順に従い表示されなければ、意味が分からい文字列の並びとなる。もし、順番が必要であれば、{}で囲まれているJSONオブジェクトの中に、順を示す情報の書き込みが必須である(でも使うことある?)。
2.JSONオブジェクトの操作
コード4 <script type="text/javascript"> var data={ "fruits":[ {"fruit":{"name":"すいか", "price":{"unit":"JPY", "$text":3000}}}, {"fruit":{"name":"いちご", "price":{"unit":"JPY", "$text":700}}} ] } alert(JSON.stringify(data.fruits[0])); alert(data.fruits[1].fruit.name+" "+data.fruits[1].fruit.price.$text+" "+data.fruits[1].fruit.price.unit); </script> 結果 {"fruit":{"name":"すいか","price":{"unit":"JPY","$text":3000}}} いちご 700 JPY
“fruits”は配列型であるため、その中の1番の“fruit”をアクセスするには“fruits[0].fruit”とする。
コード5 <script type="text/javascript"> var data={ "a": { "":"z0", "$text":"aaa", "b": { "id": "foo" }, "$text":"bbb", "":"z1", "$text":"ccc" } }; alert(data.a[""]); alert(data.a.$text); </script> 結果 z1 ccc
JSONオブジェクト内に同じキーがある場合、最後のキーが示すデータを参照される。
3.JSONオブジェクトの作成
JSONオブジェクトの作成方法として、次に挙げる方法がある。
3.1 JSONオブジェクトの初期値
コード6 var comment = {"message":"メッセージ","date":"2014-02-01T09:00:00.000Z"}; alert(JSON.stringify(comment)); 結果 {"message":"メッセージ","date":"2014-02-01T09:00:00.000Z"}
3.2 JSONオブジェクトの生成
コード7 var comment=new Object(); comment.message = "メッセージ"; comment.date = new Date(); alert(JSON.stringify(comment)); 結果 {"message":"メッセージ","date":"2014-05-01T09:00:00.000Z"}
3.3 JSON文字列からJSONオブジェクトの生成
コード8 var json = '{"message":"メッセージ","date":"2014年5月1日","age":27}'; var comment = JSON.parse(json); alert(JSON.stringify(comment)); 結果 {"message":"メッセージ","date":"2014年5月1日","age":27}
JSON.parse関数でJSON文字列のJSONオブジェクト化ができ、JSON.stringify関数でJSONオブジェクトのシリアライズができる。
4.連想配列とJSON
配列型のJSONでは、オブジェクトの中身はfor文で走査しないと対応するデータにたどり着けない。連想配列型ではキーと合致するデータを簡単に得ることができる。但し、Javascriptでは、連想配列とJSONに違いはほとんどないのでは、と思っている。
コード9 var seiza={ "牡羊座":{"date":"3/21-4/19","image":"ohitsuji.gif","yomi":"おひつじざ"}, "牡牛座":{"date":"4/20-5/20","image":"oushi.gif","yomi":"おうしざ"}, "双子座":{"date":"5/21-6/21","image":"futago.gif","yomi":"ふたござ"}, "蟹座":{"date":"6/22-7/22","image":"kani.gif","yomi":"かにざ"}, "獅子座":{"date":"7/23-8/22","image":"shishi.gif","yomi":"ししざ"}, "乙女座":{"date":"8/23-9/22","image":"otome.gif","yomi":"おとめざ"}, "天秤座":{"date":"9/23-10/23","image":"tenbin.gif","yomi":"てんびんざ"}, "蠍座":{"date":"10/24-11/21","image":"sasori.gif","yomi":"さそりざ"}, "射手座":{"date":"11/22-12/21","image":"ite.gif","yomi":"いてざ"}, "山羊座":{"date":"12/22-1/19","image":"yagi.gif","yomi":"やぎざ"}, "水瓶座":{"date":"1/20-2/18","image":"mizugame.gif","yomi":"みずがめざ"}, "魚座":{"date":"2/19-3/20","image":"uo.gif","yomi":"うおざ"} }; var a="牡羊座"; alert(a+" "+seiza[a].date+" "+seiza[a].image); 結果 牡羊座 3/21-4/19 ohitsuji.gif
5.JSONの応用
webStorageなどのKVSで、valueにいろいろな情報をJSONで記述すると、簡単に目的とするデータを得ることができる。コード8ではキーがidで、valueにJSONで記述したusernameなどの情報があり、ローカルストレージの参照、登録を示す。JSONオブジェクトを直接登録できないため、シリアライズして登録する。参照時は、得たvalueをJSONオブジェクトにパースし、必要な情報をJSONオブジェクトから取り出す。KVSではキー参照が高速であるため、valueのパース、シリアライズのコストは小さいものである。
コード10 var val=localStorage.getItem(id); if(!val){//キーがローカルストレージに登録されていない場合 var data={"username":username, "date":(new Date()).getTime()}; localStorage.setItem(id,JSON.stringify(data)); } else{//キーがローカルストレージに登録されている場合 val=JSON.parse(val); username=val.username; }
6.JSONが格納できるDB
MongoDBはドキュメント指向データベースである。KVSではJSONをシリアライズしたが、MongoDBでは、BSON(Binary JSON)と言う形で保存されるため、JSONで表せるオブジェクトであれば、何でもDBに格納できる。データの型としては、文字列型、日付型、数値型、論理型などの形式が利用できる。JSONと同じように参照・検索などの操作ができ、大変手軽なデータベースである。JSONの操作に慣れてくると、もうこのDBが手放せないものとなる。
6.1 MongoDBへの接続
ここではnode.jsを用いて説明する。Node.jsではMongoDBに接続するmongooseと言うミドルウェアを用いる。まずデータを定義するためのスキーマを作成し、データベースに接続する。
コード11 スキーマ登録とDB接続 var mongoose = require('mongoose'); //スキーマの取得 var Schema = mongoose.Schema; //commentスキーマの定義 var commentSchema = new Schema({ username : { type:String, required: true }, message : { type:String, required: true }, date : { type:Date, default: Date.now } }); //モデルの定義(スキーマの登録) mongoose.model('Comment', commentSchema); //DBへの接続 var db = mongoose.createConnection('mongodb://localhost/chat_db'); //モデルの呼び出し var Comment = db.model('Comment');
6.2 MongoDBへの登録
登録したデータモデルにデータを付加し、saveすると登録となる。
コード12 DB書き込み var comment = new Comment(); comment.username = data.username; comment.message = data.message; comment.date = Date.now(); comment.save( function(err, comment) { if (err) { console.log(err); } });
6.3 MongoDBの検索
データモデルに対して、findメソッドで参照を行う。下記の例では、comment.dateに対して期間検索を行い、条件にマッチした結果を新しい日付順にそーとし、その内の50件を対象とすることを意味する。JSTDate、ISODateは下記の参照サイトを参照のこと。
コード13 DB検索 Comment.find({"date":{"$gte":JSTDate("2014-01-01"),"$lte":JSTDate("2014-05-31")}}).sort({date:-1}).limit(50).exec( function(err, docs) { var st=""; if(docs){ for (var i=0; i<docs.length; i++) { st += "username: "+docs[i].username+" message: "+docs[i].message+" date: "+docs[i].date+ "\n"; } } else st="No Data"; console.log(st); });
参考サイト:
node.js から MongoDB にアクセス (Mongoose の紹介)