まだプログラマーですが何か?

プログラマーネタとアスリートネタ中心。たまに作成したウェブサービス関連の話も http://twitter.com/dotnsf

タグ:json

久しぶりの Java プログラミングの機会があり、これまた久しぶりにサーブレットを作りました。POST されたデータを受け取って、バイナリデータを生成して、Content-Type をつけてストリームで返す、というものです。

この POST データの受け取り方をどうするかで(少しだけ)迷ったのですが、変数もその型も数も不定で受け取るような仕様だったので、深く考えずに JSON データを受け取ることにしました。普段の Node.js の時は特殊な事情がない限り JSON で受け取ることにしているので、その延長というか「まあイマドキは JSON だよね」くらいに考えていました。

・・・が、これが意外と苦戦。 誰かの参考になれば・・・、と思って、以下実際に作ったサーブレットの実装を紹介します。

まず最初はクライアント(呼び出し)側は普通にこんな感じの実装にしました。jQuery の AJAX を使ってタイムスタンプ値を含む JSON オブジェクトをポストしています。これを受け取って処理するサーブレット(/postdata)を作るのが今回の目的となります:
  :

$.ajax({
  type: 'POST',
  url: './postdata',
  data: { timestamp: ( new Date() ).getTime(), body: 'ハローワールド' },
  success: function( result ){
    console.log( result );
  },
  error: function( err ){
    console.log( 'error' );
  }
});
:

そしてそのサーブレットのコード部分が以下です:
public class PostdataServlet extends HttpServlet {
	
  @Override
  protected void doPost( HttpServletRequest req, HttpServletResponse res )
throws ServletException, IOException {
req.setCharacterEncoding( "UTF-8" ); try{
//. JSON テキストを全部取り出す BufferedReader br = new BufferedReader( req.getReader() ); String jsonText = br.readLine(); jsonText = URLDecoder.decode( json, "UTF-8" ); //System.out.println( jsonText );
//. JSON オブジェクトに変換 JSONParser parser = new JSONParser(); JSONObject jsonObj = ( JSONObject )parser.parse( jsonText );
//. JSON オブジェクトから特性の属性を取り出す
String body = ( String )jsonObj.get( "body" );
: }catch( Exception e ){ e.printStackTrace(); } } }

要は req.getParameter() などを使って特定のパラメータ値を取り出すわけではなく、req.getReader() を使ってポストされてきた全データ(今回の場合は JSON オブジェクトをテキスト化したもの)を受け取る必要があります。そして受け取ったテキストデータを(上記の例であれば JSON-Simple ライブラリを使って)JSON オブジェクト化した上でサーブレット内で取り扱う、という手法です。


・・・単純に JSON データを扱いたかっただけなんだけど、こんなに面倒だったっけ?

 

JavaScript で JSON 配列をソートする、意外と簡単な方法があります。具体例として、例えば以下のような JSON 配列: myArray があったときに、
var myArray = [
 { id: 1, name: "鈴木", age: 30 },
 { id: 2, name: "田中", age: 29 },
 { id: 3, name: "佐藤", age: 35 },
   :
];

この JSON 配列を age 要素の小さい順にソートしたいという場合にどうやるか、というケースを紹介します。


まず、JavaScript の配列には sort 関数が標準で用意されています。直感的にこんな感じで使えます:
var array1 = [ 30, 29, 35 ];
array1.sort();                  //. => array1 = [ 29, 30, 35 ];

注意点として sort() 実行後の返り値がソート結果になるのではなく、実行すると array1 そのものが書き換えられる、という点に注意が必要です。

sort() 関数には比較関数をパラメータで指定して、比較方法を指定することもできます。例えば以下の例では比較関数 compare を「小さい順に並べる」ような内容にしているので、sort() 実行後に小さい順にソートされます(要するに普通の sort() と同じ結果になります):
//. 比較関数
function compare( a, b ){ var r = 0; if( a < b ){ r = -1; } else if( a > b ){ r = 1; } return r; }
var array2 = [ 30, 29, 35 ]; array2.sort( compare ); //. => array2 = [ 29, 30, 35 ];

比較関数はパラメータ a と b の大小関係をその返り値によって定義します:
・返り値が 0 より小さい場合、a < b(aはbよりも前に来る)
・返り値が 0 より大きい場合、a > b(aはbよりも後ろに来る)
・返り値が 0 の場合、a = b(aとbの位置関係は変わらない)

