はじめまして。Glx64xと申します。
突然ですが、JScript+MSXMLって便利ですよね!
XMLをちょっといじりたいとき、Windows標準機能だけでJavaScriptの文法でお手軽にDOM操作ができるのは非常にありがたいです。
Wineが入っていれば、Wineにもcscript.exeとjscript.dll(SpiderMonkeyベースらしい)が同梱されているため、Windows以外の環境でも割と動いちゃいます。
しかし、そんなJScriptも今や古い技術。
UTF-8のような文字コードは扱いにくいのが現状です。
また、もしこれをサーバー上で使う場合、WindowsサーバーならまだしもLinuxサーバー上でWineを動かすのは抵抗があります。
そこで、今回は簡単なスクリプトを例に、JScriptで作成したスクリプトをNode.js+xmldomに移植してみたいと思います。
なぜxmldom?
おおむねW3C標準ベースで、使い方が調べやすいと思ったからです。
xmldomの導入
Node.js及びnpmの導入方法は割愛します。
npm install xmldom
また、Shift_JISなど、UTF-8以外の文字コードを扱う場合、iconv-liteもあると便利です。
npm install iconv-lite
サンプルコード
今回はサンプルのXMLとして、Inkscapeで作成したものをテキストエディタでShift_JISに変換した以下のSVGを用意しました。(SVGの中身はXMLです。)
(TechRachoにはSVGを張ることができないため、pngに変換してあります。)
JScriptを使って画像中の「こんにちは」という文字を「こんばんは」に書き換え、該当する要素のIDを表示するスクリプトを以下に示します。
移植前(JScript)のコード
"use strict";
var dom = new ActiveXObject("MSXML2.DOMDocument");
dom.load("sample_sjis.svg");
if (dom.parseError.errorCode === 0) {
var tspans = dom.documentElement.getElementsByTagName("tspan");
for (var i = 0; i < tspans.length; i++) {
if (tspans[i].text === "こんにちは") {
WScript.Echo(tspans[i].getAttribute("id"));
tspans[i].text = "こんばんは";
}
}
dom.save("output.svg");
} else {
// Error
}
実行結果
tspan5133
移植
では、このコードをNode.js+xmldomに移植していきましょう。
まず変更した箇所だけを見ていきます。
ファイルの読み込み
JScript
var dom = new ActiveXObject("MSXML2.DOMDocument");
dom.load("sample_sjis.svg");
Node.js+xmldom
// パッケージ/モジュールの読み込み
var fs = require("fs");
var DOMParser = require("xmldom").DOMParser;
var iconv = require("iconv-lite");
// ファイルの読み込み
var file = fs.readFileSync("sample_sjis.svg");
var text = iconv.decode(file, "Shift_JIS");
// 読み込んだファイルをDOMParserにかける
var dom = new DOMParser().parseFromString(text, "application/xml");
一気に長くなりましたが、長くなるのは基本最初のモジュールやファイルの読み込みと最後のファイル書き込みだけですのでご安心ください。
JScriptではDOMDocumentにload
メソッドが生えていますが、Node.js+xmldomの場合fs.readFileSync
を使ってファイルを読み込む感じですね。
iconv-liteについては、対象のXMLファイルがUTF-8なら必要ありません。
エラー判定
JScript
if (dom.parseError.errorCode === 0) {
Node.js+xmldom
if (dom.documentElement !== null) {
MDNの説明だとDOMParserはエラーのときエラードキュメントを返すようですが、xmldomのDOMParserからはnullが返ってくるようです。
テキストノードの取得/設定と標準出力
JScript
if (tspans[i].text === "こんにちは") {
WScript.Echo(tspans[i].getAttribute("id"));
tspans[i].text = "こんばんは";
}
Node.js+xmldom
if (tspans[i].textContent === "こんにちは") {
console.log(tspans[i].getAttribute("id"));
tspans[i].textContent = "こんばんは";
}
JScriptでは要素.text
、xmldomでは要素.textContent
でテキストノードにアクセスできます。
また、標準出力はJScriptではWScript.Echo
(cscript.exeで実行している場合)ですが、Node.jsではconsole.log
です。
Node.jsのほうはブラウザとおなじですね。
ファイル書き出し
JScript
dom.save("output.svg");
Node.js+xmldom
fs.writeFileSync("output.svg",
iconv.encode(dom, "Shift_JIS"),
{encoding: null});
JScriptではDOMDocumentにsave
メソッドが生えていますが、Node.js+xmldomの場合fs.writeFileSync
に出力するオブジェクトを渡してあげる感じです。
移植後(Node.js+xmldom)のコード全体
"use strict";
var fs = require("fs");
var DOMParser = require("xmldom").DOMParser;
var iconv = require("iconv-lite");
var file = fs.readFileSync("sample_sjis.svg");
var text = iconv.decode(file, "Shift_JIS");
var dom = new DOMParser().parseFromString(text, "application/xml");
if (dom.documentElement !== null) {
var tspans = dom.documentElement.getElementsByTagName("tspan");
for (var i = 0; i < tspans.length; i++) {
if (tspans[i].textContent === "こんにちは") {
console.log(tspans[i].getAttribute("id"));
tspans[i].textContent = "こんばんは";
}
}
fs.writeFileSync("output.svg",
iconv.encode(dom, "Shift_JIS"),
{encoding: null});
} else {
// Error
}
実行結果
tspan5133
まとめ
基本的にxmldomについては、公式からもリンクが貼られているとおりMDNを参照すると大体の情報は見つかりそうです。
JScriptもNode.js+xmldomも、XMLを扱うときには便利なツールとなっています。
今回の記事で扱ったように移植は簡単なので、とりあえずどちらかで書いて、不都合が生じたら書き直すというのもありだと思います。