MyBatis 技术内幕 - 基础支持层 - 解析器模块 - XML
模块功能
- 对XPath进行封装,为MyBatis初始化时解析mybatis-config.xml配置文件及映射配置文件提供支持
- 处理动态SQL语句中的占位符
XML 简介1
- 简介:XML(全名:EXtensible Markup Language)可扩展标记性语言
- 目的:用于数据传输与存储
- 结构:XML 文档形成了一种树结构,它从“根部”开始,然后扩展到“枝叶”
- 组成:
- 文档声明:
- 定义版本及编码格式
<?xml version="1.0" encoding="ISO-8859-1"?>
- 引入文档类型定义
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
- 定义版本及编码格式
- 命名空间:
- 语法
xmlns:namespace-prefix="namespaceURI"
- namespaceURI : 用于标示命名空间,命名空间中的地址不会被解析
- xmlns:(全名:XML NameSpace ,XML 命名空间)
- 作用
- 场景:当两个不同的XML中定义了相同的元素标签,比如table,在A文件中table表示表格,B文件中table表示桌子;当两个文件混合使用时会出现歧义;
- 解决:通过定义命名空间,设置空间的前缀名称,在 table 前通过前缀进行区分 h:table 与 f:table
- 语法
- 元素:根元素、父元素、子元素
- 属性:描述元素的额外信心
- 实体:定义普通文本的变量(小于、大于、和、单引号、引号需要转义)
- 文本:XML 元素的开始标签与结束标签之间的文本,字符数据
- PCDATA:(全名:parsed character data,被解析的字符数据)
- 实体将会被转译,文本中不应包含 <、>、&、’、"
- CDATA: (全名:UnParsed character data ,不被解析的字符数据)
- 实体不会被转译,实体被当做普通文本处理
<![CDATA[ 普通文本 ]]>
- PCDATA:(全名:parsed character data,被解析的字符数据)
- 文档声明:
- 语言:
- 大小写敏感
- 属性值需要添加引号
XML 验证
-
DTD 2
- 简介:DTD(全名:Document Type Defination ) 文档类型定义
- 作用:定义 XML 文档的合法构建模块
- 功能:
- 每一个XML文件都携带一个关于自身格式的描述
- 对内对外可以按照相同标准交换数据
- 示例
-
XML 外部 DTD 引入
-
animal.xml
<?xml version="1.0" encoding="UTF-8" ?> <!-- 功能:声明文档版本 version , 编码类型 UFT-8 --> <!-- 位置:文档首行 --> <!-- DTD XML 文件外部声明 --> <!DOCTYPE animal SYSTEM "animal_dtd_outside.dtd"> <animal> <category>陆地动物</category> <name>大象</name> <sex>雄性</sex> <age>60</age> </animal>
-
animal_dtd_outside.dtd
<!ELEMENT animal (category,name,sex,age)> <!ELEMENT category (#PCDATA)> <!ELEMENT name (#PCDATA)> <!ELEMENT sex (#PCDATA)> <!ELEMENT age (#PCDATA)>
-
-
XML 内部 DTD 定义
<?xml version="1.0" encoding="UTF-8" ?> <!-- 功能:声明文档版本 version , 编码类型 UFT-8 --> <!-- 位置:文档首行 --> <!-- DTD XML 文件内部声明 --> <!-- 语法格式 !DOCTYPE 根元素名称 [包含的子元素及子元素类型] --> <!DOCTYPE animal [ <!ELEMENT animal (category,name,sex,age)> <!ELEMENT category (#PCDATA)> <!ATTLIST category id CDATA "0"> <!ATTLIST category name CDATA #REQUIRED > <!ATTLIST category description CDATA #IMPLIED > <!ATTLIST category ref CDATA #FIXED "animal"> <!ELEMENT name (#PCDATA)> <!ELEMENT sex (#PCDATA)> <!ELEMENT age (#PCDATA)> <!ENTITY elephant "大象" > ]> <!-- !ELEMENT 根元素 (子元素) 声明根元素包含的所有子元素名称 --> <!-- !ELEMENT 子元素 (类型) 声明子元素的数据类型 --> <!-- !ATTLIST 元素名称 属性名称 属性类型 默认参数 --> <!-- !ENTITY 实体名称 实体内容 > <!-- 默认参数 "" 默认数值 --> <!-- 默认参数 #REQUIRED 必要参数 --> <!-- 默认参数 #IMPLIED 可选参数 --> <!-- 默认参数 #FIXED 固定数值 --> <!-- 元素类型:PCDATA (全名:Parsed Character Data) 被解析的字符数据 --> <!-- 元素类型:CDATA (全名:Unparsed Character Data) 不应由 XML 解析器进行解析的文本数据 --> <!-- 元素类型:EMPTY 空,如 <br/> <p/> --> <!-- 元素类型:ANY 任意数据类型 --> <!-- 元素次数:+ 至少出现一次 --> <!-- 元素次数:* 任意次数 --> <!-- 元素次数:? 零次或一次 --> <!-- 实体定义: < 表示 < --> <!-- 实体定义: > 表示 > --> <!-- 实体定义: 自定义 --> <!-- 实体含义: 定义普通文本的变量 --> <animal> <category id="land" name="land" ref="animal">陆地动物</category> <name>&elephant;</name> <sex>雄性</sex> <age>60</age> </animal>
-
-
XSD3
- 简介:XSD(全名:XML Schema Defination ,XML 纲要定义)
- 作用:描述XML文档结构;作为DTD的替代者
- 优势:相比与DTD
- 支持数据类型
- 支持命名空间
XML 解析
- DOM4(全名:Document Object Model ,文档对象模型)
- 实现原理:将XML文件解析成树形结构的DOM,即Document对象,将对XML各个属性节点的操作转为对DOM中各个节点(Node)的操作
- XML 文档 --> 文档节点
- XML 标签 --> 元素节点
- XML 属性 --> 属性节点
- XML 注释 --> 注释节点
- 方式优势
- 节点查询:DOM结构的特点能够定位根节点、兄弟节点、子节点
- 便于修改:DOM结构的特点能够增加或删除某个节点
- 配合XPath使用,能够查询到任意节点元素
- 方式劣势
- 内存消耗:需要一次性将整个XML文件加载到内存中并进行解析;当XML文档的数据量较大时,会造成较大的资源损耗
- 实现原理:将XML文件解析成树形结构的DOM,即Document对象,将对XML各个属性节点的操作转为对DOM中各个节点(Node)的操作
- SAX(全名:Simple API for XML)
- 实现原理:“推模式”,顺序读入XML文档内容,解析某一类型的节点时触发该类型节点的回调函数,即是将整个XML文档推给SAX进行处理
- 方式优势:
- 节省内存:无需加载整个XML文档,每次只加载XML文档的一部分;处理过程中内存不记录XML中的数据
- 方式劣势:
- 维护关系:因为加载过程中不记录XML文档中的内存,需要额外维护一份XML文档中各个节点之间的关系
- 单向处理:流式处理方式,只能从头到尾的单向处理
- 不支持XPath
- StAX(全名:Streaming API for XML)
- 实现原理:“拉模式”,应用程序通过StAX推进解析进程,控制整个解析进行,决定何时停止,可同时解析多个XML文档
XML 查询
-
语言名称:XPath5
-
语言语法
表达式 含义 nodename 选取指定节点的所有子节点 / 从根节点选取指定节点 // 根据指定的表达式,在整个文档中选取匹配的节点,不考虑匹配节点在文档中的位置 . 选取当前节点 . . 选取当前节点的父节点 @ 选取属性 * 匹配任何元素节点 @* 匹配任何属性节点 node() 匹配任何类型的节点 text() 匹配文本节点 [] 指定某个条件,用于查找某个特定节点或包含某个指定值的节点 -
代码示例
-
XML 文档:book.xml
<book> <category id="technology"> <name>Spring</name> <purchaseDate>2018-11-11</purchaseDate> </category> <category id="literature"> <name>围城</name> <purchaseDate>2014-10-10</purchaseDate> </category> <category id="finance"> <name>金融</name> <purchaseDate>2018-12-12</purchaseDate> </category> </book>
- 注意事项
-
XML 文档顶部不要加入,否则解析时会出现异常提示文档根元素 “book” 必须匹配 DOCTYPE 根 "null"
<?xml version="1.0" encoding="UTF-8"?>
-
异常的原因在于XML文档不符合规范,需要标注文档所遵循的DTD(全名:Document Type Defination 文档类型定义),如:mapper.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.test.mapper.TestMapper">
-
- 注意事项
-
查询测试
package com.mybatis.test; import org.w3c.dom.Document; import org.w3c.dom.NodeList; import org.xml.sax.ErrorHandler; import org.xml.sax.SAXException; import org.xml.sax.SAXParseException; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.xpath.*; import java.io.IOException; public class XPathTest { public static void main(String[] args) throws ParserConfigurationException, IOException, SAXException, XPathExpressionException { DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); // 开启验证 /** * 功能:指定由此代码生成的解析器将验证被解析的文档 * 解释:此处“验证”是指 xml 规范中定义的验证解析器,实际上仅控制 dtd 验证 * 更改:要使用现代模式语言(如 w3c xml schema 或 relax ng)而不使用 dtd * 将解析器配置为非验证解析器,方法是将 setvalidating(boolean) 方法保留为 false, * 然后使用 setschema(schema) 方法将一个模式与解析器关联 */ documentBuilderFactory.setValidating(true); /** * 功能:指示是否将工厂配置为生成具有感知命名空间功能的解析器 * 解释:XML Namespace (xmlns) 属性,XML文档的命名空间,其作用是区分不同文档中的相同元素名称的含义 * 举例:A文档中table表示页面结果表格,B文档中table表示桌子,当A/B文档联合使用时会出现歧义,所以需要命名空间进行标识 * 位置:命名空间放置在文档的开始位置 * 作用:当命名空间被定义在元素的开始标签中时,所有带有相同前缀的子元素都会与同一个命名空间相关联 * 链接:http://www.w3school.com.cn/xml/xml_namespaces.asp */ documentBuilderFactory.setNamespaceAware(false); /** * 功能:指定由此代码生成的解析器将忽略注释 */ documentBuilderFactory.setIgnoringComments(true); /** * 功能:指定由此工厂创建的解析器在解析 xml 文档时,必须删除元素内容中的空格(有时也可以称作“可忽略空格”,请参阅 xml rec 2.10)。 * 注意:只有在空格直接包含在元素内容中,并且该元素内容是只有一个元素的内容模式时,才能删除空格(请参阅 xml rec 3.2.1)。 * 由于依赖于内容模式,因此此设置要求解析器处于验证模式。 */ documentBuilderFactory.setIgnoringElementContentWhitespace(false); /** * 功能:指示是否将工厂配置为生成满足以下条件的解析器:该解析器将 cdata 节点转换为 text 节点,并将其附加到相邻(如果有)的 text 节点。 */ documentBuilderFactory.setCoalescing(false); /** * 功能:指示是否将工厂配置为生成扩展实体引用节点的解析器 */ documentBuilderFactory.setExpandEntityReferences(true); // 设置 DocumentBuilder DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder(); // 设置异常处理对象 documentBuilder.setErrorHandler(new ErrorHandler() { public void warning(SAXParseException exception) throws SAXException { System.out.println("warning:"+exception.getMessage()); } public void error(SAXParseException exception) throws SAXException { System.out.println("exception:"+exception.getMessage()); } public void fatalError(SAXParseException exception) throws SAXException { System.out.println("fatal:"+exception.getMessage()); } }); // 将文档加载到一个Document对象中,资源查找通过绝对路径 String resource = "src\\main\\java\\com\\mybatis\\resources\\book.xml"; Document document = documentBuilder.parse(resource); // 创建 XPathFactory XPathFactory xPathFactory = XPathFactory.newInstance(); // 创建 XPath 对象 XPath xPath = xPathFactory.newXPath(); // 编译 XPath 表达式 // 1.查询书名为Spring的购买时间 System.out.println("查询书名为Spring的购买时间:"); xPathEvaluateResult(xPath,document,"//category[name='Spring']/purchaseDate/text()"); // 2.查询技术类型书籍名称 System.out.println("查询技术类型书籍名称:"); xPathEvaluateResult(xPath,document,"//category[@id='technology']/name/text()"); // 3.查询所有书籍名称 System.out.println("查询所有书籍名称:"); xPathEvaluateResult(xPath,document,"//category/name/text()"); } private static void xPathEvaluateResult(XPath xPath,Document document ,String expression) throws XPathExpressionException { XPathExpression xPathExpression = xPath.compile(expression); Object evaluate = xPathExpression.evaluate(document, XPathConstants.NODESET); NodeList nodeList = (NodeList) evaluate; for(int index = 0 ; index < nodeList.getLength() ; index++){ System.out.println(nodeList.item(index).getNodeValue()); } } }
-