JSONに関する注意点
職場で、JSONを返すAPIで値が無い/必要ないって時に何を返すべきかという話題がチラホラとあがります。
「trueとかってJSONなの?」って質問に自信満々で「はいJSONです!」「nullも文字列リテラルもJSONです!」とおまけ付きで即答したものの、不安になってRFCにあたったら間違えてました。
http://www.ietf.org/rfc/rfc4627.txt
ここの 2. JSON Grammar のBNFを見ると
A JSON text is a serialized object or array.
JSON-text = object / array
ってハッキリ書いてあります。
nodeの JSON.parseとかがパースできちゃうので勘違いしてました。
http://jsonlint.com/
ここにnullとか入れても正しく失敗します。
その他、ありがちなミスについては
http://d.hatena.ne.jp/m-hiyama/20080728/1217205390
檜山さんトコを参照スべし。
しかしそれにしても、HTTPに乗ったJSONの
Content-Length: 0
Content-Type: application/json
が正しいのか、正しい場合どう取り扱うべきなのかは謎のママ。
WebSocketをサクっと試すはずだった。
なのに
mod_pywebsocket 0.5.2 と Google Chrome 5.0.375.126 で echo_wsh が 動かない人(co-sche)へ。
# httpd.conf とか PythonOption mod_pywebsocket.allow_draft75 On
これ、忘れてないっすか><
echo_client.pyなら動くのに…って時は要チェックです。
差分の内どこが原因なのかまでは確認出来ていないのですが、上記のバージョンの組み合わせだと
の違いによってデフォルトではハンドシェイクがウマくいかなくなっているみたいです。
最初っから、pydocちゃんと読めよ、俺…。
ちなみに
現時点でWebSocket Protocolの最新のドラフトはhttp://www.whatwg.org/specs/web-socket-protocol/にあります。
綺麗なバラにゃトゲがある
for (var i = 0, elem; elem = elems[i]; i++) { doSomething(elem) }って書き方見やすくていいな - こんにちはこんにちはmonmonです!で紹介されている記事に、ちょっとツッコミます。
元記事の紹介
ノードリストのイテレート - Google JavaScript Style Guide 和訳
var paragraphs = document.getElementsByTagName('p'); for (var i = 0, paragraph; paragraph = paragraphs[i]; i++) { doSomething(paragraph); }これはすべてのコレクション, 配列に対してうまく動きます. 要素がなくなるまでループし, 最後には false となりループが終了します.
落とし穴
代入文が値を返すことを利用した美しいコードで、getElementsByTagNameで取ってきたノードリストのイテレートには有用ですが、以下のような例では初っ端でコケちゃいます><
var list = [0, 1, 2, 3]; for (var i = 0, item; item = list[i]; i++) { doSomeThing(item); }
なので…
↑の原文は
Tips and Tricks - Google JavaScript Style Guide
This works well for all collections and arrays as long as the array does not contain things that are treated as boolean false.
↓のように
[追記]現在は修正されています。コメント欄にも書いたように、本来は訳者のid:cou929_laさんにフィードバックを送るべきでした。[/追記]
これは、配列が真偽値のfalseとして扱われるものを含まない限り、全てのコレクションや配列に対してうまく働きます。
…って、原文にはちゃんと書いてありますね。
Scalaめっちゃ気持ちいい
ECMAScript4の頓挫以来探し求めていた、サクッと書けそうな言語にようやく出会えました。
Scalaです。
おまけについてきて嬉しかった特徴
in_arrayのややこしい版
仕事で必要に駆られて書いてみました。
イメージとしては、タイトル通りin_arrayに複雑な条件を指定出来るようにしたもので、
第一引数の配列のネストレベルが深くなる毎に、and/orをトグルしながら検索していきます。
思い出しながらなので、こんなんだったかどうか微妙><
使い方
<?php /* 検索条件 */ $needles = array(1, 2, array(3, array(4, 5))); /* 検索対象を変えながらテスト */ echo in_array_ex($needles, array(1, 3, 2)); // true echo in_array_ex($needles, array(1, 4, 2)); // false echo in_array_ex($needles, array(4, 5, 1, 2)); // true ?>
ソースコード
<?php function in_array_ex(Array $needles, Array $haystack, $logical_op = 1) { if (empty($needles)) return $logical_op; $needle = array_shift($needles); $tmp; if (is_scalar($needle)) $tmp = in_array($needle, $haystack); else if (is_array($needle)) $tmp = in_array_ex($needle, $haystack, !$logical_op); else throw new Exception( 'The 1st argument must be an array of mixed in array, and scalar.' ); if ($logical_op) return $tmp && in_array_ex($needles, $haystack, $logical_op); else return $tmp || in_array_ex($needles, $haystack, $logical_op); } ?>
概要と使い道
眠いのでまた後日…><
phpの可変長引数についていろいろ
仕事でphpを書くようになって、今まで触れていた言語とのいろんなお作法の違いによく躓く毎日です><
結構長時間ハマった部分があったので、メモメモ。
今回は、phpで可変長引数を受け取ったり渡したりする方法について、JavaScriptとの対比で見ていきます。
先ずは、受け取る方から。
これはいろんなWebページで紹介されてますねー。
addAllは、引数の総和を返す関数です。
JavaScript
function addAll() { var result = 0; var args = arguments; var i = 0; var imax = args.length; for (; i < imax; i++) { result += args[i]; } return result; } console.log(addAll(1, 2, 3, 4)); // output 10
php
<?php function addAll() { $result = 0; $args = func_get_args(); $i = 0; $imax = count($args); // ((func_num_args()との違いを後で調べる。)) for (; $i < $imax; $i++) { $result += $args[$i]; } return $result; } print(addAll(1, 2, 3, 4)); // output 10 ?>
ここで、これらの関数の引数は配列ではありません。
次、渡す方いきます。
渡す方と言っても、関数呼ぶだけならそのまま
addAll(1, 2, 3, 4);
で終わってしまいますね><
ここでは自身も可変長引数を取り、内部でaddAllを呼ぶ関数を書いてみます。*1
wappedAddAllは、引数の総和を3乗して返す関数です。
JavaScript
function wrappedAddAll() { var args = arguments; var sum = addAll.apply(null, args); return Math.pow(sum, 3); } console.log(wrappedAddAll(1, 2, 3, 4)); // output 1000
php
<?php function wrappedAddAll() { $args = func_get_args(); $sum = call_user_func_array('addAll', $args); return pow($sum, 3); } print(wrappedAddAll(1, 2, 3, 4)); // output 1000 ?>
これが出来るとなにが便利か。
実はこのcall_user_func_array、組み込み関数(ビルトインファンクション・定義済み関数)やクラスメソッド、インスタンスメソッドにも使えてしまうんです。
例えば、mysqli_stmt::bind_param(このメソッドは可変長引数を取ります。)をラップして、多次元配列を受け取ってプリペアドステートメントに順次投げるなんてこともできます。
PDO::executeみたいな。
自分は、この"user"に惑わされてスルーしてしまったが為に、かなりの時間をロスしてしまいました。
何故わざわざ"user"なのか、ご存知の方がいらっしゃいましたらコメント下さい><
*1:参照渡しだの値渡しだの参照の値渡しだの、applyはthisがナンダカンダだのというのは今回の趣旨から外れるのでここでは言及しません。
as3corelibのJSONと改行コード
as3corelibのJSONクラスを使うと、ActionScript 3.0からJSONを手軽に扱えます。
…と、得意げに言ってみましたが、実はハマりました><
タイトルにもある通り、URLLoader#load等で得た文字列をJSON.decodeする時、改行コードがLFでないとパースエラーに見舞われます。
現在(as3corelib-.92以降)はCRでもLFでもCRLFでも大丈夫です。
id:FLIP_FLOPさんにコメントを頂き、再検証しました。
なので、以下はcorelib-.90以前のバージョンにのみ該当します。
[/追記][Fault] exception, information=Error: Unexpected encountered Fault, parseError() at JSONTokenizer.as:546
JSONLintもスンナリ通るし、おっかしーなーと思いつつ、ためしに空白文字・改行文字を削ってみると、
スペースなし → NG
タブなし → NG
改行なし → OK
という結果。
もしや…と.jsonファイルの改行コードをCRLFからLFに変えたところ、OKでした。
手書きのJSONはあまり扱わないかもしれませんが、ちょっとテストしてみたい時などには不便ですね。
それに、様々なWeb APIがJSONを吐き出す昨今、コード中にCRLFが含まれないとも限りません。
そこで、取得した文字列を置換することで対応しました。
ちなみに、自分が読んだ限りでは、JSONの仕様には改行コードに関することは明記されていませんでした(文字列リテラル内のエスケープ以外)。
いつものようにコード例を添えて覚書
まずは.json。
// something.json { "propA" : [0, 1], "propB": [ [2, 3, 4], [5, 6, 7], [8, 9, 10] ] }
そして、.as。
// Main.as package { import com.adobe.serialization.json.JSON; import flash.display.Sprite; import flash.events.Event; import flash.net.URLLoader; import flash.net.URLRequest; public class Main extends Sprite { private var jsonURL:String = 'json/something.json'; private var obj:Object = {}; public function Main():void { var urlRequest:URLRequest = new URLRequest(jsonURL); var urlLoader:URLLoader = new URLLoader(); urlLoader.addEventListener(Event.COMPLETE, completeHandler); urlLoader.load(urlRequest); } private function completeHandler(event:Event):void { event.target.removeEventListener(event.type, arguments.callee); var urlLoader:URLLoader = event.target as URLLoader; var jsonString:String = (urlLoader.data as String).replace(/\r\n|\r/g, '\n'); obj = JSON.decode(jsonString); trace(obj.propB[1]); } } }
一連の流れが見えるように、まるまる書いてみました。
ポイントは、completeHandler内のvar jsonStringの行です。
これで、.jsonの改行コードはCR、LF、CRLFのどれでもOKになります。
取り込んだ後でテキストとして扱う必要がないのなら、別に\nにしなくても空文字列でよいですね><
参考サイト
- JSON
- JSONの仕様。非常に短く簡潔です。ページの先頭で言語を選べます(日本語もある)。
- JSONLint - The JSON Validator
- そのまんま、JSONの構文チェッカーです。
- もう一度、ちゃんとJSON入門 - 檜山正幸のキマイラ飼育記 (はてなBlog)
- JSON仕様の、見落としがちな部分+αが丁寧にまとめられた記事です。
初めてJSONを出力する側に立った方は必読! - Google Code Archive - Long-term storage for Google Code Project Hosting.
- as3corelibのプロジェクトページです。JSON周り以外にも、ユーティリティクラスが満載!