XML命名空间详解
一、命名空间的意义
XML 是一种非常好用的标记语言,它具有极好的可扩展性,因此当我们需要访问 XML 文档时,有可能会出现这样的情况:
在同一份 XML 文档中可能出现多个同名的标签和属性,而这些标签和属性意义的又是完全不同的,遇到这种情况如果我们不能从语法上提供区别,则 XML 处理器将无法区分它们
为了解决这个问题 XML 提供了命名空间的支持.我们想象这样一个场景,在学校的操场上你喊一句 "小明" 可能一下子会有好几个小明回应你.那你如果喊 3 年级 2 班的小明,那可能只会有你要找的那个小明回应你.这里我们指定了一个范围(3 年级 2 班),在这个范围内小明是唯一的.说简单一些,要确定一个人仅有名字是不够的,还必须要有一个确定的范围,这个范围就可以理解为命名空间
回到 XML 中,假如有这样一个文档,文档中需要保存图书的信息,同时还要保存作者的详细信息,那么可能会是如下的一个 XML 文档
<book>
<title>西游记</title>
<author>
<name>吴承恩</name>
<title>先生</title>
</author>
</book>
这个文档中 book 有两个子标签 title 和 author,title 表示书名,author 表示作者的信息.而 author 中也有一个 title 属性,这里 title 只的是头衔称呼,也就是表示的是称呼吴承恩为先生,但是这里 book 中的 title 和 author 中的 title 却是两个完全不同的意思,仅仅靠程序是很难区分开这两个 title 的含义的
二、使用前缀
解决这个问题最好的方法就是为不同的元素起不同的名字.比如:这里我们可以定一个规则,每一个 book 下的元素使用一个前缀,而 author 下的使用另一个前缀,这样我们可以通过不同的前缀来区分不同的标签,如此一来我们的文档会变成这个样子:
<b:book>
<b:title>西游记</b:title>
<a:author>
<a:name>吴承恩</a:name>
<a:title>先生</a:title>
</a:author>
</b:book>
这种方式看起来比较难看,但是确实可以达到区分的目的,现在 b:title 和 a:title 就是两个不同的标签.
通过前缀名我们可以很方便的将文档中的标签分成 a类 和 b类,带有前缀 a 的属于 a 类型,带有前缀 b 的属于 b 类型,这个类就是命名空间,这里你看起来是不是和 Java 中的包名有着异曲同工的效果呢?
但是这里还存在一个小问题,这里的命名空间还只是通过名字来区分不同的标签,也就是说命名空间只是为名字进行了一个分类,但是名字具体代表了什么意义?它们应该在哪里出现?它并没有一个说明和约束(这里就要涉及到 XML 的约束文档了 .xsd 或 schemal 文件)
三、为什么 XML 没有直接使用前缀
我们上边这个方法可以解决我们所面临的问题,但是 XML 为什么没有直接采用这种方式呢?因为这种方式存在一个漏洞,我们通过前缀来约束标签名,那么前缀是否有可能重复呢?假如一份有 apache 定义的 XML 文档,前缀我们设置为 a.然后又有一份 alibaba 定义的文档前缀同样为 a,那这两份文档是不是就会出现重复的标签呢?所以我们的问题并没有根本解决
解决这个问题现在变成了我们需要使用一个唯一的前缀来确保文档中标签的唯一性,那么现实中有哪些东西是唯一的呢?想一下,Java 中包名采用的是哪种方式?
没错!就是 uri 地址,Java 中通过将 uri 地址倒着写来到达区分包的目的,之所以可行就是因为每个公司的 uri 地址都是唯一的,所以不会出现重复
这里我们就可以为我们的标签名使用 uri 地址作为前缀比如(http://www.yeahstack.com/a),采用这种方式很显然出现重复的几率就小多了,但是注意这里的 http://www.yeahstack.com/a 并不是一个真实的地址,它的目的就是一个确保唯一
这种方式虽然看着可行但是实际上却是不现实的,不讨论 XML 中可不可以直接在标签中编写 uri 地址,仅从编写的角度上看我们也不会这个干,这玩意太麻烦了,谁会去写一个这样的标签啊
<author:http://www.yeahstack.com ..... />
四、XML中如何使用命名空间
XML 命名空间为我们提供了一个标准的语法,声明 XML 名称空间,并为 XML 文档里的某个元素确定命名空间
要在文档里使用 XML 命名空间,元素名就变成了限定名(qualified names 缩写为 qName),限定名分成了两部分,一部分就是我们之前使用的元素名,另一部分是命名空间的前缀,它确定了这个名称所在的命名空间
<b:book xmlns:b="http://www.yeahstack.com/xml/b">
我们在根标签中添加了一个 xmlns:b 属性,xmlns 代表的是 xml namespace,b 是我们声明的命名空间前缀,b 本身并没有意义,可以将它理解为是 http://www.yeahstack.com/xml/b 的一个别名,我们在标签中使用 b,就相当于使用这个 uri 地址。一旦使用了 b 这个前缀,就代表这个标签是属于 http://www.yeahstack.com/xml/b 这个唯一标识命名空间下的元素
我们还可以在一个文档中定义多个命名空间,如下的语法也是没有问题的:
<b:book xmlns:b="http://www.yeahstack.com/xml/b"
xmlns:a="http://www.yeahstack.com/xml/a">
五、默认的命名空间
这样我们在文档中就可以使用 a 和 b 两个前缀来区分不同的命名空间中的标签了.但是实际上咱们所使用的前缀并不友好,为了方便识别在开发中尽量使用便于识别的前缀,比如 book,author 等
采用以上的方式声明命名空间已经可以很好的解决了咱们的问题,但是这种方式显得有一些麻烦,因为每一个标签都需要加上一个前缀,不如直接写标签名来的爽快.所以 XML 还给我们提供了一种方式可以声明一个默认的命名空间,具体如下:
<book xmlns="http://www.yeahstack.com/xml/b"
xmlns:a="http://www.yeahstack.com/xml/a">
上边的 xmlns="http://www.yeahstack.com/xml/b" 并没有指定前缀,那么这种没有指定前缀的命名空间就会作为页面中元素的默认命名空间,除非在标签中使用其他命名空间的前缀,否则解析器都会认为元素是在默认命名空间下存在
但是要注意的是一个文档中只能有一个默认的命名空间,如下的语法是错误的:
<book xmlns="http://www.yeahstack.com/xml/b"
xmlns="http://www.yeahstack.com/xml/a">
这里我们指定了两个命名空间而都没有使用前缀,解析器在解析文档时会不知道使用哪个命名空间,所以在一个文档中只能有一个默认的命名空间,其他命名空间必须使用前缀
六、举例
我们这里就以 Spring 中的 XML 文件来进行说明
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
https://www.springframework.org/schema/mvc/spring-mvc.xsd">
<context:component-scan base-package="com.springmvc"></context:component-scan>
<bean id="internalResourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/jsp/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
<mvc:default-servlet-handler/>
<mvc:annotation-driven></mvc:annotation-driven>
</beans>
<?xml version="1.0" encoding="UTF-8"?>: XML 格式头文件
xmlns="http://www.springframework.org/schema/beans": 这里的命名空间没有带前缀,是默认的命名空间,这个空间是属于那些没有写任何前缀的标签,例如 bean 标签就没有写任何前缀,bean 标签就属于这个命名空间
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance": xsi 是 XML Schema Instance 的缩写,也可以自己起别的前缀名(不推荐自己乱起名字,使用约定俗成的前缀可以使 xml 文件结构更加清晰)
这是一个特殊的命名空间,它已经定义好了4个标签分别是 xsi:type、xsi:nil、xsi:schemaLocation、xsi:noNamespaceSchemaLocation,这几个属性只有声明了 xsi 命名空间后才能使用,这里我们只讲我们用到的 xsi:schemaLocation 这个属性
xsi:schemaLocation:这个标签的值分为两个部分,对应的是两个 uri 地址,使用换行符将它们分开,第一行是 xsd 文件的目标命名空间,第二行是 xsd 文件的所在物理位置,它的的作用是引用 xsd 文件来校验指定命名的格式,例如:
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
它代表的意思是 引用外部的 http://www.springframework.org/schema/beans/spring-beans.xsd 约束文件来对 http://www.springframework.org/schema/beans 这个名称空间里的元素进行对应的语法、规范约束
<context:component-scan base-package="com.springmvc"></context:component-scan> :
对应的名称空间是 xmlns:context="http://www.springframework.org/schema/context",主要是起到一个唯一标识的作用