【问题标题】:How to handle namespace declarations in child Elements for XPath in JavaScript?如何在 JavaScript 中处理 XPath 的子元素中的命名空间声明?
【发布时间】:2020-04-23 23:55:59
【问题描述】:

我正在尝试使用MDN: Introduction to using XPath in JavaScript 中指定的 XPath 和 XML。但是,当我尝试按原样使用我的 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: 属性移动到根目录,因为 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)

这行得通。但是,我无法确保命名空间声明位于运行时上传到我的应用程序的 XML 的根元素上。我想知道

  • 可以创建XPathNSResolver,以便考虑根中的命名空间声明以及所有子元素。或
  • xmlns: 声明可以提升到根目录。

也欢迎其他解决方案!

【问题讨论】:

    标签: javascript xml namespaces


    【解决方案1】:

    我想出了一个函数来实现第二种方法。然而,即使在应用了函数 xmlObj 之后,仍然不能直接用于给定的 XPath。 xmlObj 需要反序列化和重新序列化(参见 sn-p 中的第 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)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2010-10-27
      • 1970-01-01
      • 2018-07-13
      • 1970-01-01
      • 1970-01-01
      • 2015-04-11
      • 1970-01-01
      相关资源
      最近更新 更多