したがって、compare 関数の返り値の符号を逆にすると「大きい順に並べる」という比較関数を指定したことになり、sort() 実行後に大きい順にソートされます:
//. 比較関数
function compare( a, b ){ var r = 0; if( a < b ){ r = -1; } else if( a > b ){ r = 1; } return ( -1 * r ); } var array3 = [ 30, 29, 35 ]; array3.sort( compare ); //. => array3 = [ 35, 30, 29 ];

上記 JSON 配列のソートもこの比較関数を使った sort() の応用で可能になります。具体的には以下のように age 要素で大小比較するような比較関数を用意します:
//. 比較関数
function compare( a, b ){ var r = 0; if( a.age < b.age ){ r = -1; } else if( a.age > b.age ){ r = 1; } return r; }
var myArray = [ { id: 1, name: "鈴木", age: 30 }, { id: 2, name: "田中", age: 29 }, { id: 3, name: "佐藤", age: 35 }, : ]; myArray.sort( compare );

これだけ。応用も簡単にできそうで超便利。


Node.js のアプリケーションで、以下のような処理を実装してみました:
 - 認証(ログイン)用の API には誰でもアクセスできる
 - 認証 API では ID とパスワードを与えて認証し、正しいユーザーにはトークンを発行する
 - 認証以外の主な API はこの発行されたトークンを使ってアクセスした時だけ実行を許可する
 - API 呼び出し時にトークンがなかったり、正しくなかった場合は実行せずにエラー


この仕組を実現するために JSON Web Tokens (以下 "JWT")を使いました:
https://jwt.io/

2017081400


JWT はオープンかつ Node.js ではスタンダードなトークンベースの認証ライブラリです。以下で紹介するサンプルでは Web フレームワークである Express や、POST データを扱う body-parser も合わせて使うので、まとめてインストールしておきます:
$ npm install express body-parser jsonwebtoken

これらのライブラリを使って以下のようなアプリケーションを作成します:
//. app.js

var express = require( 'express' );
var app = express();
var bodyParser = require( 'body-parser' );
var jwt = require( 'jsonwebtoken' );

//. アプリケーションサーバーの稼働ポート番号
var port = process.env.PORT || 8080

//. 任意のシークレット文字列を登録
app.set( 'superSecret', 'welovenodejs' );  //. 任意の文字列

app.use( bodyParser.urlencoded( { extended: false } ) );
app.use( bodyParser.json() );

//. ユーザー情報(本来は DB などに格納された情報を使う)
var users = [
  { name: 'user0', password: 'pass0', admin: true },
  { name: 'user1', password: 'pass1', admin: false },
  { name: 'user2', password: 'pass2', admin: false },
  { name: 'user3', password: 'pass3', admin: false }
];

//. ドキュメントルートへの GET は許可
app.get( '/', function( req, res ){
  res.send( 'Hello. The API is at http://localhost:' + port + '/api' );
});

//. API ROUTES
var apiRoutes = express.Router();

//. トークンなしでアクセスを許可する API を先に定義する

//. POST(http://localhost:8080/api/authenticate)
apiRoutes.post( '/authenticate', function( req, res ){
  for( var i = 0; i < users.length; i ++ ){
    if( users[i].name == req.body.name && users[i].password == req.body.password ){
      //. 認証したユーザーの情報を使ってトークンを生成
      var token= jwt.sign( users[i], app.get( 'superSecret' ), {
        expiresIn: '24h'
      });
      res.json( { success: true, message: 'Authentication successfully finished.', token: token } );
      return;
    }
  }

  res.json( { success: false, message: 'Authentication failed.' } );
  return;
});

//. ここより上で定義した API には認証フィルタはかけていない(そのまま使える) //. 認証フィルタ apiRoutes.use( function( req, res, next ){
//. ポスト本体、URLパラメータ、HTTPヘッダいずれかにトークンがセットされているか調べる var token = req.body.token || req.query.token || req.headers['x-access-token']; if( !token ){
//. トークンが設定されていなかった場合は無条件に 403 エラー return res.status(403).send( { success: false, message: 'No token provided.' } ); } //. 設定されていたトークンの値の正当性を確認 jwt.verify( token, app.get( 'superSecret' ), function( err, decoded ){ if( err ){ //. 正当な値ではなかった場合はエラーメッセージを返す return res.json( { success: false, message: 'Invalid token.' } ); } //. 正当な値が設定されていた場合は処理を続ける req.decoded = decoded; next(); }); }); //. 以下はトークンがないと使えない API //. GET(http://localhost:8080/api/) apiRoutes.get( '/', function( req, res ){ res.json( { message: 'Welcome to API routing.' } ); }); //. GET(http://localhost:8080/api/users) apiRoutes.get( '/users', function( req, res ){ res.json( users ); }); //. /api 以下に API をルーティング app.use( '/api', apiRoutes ); app.listen( port ); console.log( 'server started http://localhost:' + port + '/' );

