How to invoke function from string by XPages SSJS
What's the case you want to invoke the function from string?
There are many situations you want to invoke the function from string of function name. One of case is for example, you might want to switch the functions from the parameter value of HTML Get/Post method.
The example below is client-side javascript(CSJS) when it's triggered from the URL like "http://mydomain.com/ApplyFuncXAgent.xsp?func=callMe". Then switch the flow by the query parameter value of "func".
If you want to simplify the code above, you can write the code like below:
In this case, window object is used to retrieve the function object, then call the target function. However Server-side Javascript(SSJS) does not have the window object like CSJS. Therefore you need to tweak this code a little.
(1) Use "this" instead of "window"
(2) Use eval()
Both (1),(2) can get the same result.
To pass the parameters, Use call() or apply()
Now we could call the function from string of function name. Then if you want to pass the parameters as well when call the funcion, you can use call() or apply() functions. Actually this way is pretty same as CSJS.
For example, when I call the HTTP Get request like "http://mydomain.com/ApplyFuncXAgent.xsp?func=callMe&args=aa,bb". (I added [args=aa,bb] as the function argument's values.) , the SSJS code becomes like below:
The code above is the example of using call(). If you want to use apply(), then instead of passing the arguments separated by comma, pass the Array to the second argument of apply(). Below is the sample code:
Sample code of using XAgent
In my situation, I needed to get the JSON data from the many notes views as the asynchronous connection. (Passing the JSON data for Kendo UI Grid data.) So I decided to use the XAgent to return the JSON data, however I didn't want to create XAgent XPages as many as notes views. So the sample code below calls the function from the Get parameter value of function name and returns the JSON data by the specified function.
HTTP request is like this -> "http://mydomain.com/hoge.nsf/ApplyFuncXAgent.xsp?func=testFunc&args=aa,bb"
Below is the sample code of xpCommon.jss which contains the callFuncByString() above.
The advantage of this logic is avoiding to create many XAgent for notes views. But please remember that dynamically calling the functions from string value sometimes lose the serviceability of code. For example debugging purpose.
*This XAgent is just for example and for the real coding, please pay attention to the security when you use this concept since any SSJS standard functions can be called remotely if you don't add any restriction and it is very dangerous.
XPagesDay 2014 「SSJSでも使える!Javascriptでオブジェクト指向プログラミング入門」スライド公開
XPagesDay 2014 (2014/11/18) 【A-3】「SSJSでも使える!Javascriptでオブジェクト指向プログラミング入門」で使用したスライドを公開しました。
XPagesでJSONをパースしよう、 fromJson関数の使い方
今更聞けないJSONって何?
JSON(ジェイソン、JavaScript Object Notation)は軽量なデータ記述言語の1つである。構文はJavaScriptにおけるオブジェクトの表記法をベースとしているが、JSONはJavaScript専用のデータ形式では決してなく、様々なソフトウェアやプログラミング言語間におけるデータの受け渡しに使えるよう設計されている。
引用:Wikipedia
論より証拠。 JSONってこんな形式です。
[ { "勇者": [ "隼の剣", "光の鎧" ] }, { "格闘家": [ "鉄の爪" ] }, { "魔法使い": [ "いかづちの杖", "水の羽衣" ] } ]
Javascriptのオブジェクトの表記法と配列を組み合わせてとってもシンプルにデータ格納してしまおうということですね。正直、開発者にとってはXMLよりJSONのほうが好きな人のほうがおおいんじゃないでしょうか。
さて、ここから本題です。
XPagesのサーバーサイドJavascriptでJSONをパースするには
SSJSってサーバーで動くJavascriptだよね、だったらJSONもJavascriptのフォーマットだし扱い得意だよね、と当然思う方も多いと思います。
実際、XPagesではJSONをパースさせるのに幾つか方法があるようです。
- eval() 関数を使う。 XPagesの標準関数であるeval()を使ってお手軽にパースすることができます。こちらの例ではビューのカラムに表示させるコンテンツを文書毎にもつJSONのフィールドから一度パースして表示させています。 中々軽量でいいと思います。
- XPagesに標準で実装されているランタイムライブラリ、com.ibm.commons.util.io.json を使う。これはほぼJavaよりのソリューションになりますが、JsonJavaFactory、JsonParser、Iterator 等を使いパースしていきます。 より高度なJSONデータを扱う必要がある場合には適していると思います。例はこちら
- fromJson() 関数を使う。なんとXPagesには標準でfromJson() 関数なるものが存在していました。 これを使えば簡単にSSJSでJSONをあつかえるようになります。
今回は(3)のfromJson() 関数の扱い方について書いてみます。
Domino Designerのヘルプに乗ってない !?
まずこの関数ですが、Domino Designer (バージョン9時点)のヘルプに載ってません。 もう一つJson形式のObjectを文字列に変えるtoJson() 関数も用意されていますが、どちらも載っていません。
Domino DesignerのSSJSの補完機能を見る限り、
fromJson(str:string) any
となり、 strのパラメーターにJSONフォーマットに則した文字列を渡してやると、Javascript Object形式で値が帰ってきます。
Objectから以下のように値を参照できます。
var strJsonSimple:String = '{"name":"apple","color":"red","num":"10"}';
var jsonObjSimple = fromJson(strJsonSimple);
return jsonObjSimple.name;
しかし、この場合(青文字の部分)はObjectに"name"という変数(属性?)があるということを前提にプログラミングされています。
実際は、JSON文字列がObjectになった後、変数もその値もわからないこともあると思います。
その場合、Javascriptでfor-in文による繰り返しで回してやれば値の取得が可能になります。
例として、冒頭で上げたJSONをXPagesでパースして表示しているサンプルを掲載します。 Objectに配列を持つという組み合わされた構造のJSONを以下のようにパースしています。
var strRet:String = ""; var strJson:String = '[{"勇者": ["隼の剣","光の鎧"]},{"格闘家": ["鉄の爪"]},{"魔法使い": ["いかづちの杖","水の羽衣"]}]'; var jsonObj = fromJson(strJson); for(var char_num in jsonObj){ var charEntry = jsonObj[char_num]; for(var char_name in charEntry){ strRet += "キャラクター"+char_num+": "+ char_name +" "; var charEquipment = charEntry[char_name]; var equipments = @Explode(charEquipment, ","); for (var k = 0; k < equipments.length; k++){ strRet += " 装備"+(k+1)+" = "+equipments[k]+" "; } } } return strRet;
計算結果フィールドに入れた結果はこんな感じです。
[CakePHP] JSON出力時にstring型をint型に戻す
久々のブログ、今回はCakePHPでJSON出力をするときにInteger型、String型に型変換して出力する方法について書きます。 (あ、今回も他力本願、満載)
まず、CakePHPでJSON出力をする為には
Controllerで
$yourJsonObect= $this->YourModel->find('all'); $this->set('yourJsonObect', $yourJsonObect);
としてやり
Viewで
<?php echo $javascript->object($yourJsonObect); ?>
としてやれば簡単に実現できます。
出力されるコードは以下のようになります。
[{"YourModel":{"id":"10060","foreign_id":"1","short_description":"Short Desc Test.","long_description":"Long Desc Test"}, {} ..... )]
これで、殆ど場合は問題ないのですが、データベース上でIntegerで持っているスキーマ(id, foreign_idなど)も全てString型として出力されます。
もし、JSONデータを受け取る側が厳密にInteger型、String型を区別して受け取りたい場合などに以下の方法でスマートに解決できたので紹介します。
余談ですが、自分の場合は、iPhone開発において、JSONデータをウェブサーバーから受け取り、Core DataにInsertする時に、iPhone側で型チェックや型変換をさせたくなかったので、サーバー側で吸収してしまおうという経緯がありました。
参考にしたサイトはこちらです。
CakePHPのmodelの結果配列でstring型をint型に戻す。
CakePHPでmodel::findなどの関数で結果配列を取得したときに、データベースではint型の値string型として返って来ます。
今回は、flashにjson出力でデータを渡したかったので、別に文字列型のまま渡しても問題ないのかもしれないけど、なんか気持ち悪い。
それに例えばよくある、intの0,1をフラグと扱って if (flag) {.... }なんて書いたらマズいんじゃないかと思うので、settypeで型変換を行う方法。
モデルの親クラスで、Findで取得したデータ(デフォルトは全てString)をデータベースのTypeを参照して、型変換を行っています。
app_model.phpにafterFind()オーバーライド関数を追加してやるだけ。
class AppModel extends Model{ public function afterFind($results, $primary = false) { parent::afterFind($results, $primary); $columnTypes = $this->getColumnTypes(); foreach($results as $index => $result) { foreach($columnTypes as $field => $columnType) { if($columnType == 'integer') { if(isset($results[$index][$this->name][$field])) { settype($results[$index][$this->name][$field], 'integer'); } } } } return $results; } }
これで全てのモデルが自動変換されるようになりました。 感激~
変換後のJSON出力を見ると以下のようになります。
[{"YourModel":{"id":10060,"foreign_id":1,"short_description":"Short Desc Test.","long_description":"Long Desc Test"}, {} ..... )]
Integer型の数字の部分からダブルクォーテーションが消えました。
めでたしめでたし。