美文网首页
java之XML

java之XML

作者: 云师兄 | 来源:发表于2018-10-11 22:14 被阅读9次

引子

今天整理这方面的内容是因为最近在看《Spring 源码深度解析》这本书,书中一开始就讲到了如何解析Spring配置文件,加载Bean的过程,所以今天就来回顾下,java是如何处理XML文件的。

XML文档结构

  • XML文档
    文档头有下面两种形式:
<?xml version="1.0"?>
<?xml version="1.0" encoding="UTF-8"?>
  • 文档类型定义DTD
    文档类型定义是确保文档正确的一个重要机制,但它并不是必须的。
  • 元素
    元素由根元素和子元素组成,元素内部有文本和属性,如:
<size unit="pt">36</size>

unit就是属性,36就是文本。

  • CDATA部分
    用<![CDATA[...]]>来包括一些特殊字符串,不让它们被解释为标记,如:
<!CDATA[you & me]>

在XML文件中来表示字符串"you & me"。

  • 处理指令
    XML文档中要执行的指令用<?...?>来包括指令,如:
<?xml version="1.0">

XML文档解析

java库提供了两种XML解析器:

  • 像DOM解析器这样的树型解析器:将XML文档转换为树结构
  • 像SAX解析器这样的流机制解析器

下面是使用DOM解析器来解析XML文件的例子:

try {
    DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
    DocumentBuilder builder = documentBuilderFactory.newDocumentBuilder();
    File file = null;
    Document document = builder.parse(file);
    // 获取根元素
    Element root= document.getDocumentElement();
    // 获取子元素
    NodeList children = root.getChildNodes();
    for(int i=0;i<children.getLength();i++){
        Node node = children.item(i);
        if(node instanceof Element){
            Element element = (Element) node; // 这才是子元素
        } else if(node instanceof Attr){
            Attr attr = (Attr) node;
        } else if(node instanceof Text) {
            Text text = (Text) node;
        }
        // ...
    }
} catch (Exception e){
    System.out.println(e);
}

通过DocumentBuilder对象将XML文件解析为Document对象。这里面要注意的是获取子元素的时候,getChildNodes方法返回的是元素下的所有Node接口实例,除了元素,还有属性,文本,它们都是Node接口的实现类。在IDEA中使用Ctrl+H快捷键可得:


继承关系

回到例子中,当节点类型为Text时,要想获取文本的内容,可以使用: text.getData();方法,另外如果想遍历某个元素的所有属性,可以使用如下方法:

NamedNodeMap attributes = element.getAttributes();
for (int j=0;j<attributes.getLength();j++){
    Node attribute = attribute.item(i);
    String name = attribute.getNodeName();
    String value = attribute.getNodeValue();
}

如果想获取指定属性的值,则可以使用如下:

String unit = element.getAttribute("unit");

验证XML

如果在特定场景下,我们想控制XML文档中某个元素下只能有A属性,这就要求我们在解析XML的时候,自行校验,并且如果添加了其他属性,那还得同时去修改校验的代码,这无疑不利于代码的维护,灵活性也低。为此引入下面要讲的内容:
如果要指定XML文档结构,比如提供一个文档类型定义(DTD)或一个XML Schema定义(XSD),它们包含了用于解释文档应如何构成的规则。
XSD和DTD相比,由于XSD是用XML语言实现的,可以表达更复杂的验证条件,而DTD的规则配置是自身的语法,所以XSD是设计用来替代DTD的。spring boot中pom.xml文件就是使用XSD开校验的,根节点配置如下:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

添加XML验证的优点就在于比如你想控制某个元素只有两个子元素name,size,那么就不需要再使用之前遍历DOM树那套繁琐的代码了,而是直接使用下面这种方式就可以直接获取了:

Element nameElement = (Element) children.item(0);
Element sizeElement = (Element) children.item(1);

使用XPATH来定位信息

之前使用遍历的DOM树的方法来获取某一层的元素的内容,比如下面XML文件中的version的值:

<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.10.RELEASE</version>
        <relativePath/>
    </parent>
</project>

这种方法是比较繁琐的,如果使用XPath语言,就可以用表达式/project/parent/version来直接定位到version元素上,操作要简单的多。
另外,还可以使用"/project/parent"方式来表示parent元素下的所有子元素。
在java api中提供了XPATH的方法调用:

// 获取元素的文本
String version = xPath.evaluate("/project/parent/version",document);
// 获取节点
Node node = (Node)xPath.evaluate("/project/parent/version",document, XPathConstants.NODE);
// 获取一组节点
NodeList nodeList = (NodeList)xPath.evaluate("/project/parent",document, XPathConstants.NODESET);

使用命名空间

java语言使用包来避免相同的类名冲突,只要在不同包中即可。XML中也有类似的命名空间namespace机制,命名空间用URI来表示。回到POM.xml文件中:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.10.RELEASE</version>
        <relativePath/>
    </parent>
</project>

其中xmlns:prefix="..." 的形式用于定义命名空间和前缀,此处前缀是xsi,这样xsi:schemaLocation指的是命名空间http://maven.apache.org/POM/4.0.0中的xsi。

使用SAX解析器

DOM解析器将XML文档解析为树形的数据结构,当文档很大,处理算法比较简单的时候,可以在运行时解析节点,而不需要看到完整的树形结构,此时为提高效率,可以使用流机制解析器:SAX解析器。
SAX解析器使用的是事件回调,在XML解析过程中,会产生各种事件,我们需要一个处理器为不同的事件定义事件动作。所以相比于DOM树解析器,使用SAX解析器的优点在于不需要关心要找的元素出现的上下文环境,而且也不必存储树形结构。ContentHandle接口定义了这些回调方法:


ContentHandler接口
  • startElement在遇到起始标签时调用
  • characters在遇到字符数据时调用
  • startDocument在文档开始时调用
    在使用时覆盖接口这些方法就能添加自己的控制。

下面是一个使用SAX解析器解析XML文档的示例:

try {
    SAXParserFactory saxParserFactory = SAXParserFactory.newInstance();
    SAXParser parser = saxParserFactory.newSAXParser();
    File file = null;
    DefaultHandler defaultHandler = new DefaultHandler(){
      public void startElement (String uri, String localName,String qName, Attributes attributes){
        // 处理逻辑
      }
    };
    parser.parse(file,defaultHandler);
} catch (Exception e){
}

SAX解析器在执行parse方法进行解析的时候,传入的DefaultHandler对象就是之前提到的处理器:ContentHandle接口的实现类。

相关文章

网友评论

      本文标题:java之XML

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