肝になるのはルーティングに認証フィルタを定義している箇所です。ここよりも前(上)で定義した内容には認証フィルタは有効にならないので、認証なしで使える API となります(つまり GET / と POST /authenticate は認証していなくても使えます)。

この POST /api/authenticate API でポストデータ user, password を受取り、その値が変数 users の中で定義されているいずれかの組み合わせと一致していれば 24H 有効なトークンが発行されます(これを以下の API 実行時にパラメータ指定します)。

一方、ここよりも後(下)で定義する内容には認証フィルタが有効になり、GET /api と GET /api/users は上記方法で取得したトークンがパラメータに設定されていないと正しく処理されない API となります。


実際にこのアプリケーションを実行し($ node app)、curl コマンドで挙動を確認してみましょう。まずは問題なく実行できるはずの GET / を実行します:
$ curl -XGET 'http://localhost:8080/'
Hello. The API is at http://localhost:8080/api

問題なく実行できました。次は認証フィルタをかけた GET /api を実行してみます:
$ curl -XGET 'http://localhost:8080/api'
{"success":false,"message":"No token provided."}

API を実行した結果、「トークンがない」時のエラーメッセージが表示されました。ここも期待通りに動いています(試しませんが、ユーザー一覧を取得する GET /api/users も同様のエラーになります)。

では POST /api/authenticate で認証してトークンを取得してみますが最初はわざとパスワードを間違えてみます:
$ curl -XPOST -H 'Content-Type:application/json' 'http://localhost:8080/api/authenticate' -d '{"name":"user1","password":"pass0"}'
{"success":false,"message":"Authentication failed."}

先程同様にエラーになりましたが、エラーメッセージが「認証失敗」に変わりました。では改めて正しいパスワードを指定して実行してみます:
$ curl -XPOST -H 'Content-Type:application/json' 'http://localhost:8080/api/authenticate' -d '{"name":"user1","password":"pass1"}'
{"success":true,"message":"Authentication successfully finished.","token":"XXXXXX...XXXXXX"}

今度は認証が成功しました。レスポンスにも "token" が含まれています。最後にここで返ってきた token の値を指定して、先程アクセスできなかった GET /api を実行してみます:
$ curl -XGET 'http://localhost:8080/api?token=XXXXXX...XXXXXX'
{"message":"Welcome to API routing."}

今度は API が実行できました。同様にして GET /api/users も実行できるはずです:
$ curl -XGET 'http://localhost:8080/api/users?token=XXXXXX...XXXXXX'
[{"name":"user0","password":"pass0","admin":true},{"name":"user1","password":"pass1","admin":false},{"name":"user2","password":"pass2","admin":false},{"name":"user3","password":"pass3","admin":false}]


こんな感じで API の実行可否をトークンで制御できるようになりました。

今回の例ではソースコード内に静的に用意されたユーザー一覧を使って認証を行いましたが、実際の運用ではデータベース内に定義されたテーブル情報などを使うことになると思います。ただ JWT の基本的な考え方はこの1つのソースファイルだけで実現できているので、応用しやすいと思っています。

 

このブログでも何度か Node.js のネタを扱ってますが、非同期処理に悩まされることが多いので、自分の理解の意味でもまとめておくことにしました。

まず理解の大前提として、Node.js はシングルスレッドで動作するため、いわゆる並列処理はできない仕様になっています。そして非同期に処理を実行することができます。これによって並列処理ができなくても、何かの時間のかかる処理があった場合にその終了を待たずに次の処理に進むことができることを意味しています。 ただ、この辺りがややこしく難解になっていることも事実です。

例えば REST API などの HTTP リクエストを行って、その結果が得られたら、その得られた結果の JSON オブジェクトの値を使って処理をする、などというよくあるケースでもこの問題に直面します。

まずは何が難解なのかを紹介します。REST API だとローカル環境で気軽に試せないので、わざと実行に時間のかかる関数を用意して説明します。

例えばこのような処理を考えてみます:
// test.js

