1 (изменено: badik, 2014-02-19 07:22:57)

Тема: Jscript: вычисление пути к узлу XML

Функция sPath вычисляет путь к конкретному узлу XML
Выражения xPath

  • ancestor-or-self::*  возращает родителей и сам узел

  • preceding-sibling:: узлы слева

  • following-sibling:: узлы права

      var xd = new ActiveXObject("MSXML2.DOMDocument")
      xd.setProperty("SelectionLanguage", "XPath")
      xd.async = false
      xd.loadXML('<a><b/><b><c/><c id="1"/></b></a>')
      var o = xd.selectSingleNode("//c[@id=1]")
      WScript.Echo(sPath(o))
      // >  a/b[2]/c[2]

  function sPath(xd) {
    var p = []
      if (xd)
      {
      var a = xd.selectNodes("ancestor-or-self::*")
      for (var i = 0; i < a.length; i++) {
        p[p.length] = a[i].nodeName
        var nleft = (a[i].selectNodes("preceding-sibling::" + a[i].nodeName)).length
        var nright = (a[i].selectNodes("following-sibling::" + a[i].nodeName)).length
        if (nleft != 0 || nright != 0) {
          p[p.length - 1] += "[" + (nleft + 1) + "]"
        }
      }
    }
    return  p.join("/")
  }

2

Re: Jscript: вычисление пути к узлу XML

Спасибо. badik, подскажите, а на практике в каких случаях она применимяется?

3

Re: Jscript: вычисление пути к узлу XML

Собираюсь использовать подобный алгоритм для копирования в буфер обмена xPath из HTML (HTA) представления XML. Там, правда, ещё повозиться прийдётся: связать XML и представление. Соответственно, ознакомился с большим интересом.
Спасибо.

4

Re: Jscript: вычисление пути к узлу XML

Писал заплатку для логического контроля принимаемых  внешних файлов  для которых
нет схем проверки xsd.  Основная цель отсечь плохие файлы для загрузки в свое ППО.

Для ведения протокола ошибок (логирования предупреждений) была написана данная функция.


20.02.2014 12:28:14 ****060625.xml
20.02.2014 12:28:14 Пусто:Information/DataArea/Header/Report/CardDataLine/Oper/NumberPP

20.02.2014 12:28:14 ****045631.xml
20.02.2014 12:28:14 Пусто:Information/DataArea/Header/Report/CardDataLine/Oper[2]/NameContragent
20.02.2014 12:28:14 Пусто:Information/DataArea/Header/Report/CardDataLine/Oper[2]/NumberPP

5 (изменено: Rumata, 2022-05-11 20:17:05)

Re: Jscript: вычисление пути к узлу XML

Просматривая свои старые записи, наткнулся на код, заимствованный из этой темы. Обнаружил, что предложенная автором функция не работает если xml-узлы имеют полное имя (включают префикс, например, <x:a />) и определение пространства имен для этого префикса.

Это легко исправить, если в запросах поиска узлов слева и справа использовать не nodeName, а baseName. Исправление представлено в функции selectXPath. Причем оно работоспособно и для первоначального примера.

Дополнительно реализовал функцию getXPath. Алгоритм отличается - там производится перебор всех предков и братьев узла средствами самого JS. Эффективность того или иного способа я не проверял.

Также, в обеих функциях добавил поддержку xml-атрибутов.

Функции возвращают корректные, но отличные друг от друга результаты.


var s = '<x:a xmlns:x="something"><b/><b><c/><c id="1"/></b></x:a>';

var xml = new ActiveXObject('Msxml2.DOMDocument.6.0');
//xml.validateOnParse = true;
//xml.setProperty('SelectionLanguage', 'XPath');
xml.loadXML(s);

var a = xml.selectSingleNode('//@id');

var p1 = selectXPath(a); // returns "/x:a/b[2]/c[2]/@id"
var p2 = getXPath(a);    // returns "/x:a[1]/b[2]/c[2]/@id"

WScript.Echo(p1);
WScript.Echo(p2);

// http://forum.script-coding.com/viewtopic.php?id=9277
function selectXPath(xmlNode) {
	var result = [];

	var ancestors = xmlNode.selectNodes('ancestor-or-self::*')
	for (var i = 0; i < ancestors.length; i++) {
		var node = ancestors[i];
		var nodeName = node.nodeName;
		var baseName = node.baseName;

		var L = node.selectNodes('preceding-sibling::' + baseName).length;
		var R = node.selectNodes('following-sibling::' + baseName).length;
		if ( L || R ) {
			nodeName += '[' + ( L + 1 ) + ']';
		}
		result.push(nodeName);
	}

	// It's xml attribute. Store its name manually, because the code
	// above doesn't work for xml attributes.
	if ( xmlNode.nodeType == 2 ) {
		result.push('@' + xmlNode.name);
	}

	return '/' + result.join('/')
};

// https://stackoverflow.com/a/18184670/3627676
function getXPath(xmlNode) {
	// It's xml attribute. Take its owner and continue.
	if ( xmlNode.nodeType == 2 ) {
		return arguments.callee(xmlNode.selectSingleNode('..')) +
			'/@' + xmlNode.name;
	}

	if ( xmlNode.parentNode == null ) {
		return '';
	}

	var i = 1;
	for (var sibling = xmlNode; sibling = sibling.previousSibling; ) {
		if ( sibling.nodeName == xmlNode.nodeName ) {
			i++;
		}
	}

	return arguments.callee(xmlNode.parentNode) +
		'/' + xmlNode.nodeName + '[' + i + ']';
};
( 2 * b ) || ! ( 2 * b )