9/21のアップデートで、それまで有料だった Private Node 機能がFreeプランでも使用できるようになりました。この記事では、 Private Node 機能を使って、ExcelファイルをJSONオブジェクトに変換するノードを作ってみます。
目次
- はじめに
- xlsxモジュール
- Private Node 作成の注意事項
- Private Node の雛形を作る
- HTMLファイルを作る
- JavaScriptファイルを作る
- enebularに Private Node を登録する
- 使ってみる
- おわりに
はじめに
この記事で作るものは、Excelファイルを読み込んで、JSONオブジェクトを出力(変換)するPrivate Nodeです。
記事「設定ファイルをIoTデバイスにデプロイする方法」では、オープンデータAPIを利用して、新型コロナウィルス感染症の感染者数データを取得しました。この記事を書くために、デジタル庁が運営するデータカタログサイト “data.go.jp”を眺めていたのですが、現状ではPDF形式やExcel形式(xls, xlsx共)のデータがまだまだありますね。
せっかく公開されているデータも取り扱えないと、役に立てることができません。そこで、ExcelファイルをJSONオブジェクトに変換するノードを作ろうと思いつきました。
xlsxモジュール
ExcelファイルをJSONオブジェクトに変換するといっても、一から作るのは容易ではありません。
しかし、Node-REDユーザーには強い味方 “NPM” があります。ここで、”Excel” を検索してみると、 “xlsx” というモジュールが見つかりました(他にもたくさんのExcelを扱うモジュールが見つかります。Parser機能を持つ人気がありそうなモジュールを選んでいます)。
ドキュメントを読むと便利に使えそうです。本記事では、このxlsxモジュールを利用してノードを作ってみます。
Private Node 作成の注意事項
enebularのPrivate Nodeの作り方は、基本的にはNode-REDのノードの作り方と同じです。enebularのマニュアルには注意点として、以下の4点があげられています。
- パッケージ名(package.jsonに記載するnameの値)はenebular-privatenode-contrib-から始まること
- プロジェクトに同名のパッケージ名のPrivate Nodeが登録されていないこと
- パッケージファイルのサイズは2MB以下であること
- Private NodeのNode Typeと同名のNode TypeがNode-REDのパレットに存在しないこと
以上を踏まえて、今回作るノードの名称を以下のように決めます。ノードタイプは、エディタ上で見えるノードの名前のことです。
モジュール名: enebular-privatenode-contrib-xlsx2json
ノードタイプ:xlsx2json
開発の準備として、Node.js、npmコマンド、そしてもちろんNode-REDのインストールが必要ですが、ここではリンクを紹介するだけで省略させていただきます。
- Node.jsダウンロードページ … 自身の開発環境に合わせてインストーラーをダウンロードしてインストールしてください。npmコマンド(Node Package Manager)はNode.jsをインストールすると一緒にインストールされているはずです
- Node-REDのインストール … 環境毎のNode-REDのインストール方法が説明されています
Private Node の雛形を作る
では、ノードを作り始めます。まずは、開発用のフォルダを作ります。
mkdir enebular-privatenode-contrib-xlsx2json
cd enebular-privatenode-contrib-xlsx2json
作ったフォルダに入って、最初の設定を行います。
npm init
以下のような感じで設定を行います。
package name: (enebular-privatenode-contrib-xlsx2json) enebular-privatenode-contrib-xlsx2json
version: (1.0.0) 0.1.0
description: This module convert Excel files from binary to json object.
entry point: (index.js)
test command:
git repository:
keywords: Node-RED, enebular, Excel, JSON
license: (ISC)
About to write to /hoge/hige/huge/enebular-privatenode-contrib-xlsx2json/package.json:
{
"name": "enebular-privatenode-contrib-xlsx2json",
"version": "0.1.0",
"description": "This module convert Excel files from binary to json object.",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [
"Node-RED",
"enebular",
"Excel",
"JSON"
],
"author": "Fuga Fuga <fuga.fuga@funga.jp>",
"license": "ISC"
}
Is this OK? (yes)
最初のpackage.jsonファイルが出来上がります。
さらに、package.jsonに "node-red"
キーを追加する必要があります。
{
"name": "enebular-privatenode-contrib-xlsx2json",
...省略...
"license": "ISC",
"node-red": {
"nodes": {
"xlsx2json": "xlsx2json.js"
}
}
}
次に、xlsx
モジュールをインストールします。enebular-privatenode-contrib-xlsx2json
フォルダ内で以下のようにコマンドを実行してください。--save
オプションは忘れずに!
これで、xlsx
モジュールが追加され、さらにpackage.json
のdependencies
にxlsxモジュールが追加されます。
npm install xlsx --save
次に、同じフォルダにHTMLファイル xlsx2json.html
とJavaScriptファイル xlsx2json.js
を作成します。
Node-REDのドキュメント ノードの開発・初めてのノード開発 には、親切にも雛形になるHTMLファイル(lower-case.html
)とJavaScript(lower-case.js
)のコードが記述されています。xlsx2json.html
と xlsx2json.js
の中身は、一旦、この雛形のコピーにしてしまいましょう。
HTMLファイルを作る
HTMLファイルから作ります。
先程のコピーで、xlsx2json.html
の中身は以下のようになっているはずです。
<script type="text/javascript">
RED.nodes.registerType('lower-case',{
category: 'function',
color: '#a6bbcf',
defaults: {
name: {value:""}
},
inputs:1,
outputs:1,
icon: "file.png",
label: function() {
return this.name||"lower-case";
}
});
</script>
<script type="text/html" data-template-name="lower-case">
<div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
</script>
<script type="text/html" data-help-name="lower-case">
<p>A simple node that converts the message payloads into all lower-case characters</p>
</script>
では、xlsx2json
用に、これを改造していきます。
まず、lower-case
というノード名を全て xlsx2json
に変更します。4箇所ありますから漏れないように注意してください。
次にノードのプロパティを設定していきます。category
は'function'
から'parser'
に変更しましょう。color
も'parser'
カテゴリーの他のノードに合わせて、'#DEBD5C'
に変更しましょう。defaults
では、変換するExcelファイルのファイル名(パス)を設定するfilename
を追加します。以下のように書き換えます。
defaults: {
name: {value:""},
filename: {value:""}
},
次にノードの編集ダイアログ(data-template-name="xlsx2json"
のところ)を修正します。以下の4行を追加してください。これで、Node-REDのエディタ画面で、このノードの編集ダイアログを開くとFilename
が編集できるようになります。
<div class="form-row">
<label for="node-input-filename"><i class="fa fa-tag"></i> Filename</label>
<input type="text" id="node-input-filename" placeholder="Filename">
</div>
最後にヘルプメッセージ(data-help-name="xlsx2json"
のところ)を編集します。
<p>A simple node that converts a Excel files from binary to json object.</p>
最終的には以下のようなHTMLファイルになります。
<script type="text/javascript">
RED.nodes.registerType('xlsx2json',{
category: 'parser',
color: '#DEBD5C',
defaults: {
name: {value:""},
filename: {value:""}
},
inputs:1,
outputs:1,
icon: "file.png",
label: function() {
return this.name||"xlsx2json";
}
});
</script>
<script type="text/html" data-template-name="xlsx2json">
<div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
<div class="form-row">
<label for="node-input-filename"><i class="fa fa-tag"></i> Filename</label>
<input type="text" id="node-input-filename" placeholder="Filename">
</div>
</script>
<script type="text/html" data-help-name="xlsx2json">
<p>A simple node that converts a Excel files from binary to json object.</p>
</script>
JavaScriptファイルを作る
いよいよJavaScriptファイルを作ります。
先程のコピーで、xlsx2json.js
の中身は以下のようになっているはずです。
module.exports = function(RED) {
function LowerCaseNode(config) {
RED.nodes.createNode(this,config);
var node = this;
node.on('input', function(msg) {
msg.payload = msg.payload.toLowerCase();
node.send(msg);
});
}
RED.nodes.registerType("lower-case",LowerCaseNode);
}
まずは、ノードの名前を変更しましょう。最後の行の"lowar-case"
を"Xlsx2Json"
にします。
また、関数名LowerCaseNode
をXlsx2JsonNode
に変更しましょう。これは2箇所に出てきますので、両方修正してください。2回目のところは、Node-REDにノードを登録する処理です。
次にxlsx
モジュールを使えるように、1行目でrequire
しておきましょう。
const XLSX = require('xlsx');
module.exports = function(RED) {
...
}
エディタで設定するファイル名(Filename)を取得します。先ほど、HTMLファイルで設定しましたね。
module.exports = function(RED) {
function Xlsx2JsonNode(config) {
RED.nodes.createNode(this,config);
this.filename = config.filename;
...
config.filename
にファイル名が入っています(エディタで設定していれば)。
いよいよ、Xlsx2Json
ノードにmsg
が入力された時の処理を書きます。node.on
の処理を丸ごと書き換えます。
node.on('input', function(msg, send, done) {
send = send || function() { node.send.apply(node,arguments) };
if (node.filename) {
try {
const workbook = XLSX.readFile(node.filename);
msg.payload = XLSX.utils.sheet_to_json(workbook.Sheets[workbook.SheetNames[0]]);
}
catch(err) {
msg.payload = {
"error": err,
"filename": node.filename,
};
}
} else {
msg.payload = { "error": "no filename" };
}
node.send(msg);
if (done) {
done();
}
});
以下の2行が、Excelファイルを開いて、JSONオブジェクトに変換する処理です。xlsx
モジュールが実行してくれるのでたった2行です。
const workbook = XLSX.readFile(node.filename);
msg.payload = XLSX.utils.sheet_to_json(workbook.Sheets[workbook.SheetNames[0]]);
最終的なファイルは以下のようになります。
const XLSX = require('xlsx');
module.exports = function(RED) {
function Xlsx2JsonNode(config) {
RED.nodes.createNode(this,config);
this.filename = config.filename;
const node = this;
node.on('input', function(msg, send, done) {
send = send || function() { node.send.apply(node,arguments) };
if (node.filename) {
try {
const workbook = XLSX.readFile(node.filename);
msg.payload = XLSX.utils.sheet_to_json(workbook.Sheets[workbook.SheetNames[0]]);
}
catch(err) {
msg.payload = {
"error": err,
"filename": node.filename,
};
}
} else {
msg.payload = { "error": "no filename" };
}
node.send(msg);
if (done) {
done();
}
});
}
RED.nodes.registerType("xlsx2json",Xlsx2JsonNode);
}
enebularに Private Node を登録する
ここで、一旦テストをしたいところですが、ちゃんと動作すると信じてPrivate Node化しましょう。(Node-REDのドキュメント はじめてのノード開発 には「Node-REDでノードの動作確認」という章があって、動作確認の方法を教えてくれます。そちらを参照してください!!)
ここからは、基本的にはenebularのマニュアルに従うだけです。enebular-privatenode-contrib-xlsx2json
フォルダ内で以下のようにコマンドを実行してください。
npm pack
enebular-privatenode-contrib-xlsx2json-0.1.0.tgz
というファイルができているはずです。このファイルをenebularに登録します。
enebularにSign-Inして、プロジェクトを開きます(好きなプロジェクトを開いてください)。右下の+ボタンを押して、Create Asset
ダイアログを開きます(下図)。Asset Type
をprivatenode
にします。アセット名はなんでも良いのですが、ここでは xlsx2json
としましょう。また、ここでは一旦、Description
とDefault Privilege
はデフォルトのままとします。
最後に、「Drag and drop your Private Node File(tgz) or select it」と書かれた部分に、先ほどの enebular-privatenode-contrib-xlsx2json-0.1.0.tgz
ファイルをドラッグ&ドロップします。これで準備は完了です。ダイアログ下部のUpload
ボタンを押してください。
以上で、Private Nodeの登録は完了です。
これ以降は、このプロジェクトで作るフローには、登録したXlsx2Json
ノードが使えるようになります。
使ってみる
では、いよいよPrivate Node "Xlsx2Json"
を使ってみましょう。
Private Nodeを登録したenebularのプロジェクトの中に、フローを新規作成します。フローの名前は何でも良いのですが、ここではxlsx2json-flow
という名前にします。フローエディタを開きます。
ノードのリストに、xlsx2json
ノードが登録されているはずです。
このノードを使って作るのは以下のフローです。
以下のJSONファイルをインポートすると、上図のフローがインポートできます。
[{"id":"5554444f.0eef5c","type":"tab","label":"Flow 1","disabled":false,"info":""},{"id":"9d08eb63.43f558","type":"xlsx2json","z":"5554444f.0eef5c","name":"","filename":"test.xlsx","x":400,"y":200,"wires":[["a7441d02.0d52"]]},{"id":"81f73788.75bb38","type":"inject","z":"5554444f.0eef5c","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":220,"y":140,"wires":[["b95f6ef4.47ec1"]]},{"id":"4bf6c22a.c0056c","type":"debug","z":"5554444f.0eef5c","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":790,"y":140,"wires":[]},{"id":"de2e8028.7ff2a","type":"inject","z":"5554444f.0eef5c","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":220,"y":200,"wires":[["9d08eb63.43f558"]]},{"id":"d5b5a5c4.cd4608","type":"file","z":"5554444f.0eef5c","name":"","filename":"test.xlsx","appendNewline":true,"createDir":false,"overwriteFile":"true","encoding":"none","x":600,"y":140,"wires":[["4bf6c22a.c0056c"]]},{"id":"6a087103.8934c","type":"debug","z":"5554444f.0eef5c","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":790,"y":200,"wires":[]},{"id":"b95f6ef4.47ec1","type":"http request","z":"5554444f.0eef5c","name":"","method":"GET","ret":"bin","paytoqs":false,"url":"https://www.mext.go.jp/content/20201221-mxt_syogai03-000010378_1.xlsx","tls":"","persist":false,"proxy":"","authType":"","x":410,"y":140,"wires":[["d5b5a5c4.cd4608"]]},{"id":"a7441d02.0d52","type":"split","z":"5554444f.0eef5c","name":"","splt":"\\n","spltType":"str","arraySplt":1,"arraySpltType":"len","stream":false,"addname":"","x":590,"y":200,"wires":[["6a087103.8934c"]]}]
このフローでは以下の2つを行うことができます。
- data.go.jpに登録されていた「オープンデータ・教材リスト情報」をダウンロードして、
"test.xlsx"
というファイルにして保存する - 保存した
"test.xlsx"
ファイルを読み込んで、Xlsx2Json
ノードでJSONオブジェクトに変換して、Debugノードに出力する
1の処理が画像の上の列の処理、2の処理が画像の下の列の処理になります。
2つの列ともに、左端にInject
ノードがあります。エディタを開いて、Inject
ノードを押すとフローが実行されます。上の列から順番に実行してください。 Debugウィンドウにそれぞれの実行結果が出力されます。
おわりに
この記事では、enebularのPrivate Nodeとして Xlsx2Json
ノードを作ってみました。xlsx
モジュールを使うことで、とても簡単に新しいノードが作れることがわかっていただけたのではないかと思います。
オリジナルのノードの開発というと、とてもハードルが高く感じられるかもしれません。ところが、この記事のノード開発では、xlsx
モジュールを見つけてくるところと、使い方を理解するところが一番大変だったのです。xlsx
モジュールはよくできているモジュールなので、ここまで来れば後は出来たも同然です。
ところで、今回作った Xlsx2Json
ノードには、まだまだ改良の余地があります。上のフローの図を見ても違和感があるように、どうせなら Http-Request
ノードの出力をそのままXlsx2Json
ノードに入力できるようにしたいですよね。この続きは、機会があればまた記事にしたいと思います。では、みなさん、次の記事で会いましょう!!!