0

The following code returns null for doc.evaluate(). But it returns the correct element for the XPath //cat:symbol[@dog:label='banana']". So, I think it is not finding the default namespace.

If I run console.log(ns.lookupNamespaceURI("")); or console.log(ns.lookupNamespaceURI(null));, it returns http://www.w3.org/2000/svg, which is the xmlns value of the SVG. Then, why is the code not finding the element for "//symbol[@dog:label='banana']"?

JavaScript

fetch('test.svg')
.then(response => response.text())
.then(data=>{
    const parser = new DOMParser();
    const doc = parser.parseFromString(data, "text/xml");
    const ns = doc.createNSResolver(doc.documentElement);
    const res = doc.evaluate("//symbol[@dog:label='banana']", doc.documentElement,
     ns, XPathResult.FIRST_ORDERED_NODE_TYPE, null);
    console.log(res.singleNodeValue);
})

test.svg

<?xml version="1.0" encoding="UTF-8" standalone="no"?>

<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"
 xmlns:dog="http://dog.com/dog"
 xmlns:cat="http://cat.com/cat">

    <symbol dog:label="banana">
        <rect y="10" x="10" width="90" height="90" stroke-width="5" stroke="#f00" fill="#f00" fill-opacity="0.5" />
    </symbol>
    <cat:symbol dog:label="banana">
        <rect y="5" x="5" width="90" height="90" stroke-width="5" stroke="#f00" fill="#f00" fill-opacity="0.5" />
    </cat:symbol>
</svg>
Damn Vegetables
  • 11,484
  • 13
  • 80
  • 135

1 Answers1

0

XPath 1.0 which the browser vendors support with the evaluate function on a Document node does not have the concept of a default element namespace so while the created namespace resolver maps the empty prefix '' to a URI within the context of XPath 1.0 that has no meaning as there an element name without a prefix (e.g. foo) always selects an element with local name foo in no namespace.

Martin Honnen
  • 160,499
  • 6
  • 90
  • 110
  • I am not sure if I understood the second half of the sentence. So, in the example above, to find the ``, how should I modify the JavaScript code? `doc.evaluate("//symbol[@dog:label='banana']",...)` does not work. – Damn Vegetables Dec 18 '21 at 12:50
  • With XPath 1.0, to find/select an SVG element in the SVG namespace, you need to use a prefix (in XPath) and make sure it is bound to the SVG URI e.g. `function(prefix) { if (prefix === 'svg') return 'http://www.w3.org/2000/svg'; ... }` and use that prefix to qualify element names in the path e.g. `//svg:symbol`. Or switch to XPath 2 or 3 (e.g. with Saxon-JS) where the XPath language versions knows the concept of a default element namespace for XPath evaluation and you can set that up for any evaluation, although with a different API, see https://stackoverflow.com/a/70402980/252228 – Martin Honnen Dec 18 '21 at 13:10
  • If I were to add an arbitrary entry like `svg`, I cannot use `createNSResolver` to automatically extract namespaces from the XML itself, and must add all entries manually? That is, I cannot just add a new entry to the result of `createNSResolver`? – Damn Vegetables Dec 18 '21 at 13:45
  • A single XML document can use various "conflicting" namespace binding, including for the default namespace (e.g. `............`) so I am not sure where you expect to get with that resolver alone the function returns. The prefixes in the XML input don't matter for the XPath evaluation, what matters are the namespaces. I don't see what prevents you from doing e.g. `function(prefix) { if (prefix === 'svg') then return 'http://www.w3.org/2000/svg'; else return ns(prefix); }`. – Martin Honnen Dec 18 '21 at 18:32