Java之XML解析

许多的Java框架都支持用户本身配置,其中很常见的就是使用XML文件进行配置。
本篇讲XML在Java中的解析,最后会简单地讲Mybatis在解析XML时的作法。

XML 文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>

    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC" />
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver" />
                <property name="url" value="jdbc:mysql:///mybatis" />
                <property name="username" value="root" />
                <property name="password" value="121213" />
            </dataSource>
        </environment>
    </environments>

    <mappers>
        <mapper resource="com/mapper/IUserMapper.xml" />
    </mappers>

</configuration>

XML 文件较为常见的就是上边的样子node

  1. 第一行是文档头
  2. 第二行是文档类型定义(DTD,有时是Schema。做用都是为了保证文档正确)
  3. 其他的就是元素

须要注意的地方mysql

  1. XML 是大小写敏感的
  2. XML 的属性必须用引号括起来
  3. XML 全部属性必须有值

Java DOM解析器

  1. Java读入一个XML文件须要DocumentBuilder类,能够经过DocumentBuilderFactory类构建。sql

    DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
    DocumentBuilder builder = factory.newDocumentBuilder();
  2. 以后就能够经过DocumentBuilder类的parse方法读入一个XML文件啦。
    parse接受多种参数,如FileInputStream等。express

    InputStream stream 
       = Thread.currentThread().getContextClassLoader().getResourceAsStream("mybatis-config.xml");
    Document document = builder.parse(stream);
       或        
    File file = new File("src/main/resources/mybatis-config.xml");
    Document document = builder.parse(file);
  3. 此时就已经可使用了,须要注意的一点就是,元素之间的空白字符也会被认为是子元素。
    在没有使用DTD或Schema的状况下,须要咱们手动判断元素是否继承自Element
    Node就是咱们XML文件上的一个元素,Node类还有不少实用的方法,这里就不一一列举了。apache

    // 获取根元素
    Element root = document.getDocumentElement();
    // 获取孩子元素
    NodeList childNodes = root.getChildNodes();
    for (int i = 0; i < childNodes.getLength(); i++) {
        Node node = childNodes.item(i);
        if (node instanceof Element) {
            System.out.println(node.getNodeName() + " " + node.getTextContent());
        }
    }
  4. 若是使用了XML校验,也就是DTD或者Schema。在使用时能够进行设置。
    解析器经过解析校验的文件,能够知道哪些元素没有文本节点的子元素,所以能够帮咱们剔除空白字符。mybatis

    DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
    // 开启校验
    factory.setValidating(true);
    // 忽略空白字符
    factory.setIgnoringElementContentWhitespace(true);
  5. 有个地方须要特殊处理,若是解析的是一个流的话,即parse(inputStream)
    而且在咱们的XML文件中使用的是DTD文件的相对路径,
    则须要提供一个实体解析器,用于指定DTD文件。app

    <!-- XML中指定DTD文件时使用了相对位置 -->
    <!DOCTYPE configuration SYSTEM "mybatis-config.dtd">      
    
    // 实体解析器
    public class MyEntityResolver implements EntityResolver {
        @Override
        public InputSource resolveEntity(
               String publicId, 
               String systemId) throws SAXException, IOException {
                   
           InputStream stream = Thread.currentThread()
                                      .getContextClassLoader()
                                      .getResourceAsStream("mybatis-config.dtd");
           return new InputSource(stream);
        }
    }
    
    // 构建Builder时,设置实体解析器
    DocumentBuilder builder = factory.newDocumentBuilder();
    builder.setEntityResolver(new MyEntityResolver());

Java XPath定位信息

在定位XML文件信息时,使用获取元素,再判断元素是不是目标元素的办法很是痛苦。
Java 为咱们提供了好用的XPath类。框架

  1. 建立XPath对象ide

    XPathFactory xPathFactory = XPathFactory.newInstance();
    XPath xPath = xPathFactory.newXPath();
  2. 编写表达式,调用evaluate方法求值学习

    // Document document = ...;
    // 获取dataSource元素
    String expression1 = "/configuration/environments/environment/dataSource";
    Node node = (Node) xPath.evaluate(
                        expression1, 
                        document, 
                        XPathConstants.NODE);
    
    // 也能够在当前已得到的节点下开始查找, 获取dateSource的type属性
    String type = xPath.evaluate("@type", node);
    
    // 获取mappers下的第一个mapper子元素的resource属性,注意!索引是从1开始的
    String expression2 = "/configuration/mappers/mapper[1]/@resource", document);
    String resource = xPath.evaluate(
                    expression2, 
                    document);

Mybatis 解析XML

  1. XPathParser
    在mybatis中,解析XML使用了XPathParser类,这个类是mybatis自定义的,
    类中持有一个Document对象,是咱们的XML文件,还有一个XPath对象。
    类中提供了定位信息的方法,使用的就是Java提供的XPath类。
    XPathParser解析出的元素用一个XNode对象存储。

    public class XPathParser {
    
        private Document document;
        private boolean validation;
        private EntityResolver entityResolver;
        private Properties variables;
        private XPath xpath;
        //...
        
        public XNode evalNode(String expression) {
            return evalNode(document, expression);
        }
        
        public XNode evalNode(Object root, String expression) {
            Node node = (Node) evaluate(expression, root, XPathConstants.NODE);
            if (node == null) {
                return null;
            }
            return new XNode(this, node, variables);
        }
    
        private Object evaluate(String expression, Object root, QName returnType) {
            try {
                return xpath.evaluate(expression, root, returnType);
            } catch (Exception e) {
                throw new BuilderException("Error evaluating XPath.  Cause: " + e, e);
            }
        }            
        //...
    }
  2. XNode
    mybatis将Node类进一步封装,用XNode表示。
    当构造XNode对象时,会自动解析出元素的元素名、元素的属性等。
    此外XNode中提供了获取子元素、获取父元素等行为,因为持有XPathParser对象,
    XNode中还提供了定位信息的方法。

    public class XNode {
    
        private Node node;
        private String name;
        private String body;
        private Properties attributes;
        private Properties variables;
        private XPathParser xpathParser;
        
        public XNode(XPathParser xpathParser, Node node, Properties variables) {
            this.xpathParser = xpathParser;
            this.node = node;
            this.name = node.getNodeName();
            this.variables = variables;
            this.attributes = parseAttributes(node);
            this.body = parseBody(node);
        }
        
        public XNode evalNode(String expression) {
            return xpathParser.evalNode(node, expression);
        }
        ...
    }
  3. mybatis中,获取根元素只需这样写
    XNode root = xPathParser.evalNode("/configuration");
    以后获取configuration元素下的mappers是这样写
    root.evalNode("mappers")
  4. DTD
    mybatis的XML文件使用了DTD,使用解析流的形式解析XML时,
    mybatis也提供了实体解析器XMLMapperEntityResolver,
    mybatis的DTD文件路径是/org/apache/ibatis/builder/xml/mybatis-3-config.dtd

结语

了解Java提供的解析XML类,再去看各大框架如何解析XML就很容易了。从这些框架中学习到如何封装好解析的行为,让咱们使用的过程当中,没必要花费太多功夫去获取XML文档信息,而是直接使用信息。这也是很是大的收获呀。

相关文章
相关标签/搜索