3
4

More than 5 years have passed since last update.

【JavaScript修行】 XMLデータを取得したい!(XPathについて知る編)

Last updated at Posted at 2019-07-29

結構政府関連のAPIとか、古い型であるXMLデータであることが多い。
XMLデータもリクエスト送ってパースしてweb上に描画、とかできた方がいいよね、と思って勉強。
どうも今まで使ってたfetchが上手く使えないっぽい。ええ...

XPath?

XPathを使うと取得しやすいとか。なにそれ!
で、調べてみたけど全然データがない。Qiitaも殆ど記事がない。
人気ないのかなあ。で、色々この辺のサイトを見つけました。
https://uhyohyo.net/javascript/10_1.html
https://www.techscore.com/tech/XML/XPath/XPath1/xpath01.html/
http://cloudcafe.tech/?p=2456

ちょっと脇道に逸れるけどXPathについてきちんと学んでみます。

XPath・・・XMLデータから特定の部分を抽出するための簡潔な言語

CSSではセレクタを使ってHTML文書内の特定の部分を抽出しますが、XPathはより簡潔かつ柔軟に指定ができるとされています。

おー。なんか良さそうですね。なんであんまり情報がないんでしょうか。(不安)

XPathの文法・軸編

/html/body/p

パス指定と似ていますね。htmlの子ノードのbodyの子ノードのp、を表しています。
この/要素名は、

軸::ノードテスト

の略であり、これはロケーションステップと呼ばれています。

軸の初期値はchildなので、
/p = /child::pということ。

軸は色々ある。

軸   名 軸の説明
child 直接の子のみ
descendant 子孫ノード。子全て
ancestor 親ノード全て
parent 直接の親のみ
precending-sibling 兄弟ノードのうち自分より前のノード
following-sibling 兄弟ノードのうち自分より後のノード
self 自分自身のみ
descendant-or-self 子孫と自分自身
ancestor-self 自分より上と自分自身

https://www.xmldb.jp/tech/xpath_1.php
ここにリストがあった。

XPathの文法・ノードテスト編

*・・・要素ノード全て。(テキストノード等は含まない)
note()・・・全てのテキストノード。
comment()・・・コメントノード。
node()・・・全ての種類のノード。

ちなみによく使われるself::node()というロケーションステップは、
.で省略可能です。
self軸の全てのノードって当然自分自身ですよね。何に使うんだ、、本当によく使われるのか?

/html/body/././pのように、間に挟んだりしても関係ありません。
https://uhyohyo.net/javascript/10_1.html

parent::node()..で省略可能です。
直接の親の全て、、なのでこれも当然1つです。

descendant-or-self::node()//で省略可能です。
自分自身とその子孫全てですね。これは使えそうです。

以上を踏まえて、DOMで使ってみる!

document.evcaluateメソッド・・・XPath 式やその他の引数に基づき XPathResult を返す
やっとこさJavaScript上でXPathを使えそうです。

var headings = document.evaluate("/html/body//h2", document, null, XPathResult.ANY_TYPE, null); 

第一引数・・・XPath文
第二引数・・・ルートノード。このノードから下で検索する。
第三引数・・・名前空間解決器(?)
第四引数・・・結果タイプの指定。
第五引数・・・再利用するオブジェクト。

名前空間解決器(?)
XMLやXHTMLでは名前空間がついてることがあるらしい。
<zhtml:html>←こんなの。
名前空間接頭辞をつけたのは属性で設定しているらしい。
ここについては必要になった時調べるぐらいで良さそうなので勉強省略。

結果タイプ
XPathResultのプロパティを指定。
https://developer.mozilla.org/ja/docs/Web/API/Document/evaluate
頑張って意訳してみましょう。。

結果タイプ 説明
ANY_TYPE 与えられた表記のどんなタイプでも全部
NUMBER_TYPE 数値を含む結果を1つセットする(count()ファンクションのとき便利)
STRING_TYPE 2 文字列を含む結果を1つセットする
BOOLEAN_TYPE 3 ブーリアン値を含む結果を1つセットする(not()ファンクションのとき便利)
UNORDERED_NODE_ITERATOR_TYPE 4 表現にあうノード全てを結果にセットする。順はぐちゃぐちゃ
ORDERED_NODE_ITERATOR_TYPE 5 表現にあうノード全てを結果にセットする。順は文書内通り
UNORDERED_NODE_SNAPSHOT_TYPE 6 表現にあうノードをスナップショットで結果にセットする。順はぐちゃぐちゃ
ORDERED_NODE_SNAPSHOT_TYPE 7 表現にあうノードをスナップショットで結果にセットする。順は文書内通り
ANY_UNORDERED_NODE_TYPE 8 表現にマッチしてた1つのノードを返す。ただし最初のやつとは限らない
FIRST_ORDERED_NODE_TYPE 9 表現にマッチしてた最初のノードを返す。

複数のノードをキャッチできるものは、取得したXPathResultオブジェクトからそれを扱うためのプロパティがあります。
・スナップショット系の場合
snapshotLength・・・結果のノード数
snapshotItem・・・ノードへのアクセス

スナップショット系は、evaluateメソッド実行時に全ての探索をしてしまいます。

let result = document.evaluate("/html/body//h2", document, null, XPathResult.ANY_TYPE, null);
for(var i=0; i<result.snapshotLength; i++){
  console.log(result.snapshotItem(i));
}

UNORDERED_NODE_ITERATOR_TYPE, ORDERED_NODE_ITERATOR_TYPE
この2つに関しては、次のノードが必要なたびに探索をする、という仕組みになっているので注意です。
なのでアクセス時引数がありません!
iterateNextメソッドを使います。

var node;
while(node = result.iterateNext()){
  console.log(node);
}

ITERATOR系はその仕様から、木構造に変化があったら、
のちにノードの操作をしようとしてもエラーになります。

document.evaluateが人気ない(?)謎って...

https://caniuse.com/#search=evaluate
IEが全然対応してないみたいですね...ぐむ

3
4
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
4