Namespace handling #43
Replies: 2 comments 4 replies
|
What does "proper recognition" mean to you? Namespace support is a broad concept, many aspects of which are likely out of scope for the goals of this project, but if you can share concrete use cases, that would help me understand what you're looking for. Bonus points for detailed examples of how you think this should work! |
|
I think something like this: Basically, it should be possible to identify elements and attributes from a specific namespace, where a namespace is defined exclusively by its URL (i.e., xmlns attributes are an internal detail of the XML format that should be abstracted out by the parser). I wrote this for my personal use which AFAICT fulfills that requirement. It uses some helper functions that should be self-explanatory -- they're basically all from Rust. type XmlNs = string | null;
declare module "@rgrove/parse-xml" {
interface XmlElement {
ns?: XmlNs;
is([ns, name]: readonly [XmlNs, string]): boolean;
}
}
function tag_namespaces(node: XmlElement, default_ns: XmlNs = null, namespaces: Map<string, string> = new Map()) {
let namespaces_cloned = false;
for (const [attr, value] of Object.entries(node.attributes)) {
const namespace = str_strip_prefix("xmlns:", attr);
if (namespace === null) {
continue;
}
// Optimistically avoid copying, because nested namespace declarations are uncommon.
if (!namespaces_cloned) {
namespaces = new Map(namespaces);
namespaces_cloned = true;
}
namespaces.set(namespace, value);
}
// XML allows to set a "default namespace" with a bare `xmlns="<ns url>"` attribute.
if ("xmlns" in node.attributes) {
const xmlns_attr = node.attributes["xmlns"];
default_ns = xmlns_attr;
}
const [old_node_ns, new_node_name] = str_split_once(":", node.name) ?? [null, node.name];
const new_node_ns: XmlNs | undefined = old_node_ns !== null ? namespaces.get(old_node_ns) : default_ns;
assert(`undefined ns ${dbg(old_node_ns)} used`, new_node_ns !== undefined);
node.ns = new_node_ns;
node.name = new_node_name;
for (const child of node.children) {
if (!(child instanceof XmlElement)) {
continue;
}
tag_namespaces(child, default_ns, namespaces);
}
}
XmlElement.prototype.is = function ([ns, name]: readonly [XmlNs, string]) {
assert("tag_namespaces not run", this.ns !== undefined);
return this.ns === ns && this.name === name;
}
export function xml_parse(raw: string): XmlDocument {
const parsed = parseXml(raw);
for (const child of parsed.children) {
if (child instanceof XmlElement) {
tag_namespaces(child);
}
}
return parsed;
} |
Uh oh!
There was an error while loading. Please reload this page.
Proper recognition of XML namespaces is important in some contexts. I think this is something the parser should take care of.
All reactions