// わざと1秒かけてから、パラメータの2倍の数値を出力する関数
function func1( x ){
  setTimeout( function(){
    console.log( 2 * x );
  }, 1000 );
}

// 初期値を設定して出力
var n0 = 10;
console.log( 'n0 = ' + n0 );

// 初期値を上記関数のパラメータに入れて実行
func1( n0 );

この中では func1 という関数を定義しています。setTimeout を使ってわざと1秒(1000ミリ秒)待ってから、パラメータの値の2倍を画面に出力する、という関数です。 この関数を n0 (=10 に設定)という変数をパラメータにして実行する、というものです。なので "20" という結果が出力されることを期待しています。

この内容を test.js というファイルに保存して、実行してみます(青字部分が出力結果):
$ node test
n0 = 10
20

期待通りに "20" と出力されました。とりあえずここまでは成功です。


さて問題はここからです。上記例では関数 func1 の中で console.log が実行されて出力までを行いました。これを func1 からは与えた数値を2倍した結果を受け取るようにして、func1 の外側で出力するように変更してみます。深く考えずにやるとこんな感じでしょか?
// test.js(注 正しく動きません)

// わざと1秒かけてから、パラメータの2倍の数値を出力する関数
function func1( x ){
  setTimeout( function(){
    console.log( 2 * x );
  }, 1000 );
}

// わざと1秒かけてから、パラメータの2倍の数値を戻す関数
function func2( x ){
  setTimeout( function(){
    return ( 2 * x );
  }, 1000 );
}

// 初期値を設定して出力
var n0 = 10;
console.log( 'n0 = ' + n0 );

// 初期値を上記関数のパラメータに入れて実行し、戻り値を出力
var n1 = func2( n0 );
console.log( 'n1 = ' + n1 );

func1 をほぼコピペして func2 という関数を作りました。console.log の代わりに return にして、値を返すようにしています。呼び出し元からはこの func2 を実行して、得られた結果を出力するようにしています。

これを先程と同様に実行するとこうなります:
$ node test
n0 = 10
n1 = undefined

n0(10)の値の2倍の "20" という結果を期待していたのですが、"undefined" と表示されてしまいます(正確に書くと、上記の2行はすぐに表示されますが、更に1秒くらい経過してから終了します)。この理由は最初に書いたように func2 は非同期に実行されているので、return の行が実行されるまでには1秒かかります。しかしその前に(return の行が実行される前に)関数そのものの処理は終了してしまいます。つまり値が戻る前に(戻っていない値を受け取ることになっている)n1 という変数を出力しているので "undefined" になっているのでした。

このように、期待通りに動かなかった理由は明白なのですが、ではどうすればこの関数が期待通りに動く(1秒後に与えられた結果を戻り値として戻し、受け取った側がその値を出力する)ようにできるでしょうか?これが今日紹介する大きなテーマです。


結論を先に紹介すると、ここで Promise オブジェクトを使って関数を修正し、受け取った側もその変更に合わせて一部書き直す必要があります。具体的には以下のように修正します:
// test.js

// わざと1秒かけてから、パラメータの2倍の数値を出力する関数
function func1( x ){
  setTimeout( function(){
    console.log( 2 * x );
  }, 1000 );
}

// わざと1秒かけてから、パラメータの2倍の数値を戻す関数
function func2( x ){
  return new Promise( function( resolve ){
    setTimeout( function(){
      resolve( 2 * x );
    }, 1000 );
  });
}

// 初期値を設定して出力
var n0 = 10;
console.log( 'n0 = ' + n0 );

// 初期値を上記関数のパラメータに入れて実行し、戻り値を出力
func2( n0 ).then( function( n1 ){
  console.log( 'n1 = ' + n1 );
});

まず関数 func2 側は、Promise オブジェクトを新規に作成します。Promise オブジェクトは処理が成功した場合の関数をパラメータに指定します。上図だと
function( resolve ){
  setTimeout( function(){
    resolve( 2 * x );
  }, 1000 );
}

という関数がパラメータに指定されているので、成功するとこの関数が実行されます(今回は使っていませんが、第二パラメータを指定した場合は失敗時に実行する処理を指定したことになります)。この処理の中で1秒待って、指定したパラメータを2倍して resolve とする、ということになります。

そしてこの関数 func2 を呼び出す側も少し変更が必要になります。 func2() 関数の実行結果をそのまま変数として受け取るのではなく、成功した場合(今回の例だと1秒待って2倍になった値が返された場合)の処理を .then() 内に渡して処理することになります。この then 内の処理で計算結果(resolve で処理された内容)を n1 という変数で受け取って console.log で表示する、という内容にしています。

