English Japanese Kawa.netxp [JSAN] XML.ObjTree - XML〜JavaScriptオブジェクト変換クラス

XML.ObjTree クラスは、Perl 用の XML::TreePP モジュールの JavaScript 版です。
XML ファイルと JavaScript オブジェクト(連想配列)間の相互変換を行います。
prototype.js または JSANHTTP.Request クラスと併用することで、 JKL.ParseXML後継 としても利用できます。
ECMA-357 の普及を待たずに、 ECMAScript for XML (E4X) 風のXMLのオブジェクト利用が可能になります。

Intel Mac 版の Safari で、 DOMParser オブジェクトの async プロパティが read-only となる点にも対応しています。
IE で(サーバ上でなく)ローカルのXMLファイルを開きやすくなりました。(2006/08/14)

ダウンロードと使用手順

ダウンロードは、 JSAN または ↓コチラからどうぞ。

アーカイブ: XML.ObjTree-0.24.tar.gz TARGZ
.js ソースファイル単体: lib/XML/ObjTree.js JavaScript

IE 7、Firefox 1.5.0、Opera 8.53、Safari 2.0.3 で 動作確認済 です。

<script> 要素でロードする場合

.tar.gz アーカイブにはテストファイルなどが含まれますが、 動作に必要なのは lib/XML/ObjTree.js ファイルだけです。
その他の JSAN 系クラスには依存しないので、単体で利用できます。
通常通り、<script> 要素で読み込む場合は以下のようになります。 DEMO

<html>
<head>
<script src="lib/XML/ObjTree.js"></script>
</head>
<body>
<script><!--
    var xotree = new XML.ObjTree();
    var xml = '<?xml version="1.0"?><root><node>Hello, World!</node></root>';
    var tree = xotree.parseXML( xml );       	// source to tree
    document.write( "message: "+tree.root.node );
// --></script>
</body>
</html>

JSAN.use() で動的ロードする場合

または、JSAN の use メソッドによる動的ロード機能を使うなら↓のようにも書けます。 DEMO

<html>
<head>
<script src="lib/JSAN.js"></script>
</head>
<body>
<script><!--
    JSAN.addRepository("lib");
    JSAN.errorLevel = "die";
    JSAN.use( 'XML.ObjTree' );

    var xotree = new XML.ObjTree();
    var tree = {
        root: {
            node: "Hello, World!"
        }
    };
    var xml = xotree.writeXML( tree );        // tree to source
	xml = xml.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;");
    document.write( "xml: "+xml );
// --></script>
</body>
</html>

個人的には、JSAN を使わずに、<script> 要素で明示的に読み込んだ方が分かりやすいと思います。

JavaScript オブジェクト展開仕様

以下のようなXMLファイルを展開する場合を想定します。

<?xml version="1.0" encoding="UTF-8"?>
<family name="山田">
    <father>太郎</father>
    <mother>花子</mother>
    <children>
        <girl>和江</girl>
        <boy>一郎</boy>
        <boy>次郎</boy>
    </children>
</family>

上記の XML ファイルは、 parseXML() メソッドのより、 以下のような JavaScript オブジェクト(連想配列)に展開されます。

{
    'family': {
        '-name':    '山田',
        'father':   '太郎',
        'mother':   '花子',
        'children': {
            'girl': '和江'
            'boy': [
                '一郎',
                '次郎'
            ]
        }
    }
};

逆に、writeXML() メソッドで反対方向 (JavaScriptオブジェクト→XMLコード)に変換できます。
JavaScript 上で手軽に XML コードを生成できるため、 XML-RPC リクエストなどに利用できます。

各要素ごとに展開ルールを見ていきます。
XML の要素は、それぞれ JavaScript のオブジェクト(プロパティ)に展開されます。
要素のネストはオブジェクトのネストとなるため、 子要素には .(ピリオド)を区切り文字としてアクセスできます。

tree.family.father;             # お父さんの名前

XML の属性値は、属性名の先頭に - (ハイフン)を付けたプロパティに展開されます。

tree.family["-name"];           # 家族の姓

attr_prefix プロパティにより、ハイフンでなく @(アットマーク)を プリフィックスとして使用できます。E4X 風になります。

