Node.jsのexec実行時にPythonプログラムへJSONデータを引数で渡すときurlparseデコードが便利だったメモです。
背景
Node.jsからPythonプログラムに対してデータを送るときに引数でデータを渡したくなります。
単純にシェルで実行するならいいんです。
python recieve.py "ABC ABC ABC"
たとえば、スペース付きの文字列だって、ダブルクオーテーションで囲えば無事送られますし。
python recieve.py "{\"message\":\"ABC ABC ABC\"}"
JSONデータだってエスケープすればなんとか行ける。
ですが、このコマンドをNode.js内部のexec実行するとなると、Node.js内部も噛んだエスケープをしつつの引数エスケープ(すでに何を言っているか若干不安)となり、実際、かなりままならなくなります。
頑張ればできなくもないが、結構苦労するイメージ。(込み入った具体例となると大変に分かりにくいのでここでは割愛します。)
それが、
- Node.js exec で引数を渡すときは encodeURIComponent でエンコード
- Pythonで受け取るときは urlparse でデコードしたのちにJSON.loadでデータ化
がうまくいったメモです。
今回のスペック
最近、Node-REDでPythonの連携をすることが多いので、以下で試しています。
- 実行環境
- Raspberry Pi
- Pythonのバージョン
- Raspberry PiのPython 2
- たぶん 3 でも行けるので PythonのURLとクエリパラメータのパースまとめ のQiita記事を参考に置き換えてみてください。
- Raspberry PiのPython 2
- そのほかテストした環境
- Windows 10 のPython2実行でもうまくいきました
Node.js 側 sample.js
Node.js 側は以下の通りです。同階層のrecieve.pyに対して第一引数でデータを送っています。
const path = require('path'); const exec = require('child_process').exec; // データ let payload = { "status":"random", "tree": [ [255, 0, 0], [255, 255, 0], [255, 0, 255] ] }; // JSONを文字列にしたあと、encodeURIComponentでエンコード let payloadStr = encodeURIComponent(JSON.stringify(payload)); // コマンド作成 var cmd = 'python -u ' + path.join(__dirname, 'recieve.py') + ' "' + payloadStr + '"'; // 実際に送る exec(cmd, (err, stdout, stderr) => { if (err) { console.log(err); } console.log(stdout); });
Python 側 recieve.py
受け取る側のPythonのソースです。
import sys import json import urlparse def main(): args = sys.argv # urlparse で Node.js でエンコードされた文字列をデコードして戻す estr = urlparse.unquote(args[1]) # そのうえでJSON化 datas = json.loads(estr) # print してデータを Node.js に返答 print('### datas["tree"]') print(datas["tree"]) print('### datas["tree"][2]') print(datas["tree"][2]) print('### datas["status"]') print(datas["status"]) if __name__ == '__main__': main()
繰り返しになりますが、たぶんPythonバージョン3 でも行けるので PythonのURLとクエリパラメータのパースまとめ のQiita記事を参考に置き換えてみてください。
実行してみる
実行してみると、エスケープを自前でやる必要なく、JSONデータが読めてます。
日本語のデータや大きなJSONデータを渡すとどうなるかは試してないですが、英数字で小さく渡す分には問題なく動いているので、今後使っていこうと思っています!