Как обрабатывать объявления пространства имен в дочерних элементах для XPath в JavaScript? - PullRequest
0 голосов
/ 06 января 2020

Я пытаюсь использовать XPath и XML, как указано в MDN: Введение в использование XPath в JavaScript. Однако, когда я попытался использовать XML как есть, это привело к ошибке (см. Ниже)

const xmlStr = `<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" 
    xmlns:n0="http://tempuri.org/">
    <soapenv:Header>
    </soapenv:Header>
    <soapenv:Body>
        <n0:SavePartnerDetailsWithToken xmlns:n2="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
            <n0:userToken></n0:userToken>
            <n2:myNode>valueHere</n2:myNode>
        </n0:SavePartnerDetailsWithToken>
    </soapenv:Body>
</soapenv:Envelope>`;
const xmlObj = new DOMParser().parseFromString(xmlStr, 'application/xml');
const nsResolver = xmlObj.createNSResolver(xmlObj.ownerDocument === null ? xmlObj.documentElement : xmlObj.ownerDocument.documentElement);
firstNode = xmlObj.evaluate('/soapenv:Envelope/soapenv:Body/n0:SavePartnerDetailsWithToken/n2:myNode', xmlObj, nsResolver, XPathResult.FIRST_ORDERED_NODE_TYPE, null);
console.log(firstNode.singleNodeValue.textContent)

Затем я переместил все атрибуты xmlns: в root, поскольку метод createNSResolver () ожидает documentElement в качестве параметра.

const xmlStr = `<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" 
    xmlns:n0="http://tempuri.org/"
    xmlns:n2="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
    <soapenv:Header>
    </soapenv:Header>
    <soapenv:Body>
        <n0:SavePartnerDetailsWithToken>
            <n0:userToken></n0:userToken>
            <n2:myNode>valueHere</n2:myNode>
        </n0:SavePartnerDetailsWithToken>
    </soapenv:Body>
</soapenv:Envelope>`;
const xmlObj = new DOMParser().parseFromString(xmlStr, 'application/xml');
const nsResolver = xmlObj.createNSResolver(xmlObj.ownerDocument === null ? xmlObj.documentElement : xmlObj.ownerDocument.documentElement);
firstNode = xmlObj.evaluate('/soapenv:Envelope/soapenv:Body/n0:SavePartnerDetailsWithToken/n2:myNode', xmlObj, nsResolver, XPathResult.FIRST_ORDERED_NODE_TYPE, null);
console.log(firstNode.singleNodeValue.textContent)

Это работает. Однако я не могу гарантировать, что объявления пространства имен находятся в элементе root для XML, который загружен в мое приложение во время выполнения. Я хотел бы знать, можно ли

  • XPathNSResolver создать таким образом, чтобы учитывались объявления пространства имен в root, а также все дочерние элементы. ИЛИ
  • Объявления xmlns: можно поднять на root.

Также приветствуются альтернативные решения!

1 Ответ

0 голосов
/ 07 января 2020

Я придумал функцию для достижения второго подхода. Тем не менее, даже после применения функции xmlObj по-прежнему нельзя использовать непосредственно для данного XPath. XmlObj необходимо десериализовать и повторно сериализовать (см. Строки 41 и 42 во фрагменте) для успешной оценки XPath.

function hoistNameSpaceAttributesToRoot(DOMNode) {
	if (DOMNode instanceof Element) {
		if (DOMNode.hasAttributes() && DOMNode !== DOMNode.ownerDocument.documentElement) {
			const attrs = DOMNode.attributes;
			for (let i = attrs.length - 1; i >= 0; i--) {
				if (attrs[i].name.startsWith('xmlns:')) {
					DOMNode.ownerDocument.documentElement.setAttribute(attrs[i].name, attrs[i].value);
					DOMNode.removeAttribute(attrs[i].name);
				}
			}
			if (DOMNode.hasAttribute('xmlns')) {
				DOMNode.removeAttribute('xmlns');
			}
		}
	}
	// First check that the element has child nodes
	if (DOMNode.hasChildNodes()) {
		const children = DOMNode.childNodes;

		// tslint:disable-next-line: prefer-for-of
		for (let i = 0; i < children.length; i++) {
			// do something with each child as children[i]
			// NOTE: List is live, adding or removing children will change the list
			this.hoistNameSpaceAttributesToRoot(children[i]);
		}
	}
}
let xmlStr = `<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" 
    xmlns:n0="http://tempuri.org/">
    <soapenv:Header>
    </soapenv:Header>
    <soapenv:Body>
        <n0:SavePartnerDetailsWithToken xmlns:n2="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
            <n0:userToken></n0:userToken>
            <n2:myNode>valueHere</n2:myNode>
        </n0:SavePartnerDetailsWithToken>
    </soapenv:Body>
</soapenv:Envelope>`;
let xmlObj = new DOMParser().parseFromString(xmlStr, 'application/xml');
hoistNameSpaceAttributesToRoot(xmlObj);
xmlStr = new XMLSerializer().serializeToString(xmlObj);
xmlObj = new DOMParser().parseFromString(xmlStr, 'application/xml');
const nsResolver = xmlObj.createNSResolver(xmlObj.ownerDocument === null ? xmlObj.documentElement : xmlObj.ownerDocument.documentElement);
const firstNode = xmlObj.evaluate('/soapenv:Envelope/soapenv:Body/n0:SavePartnerDetailsWithToken/n2:myNode', xmlObj, nsResolver, XPathResult.FIRST_ORDERED_NODE_TYPE, null);
console.log(firstNode.singleNodeValue.textContent)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...