DOM解析

作者: 墨线宝 | 来源:发表于2023-07-23 16:44 被阅读0次

    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为例讲解各个方法

    <!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树
    public static Document getDefaultDocument(File file) throws ParserConfigurationException, IOException, SAXException {
        // 创建DocumentBuilderFactory,用于取得DocumentBuilder
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        DocumentBuilder builder = factory.newDocumentBuilder();
        // 使用DocumentBuilder进行DOM树的转换操作,读取指定的xml文件
        return builder.parse(file);
    }
    
    操作dom树读取节点内容
    // 获取mapper节点,通过标签名获取
    NodeList mapperList = document.getElementsByTagName("mapper");
    // 遍历节点    NodeList的索引从0开始
    for(int i = 0;i<mapperList.getLength();i++){
      Node mapperNode = mapperList.item(i);
      if(mapperNode.hasAttributes()){
        // 找到mapper节点的namespace属性节点
        Node namespace = mapperNode.getAttributes().getNamedItem("namespace");
        // namespace属性节点内容
        System.out.println(namespace.getNodeValue());
      }
      if(mapperNode.hasChildNodes()){
        // mapper节点的子节点
        NodeList childNodes = mapperNode.getChildNodes();
        // 遍历子节点
        for(int j = 0;j<childNodes.getLength();j++){
          Node childNode = childNodes.item(j);
          if("resultMap".equals(childNode.getNodeName())){
            //TODO
          } else if("select".equals(childNode.getNodeName())){
            //TODO
          } else if("insert".equals(childNode.getNodeName())){
            //TODO
          }
        }
      }
    }
    
    创建dom节点
    // 创建test节点作为根节点
    Element root = document.createElement("test");
    
    // 创建type属性
    Attr type = document.createAttribute("type");
    type.setNodeValue("test");
    // 创建id节点
    Element id = document.createElement("id");
    // 创建文本节点,并放到id节点中
    id.appendChild(document.createTextNode("001"));
    // 创建name节点
    Element name = document.createElement("name");
    // 创建文本节点,并放到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 指定要使用的输出流对象(可以向文件输出,也可以指定其他输出流)
    TransformerFactory factory = TransformerFactory.newInstance();
    Transformer transformer;
    try {
        transformer = factory.newTransformer();
    } catch (TransformerConfigurationException e) {
        throw new RuntimeException("创建Transformer失败",e);
    }
    // 设置xml属性 只设置了version和ecoding  还需要设置其他属性可以从OutputKeys中查找
    transformer.setOutputProperty(OutputKeys.VERSION,"1.0");
    transformer.setOutputProperty(OutputKeys.ENCODING,"UTF-8");
    // dom数据源
    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中用来设置解析器行为的一些配置可能会用到的方法,但是就不写代码示例了

    // 该方法指定解析器是否将CDATA节点转换为文本节点,以及是否将它和周围的文本节点合并,默认false
    public void setCoalescing(boolean coalescing)
    
    // 指定解析器是否展开外部实体引用,如果为true,外部数据将插入文档,默认true
    public void setExpandEntityReferences(boolean expandEntityRef)
     
    // 指定解析器是否忽略文档中的注释。默认false
    public void setIgnoringComments(boolean ignoreComments)
      
    // 指定是否忽略元素内容中的空白。默认false
    public void setIgnoringElementContentWhitespace(boolean whitespace) 
    
    // 指定是否支持XML的名称空间。默认false
    public void setNamespaceAware(boolean awareness)
    
    // 是否验证文档,默认false
    public void setValidating(boolean validating)
    

    DOM解析的优缺点

    • 优点:可以根据需求在树形结构的各节点之间导航,轻易获取到所要的数据,也可以很容易地添加和修改树中的元素。如果需要对XML文档中的数据重复读取,DOM的优势非常明显,且在内存中可以随机访问XML文档的每个元素
    • 缺点: 需要将整个XML加载到内存中并构造树形结构,当XML文档的数据量较大时,会造成较大的内存消耗。

    https://zhhll.icu/2020/xml/DOM/DOM解析/

    本文由mdnice多平台发布

    相关文章

      网友评论

          本文标题:DOM解析

          本文链接:https://www.haomeiwen.com/subject/ngofpdtx.html