こうして修正した test.js を実行すると、以下のような結果になります(実際には n0 = 10 がすぐに表示され、1秒くらい待ってから n1 = 20 の行が表示されて終了します):
$ node test
n0 = 10
n1 = 20

Node.js の関数内で非同期処理を実行して、その非同期処理の終了を待って値を受け取るような関数を作る場合は、Promise オブジェクトを使って上記のように記述します、という紹介でした。非同期実行に慣れていないと、この辺りで戸惑うことが多いと感じたので、まとめておきました。


IBM Bluemix を通じて提供されているコグニティブサービス(認識型人工知能)API の1つに Tradeoff Analytics があります:
2015051800


この API を理解するにはデモサイトを参照していただくのがいいと思います:
http://tradeoff-analytics-demo.mybluemix.net/

このデモサイトでは「一覧の中で、自分にあったスマホはどれか?」という命題を、その人が重要視するスペックポイントを明確にした上で候補を上げていく、というものです。

スマホはメモリは多い方がいいし、バッテリーは多い方がいいけど、そうすると重量が重くなります。でも重量は気にする人もいればいない人もいます。また価格は安い方がいいに決まっていますが、メモリやバッテリー、重量などの要素と比べて重要度が高いのか低いのか、は人によって異なってきます。 全てを満たすパーフェクトなスマホはそうそう出てこないでしょうから、結局はどの要素を重要視するかでトレードオフを考えることになると思いますが、どの要素を重要視した場合はどのような結果になるか、を判断してくれる、そんな API です。

例えば上記デモサイトを開くと、デフォルトでは以下の様な表が表示されます:
2015051801


一番上の列がヘッダで、2行目からがスマホの候補リストになっています。表の3列目以降がスペックになっていて、3列目は価格(小さい方がいい)、4列目はRAMメモリ量(多い方がいい)、5列目はスクリーンサイズ(意見が別れるところですが、ここでは大きい方がいい)、6列目はカメラの画素数(多い方がいい)、7列目はメモリ量(多い方がいい)、8列目はバッテリー容量(多い方がいい)、そして9列目は重量(少ない方がいい)となっています。このうちデフォルト状態では価格、スクリーンサイズ、重量の色が緑になっていて、今はこの3点を重要視する人にとってのトレードオフを解析する指定になっています。

もしもこれら以外の点を重要視したい場合は表右下の "View/Edit JSON" と書かれた箇所をクリックして JSON の内容を変更します。例えば「スクリーンサイズは気にしないけど、バッテリー容量は重要!」という場合は以下のように値を変更します。 まずスクリーンサイズは今回意識しなくていいので、JSON 内の columns の中で "key" 値が "screen_size" になっている項目の "is_objective" 値を true から false に変更します。この "is_objective" の値が true のものはトレードオフの考慮に含めて、false のものは含めない、という指定です。なお "goal" 値が "MAX" のものは「(メモリ量など)大きい方がいい」という値、"MIN" のものは「(価格など)小さい方がいい」という値だという指定です:
{
  "key": "screen_size",
  "full_name": "Screen size (inch)",
  "type": "NUMERIC",
  "is_objective": false,
  "goal": "MAX"
},

続いてバッテリー容量はトレードオフに新たに含めたいので、同様にして "key" 値が "battery" になっている項目の "is_objective" 値を false から true に変更します:
{
  "key": "battery",
  "full_name": "Battery (mAh)",
  "type": "NUMERIC",
  "is_objective": true,
  "goal": "MAX"
},

この状態で編集エリア右下の "Back to table" をクリックすると、価格とバッテリー容量と重量の3点が重要視指定された状態の表が表示されるはずです。なお、これら以外でも JSON を変更することで候補スマホの名称や各数値を変更することもできます。全て JSON フォーマットで指定されていることを確認しておいてください:
2015051802


重要視する項目が決まったら、画面右下の "Analyze Sample Data" と書かれたボタンをクリックして、トレードオフを計算します:
2015051803


すると画面下に以下のようなグラフが表示されて、指定した3項目でのトレードオフを考えた結果が表示されます。各スマホアイテムがそれぞれの要素を意識した時にどの位置にあるのか、ということが視覚化されています:
2015051804


