3.1 命名空间:前缀绑不上就选不中


3.1 命名空间:前缀绑不上就选不中

本节摘要:元素进入某个命名空间后,裸名路径选不中它。默认命名空间尤其阴:源码看不见前缀,查询却必须绑一个。前缀只是本地贴纸,引擎比较的是 URI。URI 不必能在浏览器打开,它只是唯一护照号。

学习目标

阅读完本节,你应当能够:

  1. 解释为何带默认命名空间的文档让 /bookstore/book 变空
  2. 在查询侧注册前缀到 URI 的映射并写出 p:book
  3. 对比 local-name 应急写法与正确绑前缀
  4. 说明 xmlns 声明不是用 @xmlns 去选元素的办法

一、同一棵书店树,加上默认命名空间就空

把根改成带默认命名空间(URI 写成文字,不当超链接):

<bookstore xmlns="urn:bookstore-example"> <book category="web"> <title>Learning XML</title> </book> </bookstore>

此时 /bookstore/book 命中零个。不是树没了,是 bookstorebook 都属于 urn:bookstore-example,而你的路径在问「无命名空间、本地名 bookstore 的元素」。问错护照,海关放行零人。

正确做法:在引擎里注册 p → urn:bookstore-example,写:

/p:bookstore/p:book

lxml 是 root.xpath('/p:bookstore/p:book', namespaces={'p': 'urn:bookstore-example'})。Java 要实现 NamespaceContext。浏览器 document.evaluate 的解析器回调负责前缀。不注册就写 p:book,引擎报未绑定前缀。

原文集把命名空间比作模块:不同词汇都可以有 title,书名和人的称谓不会撞。护照是 URI,前缀是你在本文件里给护照起的简称。源文档用 pub,你可以在查询里用 b,只要 URI 相同。

二、默认、带前缀、属性

默认命名空间 xmlns="URI" 作用在该元素及其未加前缀的后代元素上,默认不作用在属性上。所以 category 属性通常仍在无命名空间,路径是 /p:bookstore/p:book/@category。有人给属性也写 xmlns 期望它进默认空间,匹配会落空。属性要进命名空间必须显式 pub:id="..."

带前缀声明 xmlns:pub="URI" 只让 pub:book 进入该空间,未加前缀的元素若另有默认空间则进默认,否则进空空间。混合文档最常见:外壳 HTML 空间,内嵌 SVG 空间。路径每一步都要带对前缀。

写法 选得中带默认空间的 book 代价
/bookstore/book 最常见的空
/p:bookstore/p:book 且已注册 正道
//*[local-name()='book'] 是,但也误伤其他空间的 book 应急
//book 双斜杠不帮你忽略空间

⚠️ 常见坑:只给最后一步加前缀 /bookstore/p:book。祖先 bookstore 仍在命名空间里,第一步就空,后面无从走起。默认空间下每一层元素都要带前缀。
💡 关键直觉:前缀是你口袋里的贴纸,可以和源码不一致;URI 必须一致。URI 打不开网页没关系,它不是网址,是护照号。

图:默认空间让裸名全部落空

图:默认空间让裸名全部落空

三、应急、HTML、调试

//*[local-name()='book' and namespace-uri()='urn:bookstore-example'] 不依赖查询前缀注册,有的环境很难塞 NamespaceContext 时用。只写 local-name 在合并文档里会把两套词汇的 book 混成一锅。能注册前缀就注册。

HTML5 解析后,元素往往落在 XHTML 命名空间。浏览器里对 HTML 文档用 XPath,可能要绑这个空间,或改用 CSS 选择器。lxml 的 HTML 解析器有时把节点放进空空间,同一条路径在 XML 模式和 HTML 模式结果不同。解析器选项要和路径约定绑在一起。

调试空结果:先 local-name() 打出实际本地名,再 namespace-uri() 打出护照。两者都对得上再写前缀路径。不要先怀疑轴。