子要素名に同じ要素名が複数回登場する場合は、配列に展開されます。

tree.family.children.boy[0];    # お兄さんの名前
tree.family.children.boy[1];    # 弟の名前
tree.family.children.girl;      # 女の子の名前(姉妹はいないので配列化されない)

コンストラクタ・メソッド解説

  xotree = new XML.ObjTree()

XML.ObjTree クラスのコンストラクタです。
今のところ、特に引数はありません。

force_array プロパティ

  xotree.force_array = [ "rdf:li", "item", "-xmlns" ];

XML.ObjTree オブジェクトのプロパティとしては、 force_array が利用可能です。

デフォルト動作では、XML→JavaScript オブジェクト(連想配列)への展開時は 子要素の登場回数で配列化するか否かを自動的に決定しますが、 force_array プロパティで「必ず配列化する要素名・属性名」を指定できます。
force_array プロパティで指定していない要素名・属性名は デフォルト動作で自動判別されます。

例えば、RDF ファイルでは、<rdf:li> や <item> 要素は 複数回登場する可能性があることが予め分かっていますから、 force_array プロパティを指定することで自動判別を回避し、 強制的に配列化を行います。
配列化どうかのチェックを省けるため、スクリプトの動作がより安全になります。
(可能な限り force_array プロパティを使用することが推奨されます)

attr_prefix プロパティ

  xotree.attr_prefix = '@';

XML の属性を JavaScript のオブジェクトに展開した際の識別用のプリフィックス文字を 指定します。デフォルトは「-」半角ハイフンです。 「@」を指定すると、 ECMAScript for XML (E4X) 風になります。
なお、attr_prefix は必ず1文字で指定します。(空・2文字〜は無効)

parseXML() メソッド

  tree = xotree.parseXML( xmlsrc );

parseXML メソッドは、 XML ソースコードをパースして、JavaScript オブジェクト(連想配列)に展開します。

なお、XML ソースコードから DOM への展開の内部処理では、 Firefox 等では DOMParser クラスを利用しています。 IE では、ActiveX の XMLDOM を利用しています。 XML::TreePP とは異なり、自前で文字列解析は行っていません。

parseDOM() メソッド

  tree = xotree.parseDOM( domnode );

parseDOM メソッドは、 DOM ツリーをパースして、JavaScript オブジェクト(連想配列)に展開します。
主に、XMLHTTPRequest の responseXML.documentElement を 指定することを想定しています。
XML ファイル全体でなくて、XML から取得した DOM ツリーの一部分のみを 展開する場合にも利用できます。

それ以外の用途としては、 表示している HTML ページ自身も DOM なので、そのまま指定できたりします。
(※ Opera は HTML の属性名が大文字になっていたり、互換性がなくて困ります…)

parseHTTP() メソッド(同期モード)

  tree = xotree.parseHTTP( url, options );

parseHTTP メソッドは、 ウェブサーバから XML ファイルを受信して、JavaScript オブジェクト(連想配列)に展開します。
内部的に、 JSANHTTP.Request クラスまたは prototype.js の Ajax.Request クラスを利用します。
HTTP/Request.js または prototype.js のどちらかのファイルを 予めロードしておく必要があります。

なお、ブラウザのセキュリティ制限により、表示している HTML ファイルとは違う ドメイン上にある XML ファイルへはアクセスできないのでご注意ください。

第1引数は、XMLファイルのURLを指定します。
もちろん、静的なXMLファイルでなく、CGI などの呼び出しでも構いません。

第2引数は、HTTP.Request/Ajax.Request クラスのオプションオブジェクトを指定します。
method, postBody, parameters, onLoading プロパティなどを指定できます。
単純に GET メソッドを使用する場合は、空 {} にするか省略できます。
POST メソッドを使用する場合は、postBody か parameters を指定してください。

XMLHTTPRequest の同期モード(sync)で通信を行うため、 サーバからのレスポンスが完了するのを待ってから展開処理を行います。 ↓の非同期モード(async)を利用した方が、操作性が向上します。

parseHTTP() メソッド(非同期モード)

  xotree.parseHTTP( url, options, callback );

XMLHTTPRequest の非同期モード(async)で通信を行います。
通信完了を待たずにparseHTTPメソッド自体は終了して、次の行の処理に進みます。
XML の受信が完了した後(onCompleteイベント)に、 第3引数で指定したコールバック関数が呼び出されます。

