さて、jQuery の助けを借りて、私のユース ケース シナリオで機能するこの代替 XPath パーサーを作成しました。パーサーは入力によって指定された XPath にとどまろうとしますが、DOM モデルがパスの途中に新しいタグを追加し、パスの残りの部分がこの 1 つの要素にラップされている場合、パーサーはこの追加を認識してこれを含めます単一の要素をパスに追加します。もちろん、これはすべてのユース ケース シナリオで機能するわけではありませんが、私の場合は機能します。たぶん、この解決策は、少なくともいくつかの拡張の後、他の人に役立ちます:
var SloppyXPathParser = (function () {
function childExists($cursor, element) {
assertSelection($cursor);
var $movedCursor = $cursor.children(element.name);
if ($movedCursor.size() > element.index) {
return jQuery($movedCursor.get(element.index));
} else if ($cursor.children().size() == 1) {
return childExists(jQuery($cursor.children().get(0)), element);
} else {
throw 'Cannot browse to \'' + element.name + '\' at index ' + element.index + '\'';
}
}
function assertSelection($cursor) {
if (!($cursor instanceof jQuery) || $cursor.size() != 1) {
throw 'Selection is invalid: ' + $cursor.size();
}
}
function parsePath(rawPath) {
var nodes = rawPath.split('/');
var regex = new RegExp('([a-zA-Z]+)\\[([0-9]+)\\]');
var elements = [];
var index = 0;
jQuery(nodes).each(function (key, element) {
if (element.length == 0) {
return true;
}
if (!regex.test(element)) {
throw 'Path element does not match regex: ' + element;
}
var matched = regex.exec(element);
elements[index++] = { name: matched[1], index: matched[2] };
});
return elements;
}
function findElement(input) {
var elements = parsePath(input);
var $cursor = jQuery(document);
jQuery(elements).each(function (key, element) {
$cursor = childExists($cursor, element);
});
try {
assertSelection($cursor);
} catch (cause) {
console.log('Exception: ' + cause);
return false;
}
return $cursor.get(0);
}
return {
find: function (input) {
return findElement(input);
}
}
})();
var input = '/html[0]/body[0]/table[0]/tr[1]/td[1]';
SloppyXPathParser.find(input);
HTMLソースは次のとおりです。
<html>
<body>
<table>
<tr>
<td>wrong</td>
<td>wrong</td>
</tr>
<tr>
<td>wrong</td>
<td>right</td>
</tr>
</table>
</body>
</html>
tbody
ブラウザがDOM に要素を追加することを Firebug などで確認できます。パーサーはこれを認識し、エントリをスキップします。