xmlns:pub 在数据模型里是命名空间节点,不是普通属性。@xmlns:pub 在 1.0 里行为因引擎而异,不要靠它取 URI。用 namespace-uri() 或宿主 API。

问题:为什么 CSS 选择器不管命名空间,XPath 管?

CSS 在 HTML 里默认按本地名匹配。XPath 出身 XML,命名空间是一等公民。用 XPath 处理 XHTML,就要接受护照检查。

问题:能声明查询的默认命名空间吗?

2.0 / XQuery 可以声明默认元素命名空间,路径又可以写裸名。1.0 没有这个便利,只能逐步加前缀。这是「升级 2.0」少数几个能立刻感到爽的点。

四、HTML、应急写法、以及调试时先打护照

HTML5 解析之后,元素常常落在 XHTML 命名空间里。你在浏览器控制台写 //div 可能空,写带前缀的路径才有。也有解析器把 HTML 放进空空间,同一条字符串在 XML 模式和 HTML 模式结果不同。所以「浏览器 XPath 坏了」优先怀疑护照,其次怀疑 tbody 补全,最后才怀疑轴。lxml 的 HTML 解析器选项要和路径约定写在一起,换选项等于换树。

local-name 等于 book 能在无法注册 NamespaceContext 的环境里救命。加上 namespace-uri 等于那串护照,误伤会降下来。只写 local-name 在合并文档里会把两套词汇的 book 煮成一锅,报表数字会悄悄偏大。能注册前缀就注册。前缀不必和源码相同:源码用 pub,查询用 p,URI 相同即可。这是海关认护照不认贴纸的直接推论,写进团队规范可以减少「必须抄源码前缀」的迷信。

默认空间不作用在属性上。@category 在带默认空间的书店文档里通常仍可选中。有人给属性也期望进默认空间,匹配落空。属性要进命名空间必须源码写带前缀的属性名。查询侧对应地写 p:id 这类带前缀属性测试。调试时分别打印元素的 namespace-uri 和属性的 namespace-uri,两者经常不一样。

只给最后一步加前缀是第二常见错误。/bookstore/p:book 的第一步仍在问无空间的 bookstore,空。默认空间下每一层元素都要带你的前缀。祖先链有三层就写三次。嫌烦,2.0/XQuery 可以声明默认元素命名空间,1.0 没有这条福利。

xmlns 声明在数据模型里是命名空间节点。不要用 at xmlns 去「选元素」或「读取 URI」当常规手段,行为因引擎而异。用 namespace-uri 函数或宿主 API。URI 打不开网页不代表写错,它从来不是用来打开的。

五、给默认空间书店绑前缀,一层都不少

根写成带默认空间 urn:bookstore-example。裸名 /bookstore/book 零节点。注册 p 到该 URI,写 /p:bookstore/p:book 命中。只写 /bookstore/p:book 仍空,第一步护照不对。三层嵌套就写三次前缀。属性 category 通常仍无空间,/p:bookstore/p:book/@category 能取。local-name 等于 book 能中,也会中其他空间的 book。URI 打不开网页没关系。源码前缀 pub、查询前缀 p,URI 相同即可。HTML 可能在 XHTML 空间。xmlns 不当普通 at 用。

练习:同一份带默认空间的片段,先跑裸名确认空,再注册前缀跑通,再故意只给最后一步加前缀确认仍空。打印 namespace-uri 对照护照号。把 local-name 应急和正确绑前缀各跑一次,用第二套也叫 book 的词汇证明应急会误伤。1.0 没有查询侧默认空间,不要要求 lxml 像 XQuery 那样裸名复活。每一层都带前缀嫌烦,这是 1.0 的税,升级 2.0 才能免。

六、默认空间是看不见前缀的护照

