Block Rockin’ Codes

back with another one of those block rockin' codes

JSON - を node の Stream で整形する

intro

ちょっと反応が遅れてしまいましたが。


404 Blog Not Found:JSON - をnodeで整形する


こちらの記事は Stream 厨として見逃す訳にはいきませんでした。

motivation

まあ、 JSON ですしね。

  • そのJavaScriptに今や標準搭載のJSON.stringify()は実はpretty printできるし

確かに stringify でできますね。(昔こちらにも書きました。)

  • どうせなら標準入力だけではなくURIやファイル名で直アクセスしたいし

同意です。


しかし、実装を見てみると、、
(以下、主要な部分の抜粋)

// stdout
stdin.on("data", voorhees);
// http
http.get(req, function (res) {
  res.on('data', function (data) {
    chunks.push(data)
  }).on('error', function (e) {
    console.log(e.message);
  }).on('end', function () {
    voorhees(chunks.join(''))
  });
});
// fs
fs.readFile(argv[2], function (err, data) {
  if (err) throw err;
  voorhees(data);
});


実は、こうした「データソースから受け取ったデータを加工して、次に渡す」といったものこそ、 Stream に向いています。
正確には、今回の用途は「データをまとめて扱っている」ので、 Stream にする必要はかならずしもないです。


しかし、 Stream にしておくと以下のようなメリットがあるかと思います。

  • すでに Stream として抽象化されているものとの接続製があがる。(substackdominic のライブラリ集が使える!)
  • うまくやれば、 JSON のストリーム(次々と途切れなく流れてくる JSON の配列とか) を、途中途中で Stringify しながら画面表示したりできる。
  • voorhees は CLI を提供するフロントの層と JSON を整形するロジックの層が1つになっています(それ自体をどうこう言う気はないです) が、こうしたレイヤの分離も Stream を使うとうまくいったりします。


そしてなにより、先の 3 つのリソース(stdin/out, file, http) は全て、標準ですでに Stream として抽象化されています。


乗るしか無い、このエコシステムに!

Transform Stream

今回の用途だと、 Transform Stream にすることになります。特徴だけ説明します。


今回は、チャンクごとに処理せず、まとめて最後に処理するので、 _transform() はデータを溜めるだけで、 _flush() で全部 stringify() 処理します。

JSPP.prototype._transform = function(chunk, output, cb) {
  chunk = chunk.toString();
  if (chunk) {
    this.data += chunk;
  }
  return cb(null);
};

JSPP.prototype._flush = function(output, cb) {
  if (this.data) {
    try {
      output(this._pp(this.data));
    } catch(e) {
      return cb(e);
    }
  }
  cb(null);
};


使う場合は、 pipe するだけです。

// 標準入出力の場合
process.stdin.pipe(jspp).pipe(process.stdout);


// http の場合
http.get(url, function (res) {
  res.pipe(jspp).pipe(process.stdout);
});


// ファイルの場合
fs.createReadStream(file).pipe(jspp).pipe(process.stdout);


こうして、標準モジュールや、他の Stream を実装したライブラリと、好き放題繋ぐ事ができます。

jspp-stream

ということで、以前作った ltsv-stream をちょちょっといじって、 jspp(json pritty print) stream ということで実装してみました。


https://github.com/Jxck/jspp-stream


Stream 実装(jspp-stream.js)だけでなく、これを使った voorhees 互換の jspp コマンドも同梱してあるので、 npm -g で入れれば使えます。
(こうして、ロジックの層とフロントの層が簡単に分けられるのも、 Stream を使うメリットの1つです)

$ npm install -g jspp-stream
$ cat test.json | jspp
$ jspp /path/to/file.json
$ jspp http://some.json.com


テストもエラー処理も適当ですが、まあこんな感じでできますよということで。