コールバック関数は、 第1引数にXMLファイルの内容を展開したJavaScriptオブジェクト、 第2引数にXMLHTTPRequestインスタンスを付けて呼び出されます。

writeXML() メソッド

  xmlsrc = xotree.writeXML( tree );

writeXML メソッドは、 JavaScript オブジェクト(連想配列)から XML ソースコードを生成します。
XML-RPC のリクエストボディなどに利用できます。

サンプル・プログラム

テキストノードと属性値

テキストノードのみの要素や、テキストノードを持たない要素は JavaScript オブジェクトとして単純に展開されますが、 テキストノードと属性の両方を持つ要素や テキストノードと子要素を持つ要素の場合、 テキストノードの値を単純にJavaScriptオブジェクトのプロパティ値としては 格納できないため、 #text というプロパティ名に展開されます。

var xotree = new XML.ObjTree();
var xmlsrc = '<span class="author">Kawasaki Yusuke</span>';
var tree = xotree.parseXML( xmlsrc );
var class = tree.span["-class"];        # 属性名の先頭は -
var name  = tree.span["#text"];         # テキストノード

parseHTTP() +GETメソッド+同期モード(sync)

以下のサンプルは、index.html(タグの閉じ忘れ等がないもの)の <html> 要素の lang="〜" 属性の値を表示します。

var xotree = new XML.ObjTree();
var url = "http://example.com/index.html";
var tree = xotree.parseHTTP( url );
alert( tree.html["-lang"] );

なお、parseHTTP() メソッドを呼び出す前に、 予め HTTP/Request.js or prototype.js をロードしておく必要があります。

parseHTTP() +POSTメソッド+非同期モード(async)

以下のサンプルは、 トラックバックping を送信して、その返り値を表示しています。

var xotree = new XML.ObjTree();
var url = "http://example.com/mt-tb.cgi";
var opts = {
    postBody:   "title=...&excerpt=...&url=...&blog_name=..."
};
var func = function ( tree ) {
    alert( tree.response.error );
};
xotree.parseHTTP( url, opts, func );

parseHTTP() メソッドの第3引数として、 コールバック関数(無名ファンクション)を指定しています。
このコールバック関数は、onComplete イベント時に呼び出されます。

簡易 RSS リーダー

以下のサンプルは、簡易的な RSS リーダーです。
RDF ファイルを受信して、各記事を <div> 要素で並べて表示します。

var xotree = new XML.ObjTree();
xotree.force_array = [ "rdf:li", "item" ];
var url = "http://example.com/news-rdf.xml";
var func = function( tree ) {
    var elem = document.getElementById("rss_here");
    for( var i=0; i<tree["rdf:RDF"].item.length; i++ ) {
        var divtag = document.createElement( "div" );
        var atag = document.createElement( "a" );
        atag.href = tree["rdf:RDF"].item[i].link;
        var title = tree["rdf:RDF"].item[i].title;
        var tnode = document.createTextNode( title );
        atag.appendChild( tnode );
        divtag.appendChild( atag );
        elem.appendChild( divtag );
    }
};
xotree.parseHTTP( url, {}, func );

XML-RPC リクエストの送信

以下のサンプルでは、writeXML() メソッドで生成した weblogUpdates.ping の XML-RPC リクエストを、 prototype.js を利用してサーバに送信し、 その返り値(XML)を parseDOM() メソッドで確認しています。

var xotree = new XML.ObjTree();
var reqtree = {
    methodCall: {
        methodName: "weblogUpdates.ping",
        params: {
            param: [
                { value: "Kawa.net xp top page" },  // 1st param
                { value: "http://www.kawa.net/" }   // 2nd param
            ]
        }
    }
};
var reqxml = xotree.writeXML( reqtree );       // JS-Object to XML code
var url = "http://example.com/xmlrpc";
var func = function( req ) {
    var resdom = req.responseXML.documentElement;
    xotree.force_array = [ "member" ];
    var restree = xotree.parseDOM( resdom );   // XML-DOM to JS-Object
    alert( restree.methodResponse.params.param.value.struct.member[0].value.string );
};
var opt = {
    method:         "post",
    postBody:       reqxml,
    asynchronous:   true,
    onComplete:     func
};
new Ajax.Request( url, opt );

