DOM解析
DOM解析介绍
DOM是基于树形结构的XML解析方式,会将整个XML文档读入内存并构建一个DOM树,基于这棵树形结构对各个节点进行操作。XML文档中每个成分都是一个节点,整个文档是一个文档节点,每个XML标签对应一个元素节点,包含在XML标签中的文本是文本节点,每一个XML属性是一个属性节点,注释属于注释节点。
DOM树所提供的随机访问方式很灵活方便,可以任意地控制整个XML文档中的内容,但是DOM分析器把整个XML文件转化为DOM树放到了内存中,当文档比较大或者结构比较复杂时,对内存需求比较高。
DOM接口介绍
介绍一下DOM解析中的核心接口
- DocumentBuilderFactory 获取DOM解析器的工厂类
- DocumentBuilder DOM解析器的标准接口
- Document:代表了整个XML文档,表示整棵DOM树的根,常用方法
- public NodeList getElementsByTagName(String tagname); 取得指定节点名称的NodeList
- public Element createElement(String tagName) 创建一个指定名称的节点
- public Text createTextNode(String data) 创建一个文本内容节点
- public Attr createAttribute(String name) 创建一个属性
- NodeList: 表示一个节点的集合
- public int getLength(); 取得节点的个数
- public Node item(int index); 根据索引取得节点对象
- Node:每一个Node代表了DOM树中的一个节点
- public Node appendChild(Node newChild) 在当前节点下增加一个新的节点
- public NodeList getChildNodes() 得到本节点下的全部子节点
- public Node getFirstChild() 得到本节点下的第一个子节点
- public Node getLastChild() 得到本节点下最后一个子节点
- public boolean hasChildNodes() 判断是否还有其他节点
- public boolean hasAttributes() 判断是否还有其他属性
- public String getNodeValue() 获取节点内容
- NamedNodeMap:表示一组节点和其唯一名称对应的一一对应关系,主要用于属性节点的表示
Node接口的实现
- Attr 属性节点
- CDATASection CDATA片段
- CharacterData 字符数据
- Comment 注释
- Document 文档
- DocumentFragment 文档片段
- DocumentType 文档类型
- Element 元素
- Entity 实体
- EntityReference 实体引用
- Notation 符号
- ProcessingInstruction 处理指令
- Text 文本数据
DOM解析示例
xml文件示例
下面以mybatis的一个mapper.xml为例讲解各个方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.apache.ibatis.submitted.rounding.Mapper"> <resultMap type="org.apache.ibatis.submitted.rounding.User" id="usermap"> <id column="id" property="id"/> <result column="name" property="name"/> <result column="funkyNumber" property="funkyNumber"/> <result column="roundingMode" property="roundingMode"/> </resultMap>
<select id="getUser" resultMap="usermap"> select * from users </select> <insert id="insert"> insert into users (id, name, funkyNumber, roundingMode) values ( #{id}, #{name}, #{funkyNumber}, #{roundingMode} ) </insert>
<resultMap type="org.apache.ibatis.submitted.rounding.User" id="usermap2"> <id column="id" property="id"/> <result column="name" property="name"/> <result column="funkyNumber" property="funkyNumber"/> <result column="roundingMode" property="roundingMode" typeHandler="org.apache.ibatis.type.EnumTypeHandler"/> </resultMap> <select id="getUser2" resultMap="usermap2"> select * from users2 </select> <insert id="insert2"> insert into users2 (id, name, funkyNumber, roundingMode) values ( #{id}, #{name}, #{funkyNumber}, #{roundingMode, typeHandler=org.apache.ibatis.type.EnumTypeHandler} ) </insert>
</mapper>
|
方法使用示例
将xml文件转为dom树
1 2 3 4 5 6 7
| public static Document getDefaultDocument(File file) throws ParserConfigurationException, IOException, SAXException { DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = factory.newDocumentBuilder(); return builder.parse(file); }
|
操作dom树读取节点内容
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| NodeList mapperList = document.getElementsByTagName("mapper");
for(int i = 0;i<mapperList.getLength();i++){ Node mapperNode = mapperList.item(i); if(mapperNode.hasAttributes()){ Node namespace = mapperNode.getAttributes().getNamedItem("namespace"); System.out.println(namespace.getNodeValue()); } if(mapperNode.hasChildNodes()){ NodeList childNodes = mapperNode.getChildNodes(); for(int j = 0;j<childNodes.getLength();j++){ Node childNode = childNodes.item(j); if("resultMap".equals(childNode.getNodeName())){ } else if("select".equals(childNode.getNodeName())){ } else if("insert".equals(childNode.getNodeName())){ } } } }
|
创建dom节点
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| Element root = document.createElement("test");
Attr type = document.createAttribute("type"); type.setNodeValue("test");
Element id = document.createElement("id");
id.appendChild(document.createTextNode("001"));
Element name = document.createElement("name");
name.appendChild(document.createTextNode("张三"));
root.setAttributeNode(type);
root.appendChild(id); root.appendChild(name); document.appendChild(root);
|
DOM持久化
将生成的xml保存到文件中,需要使用TransformerFactory、Transformer、DOMSource和StreamResult四个类来完成
类介绍
- TransformerFactory 取得一个Transformer对象
- DOMSource 接收Document对象
- StreamResult 指定要使用的输出流对象(可以向文件输出,也可以指定其他输出流)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| TransformerFactory factory = TransformerFactory.newInstance(); Transformer transformer; try { transformer = factory.newTransformer(); } catch (TransformerConfigurationException e) { throw new RuntimeException("创建Transformer失败",e); }
transformer.setOutputProperty(OutputKeys.VERSION,"1.0"); transformer.setOutputProperty(OutputKeys.ENCODING,"UTF-8");
DOMSource source = new DOMSource(document);
StreamResult result = new StreamResult(new File(fileName)); try { transformer.transform(source,result); } catch (TransformerException e) { throw new RuntimeException("输出文件错误",e); }
|
解析器自定义设置方法介绍
下面介绍一些DocumentBuilderFactory中用来设置解析器行为的一些配置可能会用到的方法,但是就不写代码示例了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| public void setCoalescing(boolean coalescing)
public void setExpandEntityReferences(boolean expandEntityRef)
public void setIgnoringComments(boolean ignoreComments)
public void setIgnoringElementContentWhitespace(boolean whitespace)
public void setNamespaceAware(boolean awareness)
public void setValidating(boolean validating)
|
DOM解析的优缺点
- 优点:可以根据需求在树形结构的各节点之间导航,轻易获取到所要的数据,也可以很容易地添加和修改树中的元素。如果需要对XML文档中的数据重复读取,DOM的优势非常明显,且在内存中可以随机访问XML文档的每个元素
- 缺点: 需要将整个XML加载到内存中并构造树形结构,当XML文档的数据量较大时,会造成较大的内存消耗。