例えば、ここでバッテリー項目の下限のスライダーを 1000 から 1500 近くまで移動させると、「バッテリー容量が指定値(1500)以下のものは考慮しない」という指定をしたことになり、画面上部付近にあったいくつかのバッテリー容量の少ないスマホが画面から消えます。これで更に絞込みやすくなります:
2015051805


・・・と、なんとなく「トレードオフ解析」の意味がお分かりいただけたでしょうか? IBM Bluemix で用意されているのは、このような解析を行うための(与えられた条件から、上記の結果グラフを書くための)API が用意されている、という点が特徴です。つまり上記デモサイトのようなことを皆さんの作るアプリケーションの中でも実現できるようになる、ということです。

実際にこの API を利用するには、IBM Bluemix で上記の Tradeoff Analytics サービスをランタイムアプリケーションにバインドして資格情報を確認し、利用のための username と password を取得しておく必要があります
2015051801


ここまで準備できれば API そのものは単純です。取得した username と password を使って Basic 認証を行い、https://gateway.watsonplatform.net/tradeoff-analytics-beta/api/v1/dilemmas に対して、解析させたい JSON(上記の Edit JSON をクリックして編集したもの)をポストするだけです。

成功すると、以下の様な JSON が結果として返ってきます:
{
  "problem": {
    "columns" : [ {
      "key" : "price",
      "format" : "",
        :
        (ポストした JSON の内容)
        :
  },
  "resolution" : {
    "solutions" : [ {
      :
      :
    } ],
    "map" : [ {
      "nodes" : [ {
        "coordinates" : { "x":0.0, "y":0.0 },
        "solution_refs" : []
      }, {
        "coordinates" : { "x":1.0, "y":0.0 },
        "solution_refs" : []
      }, {
        "coordinates" : { "x":2.0, "y":0.0 },
        "solution_refs" : []
      }, {
        "coordinates" : { "x":3.0, "y":0.0 },
        "solution_refs" : []
      }, {
        "coordinates" : { "x":4.0, "y":0.0 },
        "solution_refs" : []
      }, {
        "coordinates" : { "x":5.0, "y":0.0 },
        "solution_refs" : []
      }, {
        "coordinates" : { "x":6.0, "y":0.0 },
        "solution_refs" : [ "5" ]
      }, 
        :
        :
      } ],
      "anchors" : [ {
        "name" : "price",
        "position" : { "x":0.0, "y":0.0 }
      }, {
        "name" : "battery",
        "position" : { "x":4.5, "y":7.8 }
      }, {
        "name" : "weight",
        "position" : { "x":9.0, "y":0.0 }
      } ],
        :
        :
    }
  }
}

結果の JSON の細かい内容は以下のリファレンスを参照していただきたいのですが、大きく3つの箇所を参照することになります。 1つ目は "problem" で、この中にポストした JSON がそのまま記述されているはずです。つまり「この問題に対する実行結果である」ことを示しています。

2つ目は "resolutions" の中の "map" で、ここに散布図を書いた時の各点の座標(x,y)が示されており、またその座標位置に相当するアイテムがあった場合は "solutions_refs" という配列値で示されます。例えば上の例ですと ( 6.0, 0.0 ) の位置に "key" 値が "5" に相当するアイテムがある、ということになります。

最後の3つ目は "resolutions" の中の "anchors" で、これが指定した各トレードオフ要素(この例では価格とバッテリー容量と重量)が散布図上のどの位置にあるかを示しています。


後はこの結果を受け取った側でどのように見せるか、という問題になります。チャート描画の API を使ってもいいし、Canvas を使って自分で記述してもいいと思います。ここから先は開発者やデザイナーの腕の見せ所と言えます。そういう意味ではここのデモサイトはなかなか良くできてますよね。


というわけで、これまでにこのブログで紹介してきたコグニティブサービスとは少し(特に実行方法の面で)毛色が違うサービスと言えます。API 実行時に指定フォーマットの JSON を作る必要があるのが少し面倒ですが、トレードオフを意識する要素と、それらの値は大きい方が嬉しいのか小さい方が嬉しいのかの情報、そしてアイテムの一覧を JSON にしてポストすれば結果が返ってくる、というものです。REST API そのものはシンプルなのでエクセルのような表計算からも実行できると思っています(エクセルだと逆に結果の視覚化が難しいかも・・)。


なお、ここで紹介した Watson Tradeoff Analytics API について、詳しくは API リファレンスを参照してください:
http://www.ibm.com/smarterplanet/us/en/ibmwatson/developercloud/apis/#!/tradeoff-analytics
 

このページのトップヘ