prototype.js を直接呼ばずに、parseHTTP()メソッドを利用した方が スクリプトはシンプルになりますが、prototype.js が好きだったり、 何か直接細かい処理を行いたい場合には、parseDOM()メソッドの方がいいかもしれません。

JKL.ParseXMLとの比較

XML.ObjTree の前身である JKL.ParseXML クラス(jkl-parsexml.js)は、 XMLHTTPRequest へのラッパールーチンを内蔵しています。
このラッパーは、IE では状況に応じて Microsoft.XMLDOM(DOMDocument)を利用することで Content-Type: 許容問題 に対応するなど、細かい配慮もあります。

XML.ObjTreeクラスJKL.ParseXMLクラス
公開開始2006/04/062005/05/18
ライブラリ依存関係 parseHTTP() メソッドのみ、 HTTP.Request クラス
または prototype.js のいずれかが必要
それ以外のメソッドでは依存なし。単体でOK
依存ライブラリなし
単体のみで利用可能
XMLソースコード→JSオブジェクト parseXML()で対応 × 非対応
DOMツリー→JSオブジェクト parseDOM()で対応 △ parseDocument()で対応(非公開)
外部XMLファイル→JSオブジェクト parseHTTP()で対応 ○ parse()で対応(これがメイン機能)
JSオブジェクト→XMLソースコード writeXML()で対応 × 非対応
XML以外の形式→JSオブジェクト × 非対応 サブクラスで各形式に対応
application/rdf+xml等 △ ブラウザ依存 ○ できるだけ対応

JKL.ParseXML クラスが サブクラス などの拡張で XMLHTTPRequest のラッパー用途を志向しているのに対し、 XML.ObjTree クラスは XMLHTTPRequest のラッパーは既存の他クラスに任せて、 XML コード生成も含めた XML 処理に特化したクラスとなっています。

prototype.js や他の JSAN 系クラスとの親和性は、XML.ObjTree クラスの方が高いです。
逆に、難しいこと抜きに XMLHTTPRequest も含めてシンプルに使いたい場合は JKL.ParseXML クラスが適しているかと思われます。

更新履歴

2006/08/14 - バージョン0.24
IE などを利用した際に parseHTTP() メソッドで ローカルのファイルもパースしやすくなりました。
responseXML が取得できない場合は、responseText 経由でパースします。 (thanks to Iair Salem)
writeXML() メソッドにbooleanを含むオブジェクトを 渡した場合にエラーになるバグを修正しました。 (thanks to syunduel and James Revillini)
ドキュメントに xmlDecl プロパティの記述を追加しました。(以前より使用は可能だった)
2006/05/26
オンラインDEMOのページを設置しました。 お気軽にお試し下さい。
2006/05/14 - バージョン0.23
Intel Mac 版の Safari では、 DOMParser オブジェクトの async プロパティが read-only なのに対応。
Intel Mac 版の Safari では、バージョン 0.22 はブラウザがクラッシュします。
PowerPC 版の Safari や、他のブラウザでは影響ありません。
2006/04/30 - バージョン0.22
force_array プロパティ を追加しました。
'@' を指定すれば、ECMA-357 の普及を待たずに、 ECMAScript for XML (E4X) 風のXMLのオブジェクト利用が可能になります。
2006/04/06 - バージョン0.21
JSAN 用にテストスクリプトなどを追加し、 XML.ObjTree としてリニューアルしました。
parseXML()   parseDOM()   parseHTTP()   writeXML() の4メソッドが利用可能です。
2006/02/03 - バージョン0.19
JKL.ParseXML の安定動作バージョンです。 (parseHTTP() メソッド相当)
2005/05/18 - バージョン0.01
XML.ObjTree の前身である JKL.ParseXML クラス(jkl-parsexml.js)を公開しました。

コメントはこちらへ by AjaxCom

その他のページへのリンク

このページへのトラックバック by AjaxTB

トラックバックURL:http://www.kawa.net/service/tb/ajaxtb.cgi/works/js/xml/objtree.html

Kawa.netxp © Copyright 2006 Yusuke Kawasaki