この記事は、セカンドライフ 技術系 Advent Calendar 2013向けに書かれた記事です。今回は、JSONとリストを一括変換するLSL関数を紹介します。
llJson2List
list llJson2List(string src)
JSON文字列を、LSLのリスト変数に変換します。引数に対象のJSONを指定し、返り値が変換したリストになります。
// リストの表示をわかりやすくするため、文字列への変換関数を用意しておきます string dump(list l) { return "[ " + llDumpList2String(l, ",") + " ]"; } // JSONオブジェクトを変換してみます list value1 = llJson2List("{ \"a\" : 1, \"b\" : true }"); // [ a,1,b, ]と表示されます。strided list(ひとつ飛ばしのリスト)になります // リストの最後の項は、JSON_TRUEです llOwnerSay(dump(value1)); // JSON配列を変換してみます list value2 = llJson2List("[ 1, \"two\", \"3\" ]"); // [ 1,two,3 ]と表示されます。普通にリストに変換されます // ちなみにリストの要素は全て文字列型になります llOwnerSay(dump(value2)); // 単一の値を変換してみます list value3 = llJson2List("1"); // [ 1 ]と表示されます。ひとつだけ要素を含んだリストになります llOwnerSay(dump(value3)); // keyをnullにした不正なJSONを変換してみます list value4 = llJson2List("{ \"a\" : 1, null : true }"); // [ ]と表示されます。JSON_INVALIDだけ含んだリストです llOwnerSay(dump(value4));
JSONオブジェクトは、strided list(ひとつ飛ばしのリスト)に変換されます。LSLにはstrided listを扱える関数がいくつかあるので活用できそうです。また、JSONをリストに変換した時、リストの要素は全て文字列型になります。これは例えば数値をfloatに勝手に変換して桁落ちしたりしないような配慮だとおもいます。
不正なJSONが入力された場合は、JSON_INVALIDのみ含んだリストが返されるので、入力値のJSONが信頼できない場合は、条件分岐でJSON_INVALIDと比較するのが良さそうです。
if(llList2String(value4, 0) != JSON_INVALID) { // 本来の処理 // ... }
では、ネストしたJSONは正しく変換できるのでしょうか。
string json = " { \"a\" : 1, \"b\" : [ 1, \"two\", \"3\" ], \"c\" : { \"d\" : 2, \"e\" : false } }"; // ネストしたJSONを変換してみます list value1 = llJson2List(json); // [ a,1,b,[ 1, "two", "3" ],c,{ "d" : 2, "e" : false } ]と表示されます。 llOwnerSay(dump(value1));
JSONの内部にあるJSONは再帰的に変換されず、文字列としてリスト内に格納されます。なので、深いところにある値を取得するには、自力で何回か変換してやる必要があります。
// [ 1, \"two\", \"3\" ]の部分を取得し、変換します list value2 = llJson2List(llList2String(value1, 3)); // [ 1,two,3 ]と表示されます。 llOwnerSay(dump(value2)); // { \"d\" : 2, \"e\" : false }の部分を取得し、変換します list value3 = llJson2List(llList2String(value1, 5)); // [ d,2,e, ]と表示されます。最後の項はJSON_FALSEです llOwnerSay(dump(value3));
llList2Json
string llList2Json(string type, list values)
LSLのリストを、JSON文字列に変換します。引数の1つめはJSONの種類(JSON_OBJECTか、JSON_ARRAY)、2つめは変換するリストです。返り値は変換したJSON文字列になります。
// JSONオブジェクトに変換してみます string value1 = llList2Json(JSON_OBJECT, ["a", 1, "b", JSON_TRUE]); // {"a":1,"b":true}と表示されます。JSON_TRUEはtrueに変換されます llOwnerSay(value1); // JSON配列に変換してみます string value2 = llList2Json(JSON_ARRAY, [1, "two", "3"]); // [1,"two","3"]と表示されます。ListからJSONへの変換では数値と文字列は区別されます llOwnerSay(value2);
ちなみに、keyに数値を指定するなど、おかしな操作をするとJSON_INVALIDが返ります
// Keyに1を指定 string value3 = llList2Json(JSON_OBJECT, [1, 2]); // JSONのKeyは文字列でないといけません if(value3 == JSON_INVALID) { llOwnerSay("不正な操作です"); }
では、ネストしたJSONを作ってみましょう。
/* こんなJsonを作ってみます { "a" : 1, "b" : [ 1, "two", "3" ], "c" : { "d" : 2, "e" : false } } */ // 内部のJSON(配列)を、先に作っておきます string subjson1 = llList2Json(JSON_ARRAY, [1, "two", "3"]); // 内部のJSON(オブジェクト)を、先に作っておきます string subjson2 = llList2Json(JSON_OBJECT, ["d", 2, "e", JSON_FALSE]); // 最後にそれらを合成します string json = llList2Json(JSON_OBJECT, ["a", 1, "b", subjson1, "c", subjson2]); // {"a":1,"b":[1,"two","3"],"c":{"d":2,"e":false}}と表示されます llOwnerSay(json);
小さい部品から先に作っていけばいいわけですね。ちなみにこういう書き方もできます。
// 一気に組みあげます string json = llList2Json(JSON_OBJECT, [ "a", 1, "b", llList2Json(JSON_ARRAY, [1, "two", "3"]), "c", llList2Json(JSON_OBJECT, ["d", 2, "e", JSON_FALSE]) ] ); // {"a":1,"b":[1,"two","3"],"c":{"d":2,"e":false}}と表示されます llOwnerSay(json);
以上で、一括変換の説明も終わりです。
2回に渡ってLSLでJSONを扱う方法を書いてきました。JSONを扱えるようになることで、Webサービスとの連携がやりやすくなるはずです。例えばTwitterもJSONを返すAPIがありますね(OpenAuthの認証が必要ですが)。しかし一方でなんでもJSONオブジェクトにしちゃったりすると、Keyの分だけ少ないLSLメモリを消費してしまうという罠もあります。本番運用では、そのへんも見極めて最適なデータ形式を選択していけるといいんじゃないかなとおもいます( ̄∇  ̄ )