【问题标题】:lxml hiding 'xmlns' attribute of root <svg> taglxml隐藏根<svg>标签的'xmlns'属性
【发布时间】:2018-04-06 04:18:05
【问题描述】:

(在我问我的问题之前,请耐心等待下面的小故事!)

我有一些由 MathJax 生成的 SVG 元素,生成后看起来像这样(在元素检查器中可以找到):

<svg xmlns:xlink="http://www.w3.org/1999/xlink" width="6.768ex" height="2.468ex" viewBox="0 -825.2 2914.1 1062.4">
    <defs>...</defs>
    <g>...</g>
</svg>

当我尝试在 chrome 或 safari 中单独显示此 SVG 时,浏览器显示以下错误消息:

此 XML 文件似乎没有任何关联的样式信息。文档树如下所示。 [...]

经过一些实验,我发现罪魁祸首是缺少 'xmlns' 标签。 (我猜 MathJax 在具有此标签的页面中将另一个 SVG 放在更高的位置,因此在网页内,它不需要第二次重复。或者其他什么。)即,将开头的 &lt;svg&gt; 标签更改为此允许浏览器自行显示 SVG:

<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="6.768ex" height="2.468ex" viewBox="0 -825.2 2914.1 1062.4">
    <defs>...</defs>
    <g>...</g>
</svg>

(注意新的xmlns 属性。)

好的。很好。

现在我想自动执行添加缺少的xmlns 标记的任务。我想为此使用 python lxml 实用程序。

不幸的是(终于来了我的问题!),lxml 似乎隐藏了所有以 'xmlns' 开头的属性,我不知道为什么。虽然它允许我添加“xmlns”属性(例如,通过做

root.attrib['xmlns'] = "http://www.w3.org/2000/svg"

其中root 是文档的根&lt;svg&gt; 标记),我无法测试'xmlns' 属性是否已经存在,实际上如果我在同一个文件上运行脚本两次,这会导致两个单独的xmlns 标签被添加,这反过来又导致 lxml 抱怨和崩溃。

所以:(i) 为什么 lxml 对我隐藏某些属性,以及 (ii) 无论如何我如何才能添加 xmlns 标记,除非它已经不存在? (当然我可以手动解析文件,但我想要一个使用 lxml 的独立解决方案。)

【问题讨论】:

  • xmlns="..." 并不是真正的属性;它是一个命名空间声明。因此在attrib 中找不到它。
  • 也许问题是您正在使用文本复制/粘贴操作从 MathJax 输出复制此 XML 子树,而正是此复制操作丢失了范围内的命名空间?在这种情况下,答案可能是找到一种可识别 XML 的方法来提取 SVG 子树?
  • 问题是 MathJax 没有生成有效的 SVG,至少在浏览器 POV 中有效。如果没有限定所有标记和属性名称的命名空间,浏览器将不知道如何处理文档。没有 SVG 命名空间的标签 svg 可能意味着任何东西。所以真正的问题是:如何让 MathJax 生成浏览器可以理解的 SVG?
  • @MichaelKay 复制粘贴操作以打开&lt;svg&gt; 标记开始,以关闭&lt;/svg&gt; 标记结束。信息不会在那里丢失。问题是:当我使用lxml 加载一个xml 文档时,我没有发现根元素是否已经包含xmlns 属性的方法......
  • @LutzHorn (有趣的是,当它嵌入到较大的 html 文档中时,浏览器不会抱怨 MathJax 的 svg。它只会在 SVG 独立时抱怨。但这是一个谜MathJax 的人无疑应该能够解释。)

标签: xml svg lxml


【解决方案1】:

我混合了之前关于命名空间的两个答案。一个来自lxml: add namespace to input file,另一个来自Adding xml prefix declaration with lxml in python。第一个答案不涉及复制属性,所以我从第二个答案中借用它。

from lxml import etree
from io import StringIO, BytesIO

# excerpt from https://commons.wikimedia.org/wiki/File:SVG_logo.svg
# note that xmlns is omitted
xml = '<svg xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="-50 -50 100 100"><rect id="background" x="-50" y="-50" width="100" height="100" rx="4" fill="#f90"/>  <g id="c">      <circle id="n" cy="-31.6" r="7.1" fill="#fff"/> </g></svg>'
tree = etree.parse(StringIO(xml))
root = tree.getroot()

nsmap = root.nsmap
nsmap[None] = 'http://www.w3.org/2000/svg'
root2 = etree.Element(root.tag, nsmap=nsmap)
root2[:] = root[:]
for a in root.attrib:
  root2.attrib[a] = root.attrib[a]

tree2 = etree.parse(StringIO(etree.tostring(root2, encoding="unicode")))
root3 = tree2.getroot()
print(root3)
# <Element {http://www.w3.org/2000/svg}svg at 0x58778f0>

print(root3.nsmap)
# {'xlink': 'http://www.w3.org/1999/xlink', None: 'http://www.w3.org/2000/svg'}

这对你有用,但我相信 MathJax 有办法处理这种任务。

【讨论】:

  • 我不确定我是否理解。我可以通过执行root.attrib['xmlns'] = 'http://...' 添加xmlns 属性。 (虽然如果 'xmlns' 属性已经存在,lxml 稍后会崩溃。)你是说你的方法在任何一种情况下都不会导致 lxml 崩溃?我真正想要的是一种能够测试“xmlns”属性是否已经在根标记中或不在lxml中的方法......
  • etree.fromstring 等将始终剥离 xmlnsroot.attrib['xmlns'] = 'http://...' 不会设置命名空间。在设置该方式后尝试root.nsmap 以验证这一点。使用nsmap 而不是attrib
  • tostring(root2) 的输出很好,但是元素对象仍然没有绑定到命名空间。 print(root2) 输出 &lt;Element svg at 0x58646e8&gt; 而不是 &lt;Element {http://www.w3.org/2000/svg}svg at 0x58646e8&gt;。必须解析 tostring(root2) 的结果才能解决此问题。
  • 好点。我修改了代码。但我担心 OP 没有赶上。
猜你喜欢
  • 1970-01-01
  • 2019-03-23
  • 2021-04-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-02-14
相关资源
最近更新 更多