源码里标签还叫书店,查询裸名却是零,因为元素已经进了默认空间。查询侧必须注册贴纸到护照号,而且每一层元素都要贴,只给最后一步贴,第一步就空。属性通常不进默认空间,分类盖章继续按属性取。贴纸可以和源码不一致,护照号必须一致。本地名应急会把另一套词汇的同名标签混进来。护照号打不开网页不说明写错,它不是用来打开的。超文本解析后可能落在另一套空间,同一串字在两种解析模式下结果不同。声明节点不要当普通属性来选元素。一点零没有查询侧默认空间开关,嫌每一层都贴麻烦,那是一点零的税。调试先打本地名和护照号,再改轴。

原文集把空间比作模块:不同词汇都可以有标题,书名和人称谓不会撞。护照是号,贴纸是本文件里的简称。默认声明作用在该元素及其未加贴纸的后代元素上,默认不作用在盖章上,所以分类盖章通常仍在无空间。带贴纸的声明只让带该贴纸的标签进入该空间。混合文档外壳一套空间、内嵌另一套,每一步都要带对。应急写法加上护照号检查能降低误伤,只写本地名在合并文档里会把两套词汇煮成一锅。超文本解析后元素往往落进另一套空间,浏览器里裸名字可能空。调试空结果先打本地名和护照,不要先怀疑方向。

同一份带默认空间的片段做三跳:裸名应空,注册贴纸应中,只给最后一步加贴纸应仍空。打印护照号对照。把本地名应急和正确绑贴纸各跑一次,用第二套也叫书的词汇证明应急会误伤。属性分类通常仍无空间,带前缀的书再揭章能取到。源码贴纸和查询贴纸可以不同,号必须同,打不开网页不说明写错。超文本解析后可能落在另一套空间,同一串字两种解析模式结果不同。声明不要当普通盖章。低版本没有查询侧默认空间开关,每一层都贴是税。调试先打本地名和护照号,再改方向。空结果的外表和元素名写错完全一样,都是零节点,所以必须先打护照而不是先改方向。原文把空间比作模块,不同词汇都可以有标题,书名和人称谓不会撞。

三跳实验:裸名空、全层贴纸中、只贴最后一步空。护照号对得上才算绑对。本地名应急会误伤第二套词汇。盖章通常无空间。贴纸可换号不可换。打不开网页没关系。两种解析模式可能把同一串字放进不同空间。声明不是普通盖章。低版本没有查询侧默认空间。调试先打本地名和护照。空的外表和拼错名字一样,所以必须先打护照。模块比喻:不同词汇都可以有标题。每一层都贴是低版本的税。

护照检查通过之前,不要把空结果解释成样本写错。先打本地名和号,再决定是绑贴纸还是改树。每一层都贴完,盖章再按无空间去揭。

海关门口的三句话可以当口令:看不见贴纸也有护照,每一层都要验,盖章通常免验。口令背熟,空结果先对口令再改方向,排错时间会短一半。

超文本和标记语言两种解析模式可能把同一份源码放进不同空间,路径约定必须和解析器选项写在一起,换选项等于换树,昨天能中的裸名今天会空,先对解析器再改路径。

动手验收用同一份带默认空间的片段连跑三跳:裸名应空,每一层都贴查询贴纸应中,只给最后一步贴应仍空。三跳对不上,空的外表和拼错元素名完全一样,所以必须先打印本地名和护照号,再决定改不改轴。属性分类通常仍无空间,揭章继续按无前缀写;给属性也强行加贴纸,反而会空。

本节速览

  • 默认命名空间让裸名变空:不是树没了
  • 前缀可与源码不同,URI 必须同
  • 每一层元素都要带前缀:只给最后一步加不够
  • 属性默认不进默认空间:继续 @category
  • local-name 应急会误伤:长期方案是注册映射
  • URI 不是用来打开的网址:打不开不代表写错

下一节在已经能选中的前提下,用 2.0 变量把重复路径收起来,并说清自定义函数通常住在 XSLT 和 XQuery 里。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U