Libxmljs - Finding XML elements with namespaces using xpath
Today I spent a stupid time trying to parse a XML input to JS. Even using a great module libxmljs I was having problems to read any element that was not inside of client element (xpath: //client
).
SEARCH OK.
0
Why I could not get any of these elements?
- soap:Envelope - xpath:
//soap:Envelope
- soap:Body - xpath:
//soap:Body
- Response - xpath:
//Response
- Result - xpath:
//Result
This question forced me study a bit more about xml structure. In a first moment I didn't want to study it because XML is too boring. But I was spending too much time just guessing and searching a solved solution for my problem.
Reading XML Namespaces and How They Affect XPath and XSLT I could finally understand why I could get only elements inside of client.
Summarizing that article there are three types of namespaces:
Prefix Namespaces๐
A namespace is composed of a name and a URI specification. The scope of the prefix-namespace mapping is that of the element that the namespace declaration occurs on as well as all its children.
Lord of the Rings
J.R.R. Tolkien
Default Namespaces๐
Since use prefix namespace for all element is too verbose. Any element without a prefix namespaces is associated to the default namespace.
Lord of the Rings
J.R.R. Tolkien
Undeclared Namespaces๐
If an element has blank namespace (xmlns=""
) then it is a undeclared namespace and any children element without a prefix namespace is associated to that undeclared namespace.
Lord of the Rings
J.R.R. Tolkien
Understanding these namespaces helped me finally solve my problem. The reason I could only get the client element and its children was because it was using an undeclared namespace. Using libxmljs is necessary specify the namespace on reading an element with namespace. Which means I was not passing the namespace for those elements with namespace and their children.
This is simple example to clarify the final solution:
;
;
;
"-->soap:Envelope", "/soap:Envelope";
// does not find element
"-->soap:Envelope", "/soap:Envelope", ;
// finds element
"-->soap:Body", "//soap:Body", ;
// finds element
"-->Response", "//xmlns:Response", "http://tempuri.org/";
// finds element
"-->Response", "//Response";
// does not find element
"-->Result", "//Result";
// does not find element
"-->client", "//client";
// finds element
"-->msg", "//msg";
// finds element
"-->xmlns:Result", "//xmlns:Result", "http://tempuri.org/""//msg";
// finds element