解析XML的方式分为两种:

  • 文档对象模型(Document Object Model)即DOM,该解析器为树形解析器,DOM解析器会将XML文档解析为对应的树形结构。
  • XML简单接口(Simple API for XML) 即SAX,该解析器为流机制解析器,SAX解析器会将读入XML文档中的各个部分作为事件。

由于DOM解析器会将整个XML文档的内容转化为树形结构,故需要消耗大量的内存。其好处在于,我们可以随时查找之前的元素或文本。如果,我们不需要关注已遍历过的文本内容,那么我们就选用SAX解析方式。

下面我们来用两种不同的解析方式,遍历如下XML文档中的内容(由于文档校验DTD或者XML SCHEMA较为复杂,此处我们只是介绍如何遍历XML元素,故略去文档校验的部分代码)。

<?xml version="1.0" encoding="UTF-8"?>
<Log>
  <!--log filename-->
  <Filename>java</Filename>
  <!--log limit,must between 10485760(10M) and 52428800(50M)-->
  <Limit>20971520</Limit>
  <!--log count,cannot be lower than 1-->
  <Count>100</Count>
  <!--whether to follow the existing file to write,just allow 'TRUE' or 'FALSE' and case insensitive-->
  <isAppend>true</isAppend>
  <!--Whether to compress the log,just allow 'True' or 'FALSE' and case insensitive-->
  <isCompress>TRUE</isCompress>
</Log>

DOM解析器

DOM解析器解析XML文档的步骤为:

  1. 创建工厂对象,并设置工厂对象ignoringComments属性为true,即忽略XML文档中注释。如果不设置IgnoringComments属性为true,那么在遍历子元素时,解析器会将注释作为子元素进行遍历。
DocumentBuilderFactory factory=DocumentBuilderFactory.newInstance();
factory.setIgnoringComments(true);
  1. 生成文档对象Document
DocumentBuilderFactory factory=DocumentBuilderFactory.newInstance();
factory.setIgnoringComments(true);
DocumentBuilder builder=factory.newDocumentBuilder();
Document doc=builder.parse("./logConfig.xml");
  1. 生成根节点元素
DocumentBuilderFactory factory=DocumentBuilderFactory.newInstance();
factory.setIgnoringComments(true);//忽略文档中的注释
DocumentBuilder builder=factory.newDocumentBuilder();
Document doc=builder.parse("./logConfig.xml");
Element root=doc.getDocumentElement();
  1. 遍历Document文档中子元素、属性以及文本内容
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.DocumentBuilder;
import org.w3c.dom.*;
import org.xml.sax.SAXException;
import java.io.IOException;

public class xmlTest
{
 public static void main(String[] args) throws ParserConfigurationException,SAXException,IOException
 {
  DocumentBuilderFactory factory=DocumentBuilderFactory.newInstance();
  factory.setIgnoringComments(true);
  DocumentBuilder builder=factory.newDocumentBuilder();
  Document doc=builder.parse("./logConfig.xml");
  Element root=doc.getDocumentElement();
  System.out.println(root.getTagName());
  NodeList list=root.getChildNodes();//获取根节点下子节点
  for(int i=0;i<list.getLength();i++)//NodeList的getLength()方法返回子节点的个数
  {
   Node node=list.item(i);//方法item(i)获取NodeList中索引位置为i的节点
   if(node instanceof Element)//排除掉子节点中的文本、元素间的空白字符
   {
    Element elem=(Element)node;
    Node text=elem.getFirstChild();//获取元素的文本节点
    System.out.println(elem.getTagName()+":"+((CharacterData)text).getData());//getTagName()方法用于获取元素名称,getData()方法用于获取元素的内容
    NamedNodeMap attrs=elem.getAttributes();//遍历元素的属性,生成NamedNodeMap对象,与NodeList使用方式基本相同,当然如果知道属性名可以调用String getAttribute(String name)直接获取name属性的值
    for(int j=0;j<attrs.getLength();j++)
    {
     Node attr=attrs.item(j);
     System.out.println(attr.getNodeName()+":"+attr.getNodeValue());//getNodeName()获取属性名称,getNodeValue()用于获取属性值
    }
   }
  }
 }
}

流机制解析器

目前Java类库中可用的XML流机制解析器有两种:

  1. SAX解析器
  2. StAX解析器

因此,我们分别介绍两种流机制解析器的使用方法。

SAX解析器

步骤如下:

  1. 创建工厂对象
SAXParserFactory factory=SAXParserFactory.newInstance();
  1. 获取SAXParser对象
SAXParserFactory factory=SAXParserFactory.newInstance();
SAXParser parser=factory.newSAXParser();
  1. 拓展通用类org.xml.sax.helpers.DefaultHandler并
    覆盖类中的方法
  1. startElement(String uri,String lName,String qName,Attributes attrs)
    在遍历元素起始标签时调用
  2. characters(Char[] ch,int start,int length)
    在访问字符内容时调用(元素内容、标签间空白字符)。
  3. endElement(String uri,String lName,String qName)
    在访问元素结束标签时调用。
SAXParserFactory factory=SAXParserFactory.newInstance();
SAXParser parser=factory.newSAXParser();
DefaultHandler handler=new DefaultHandler(){
StringBuilder sb;//记录元素名称,属性名,属性值以及元素内容
public void startElement(String uri,String lName,String qName,Attributes attrs)
   {
    sb=new StringBuilder(qName+" [");//qName元素名
    for(int i=0;i<attrs.getLength();i++)//获取元素属性名及属性值
    {
     sb.append("("+attrs.getLocalName(i)+","+attrs.getValue(i)+")");
    }
    sb.append("]");
   }
   public void characters(char[] ch,int start,int length)//读入文本字符串时调用
   {
    String text=new String(ch,start,length);
    if(text.trim().length()!=0)//剔除元素标签间的空白字符
     sb.append(" "+text);
   }
   public void endElement(String uri,String lName,String qName)//访问结束标签时调用
   {
    System.out.println(sb);//输出元素信息
   }
};
  1. 遍历XML文档
SAXParserFactory factory=SAXParserFactory.newInstance();
SAXParser parser=factory.newSAXParser();
DefaultHandler handler=new DefaultHandler(){
StringBuilder sb;//记录元素名称,属性名,属性值以及元素内容
public void startElement(String uri,String lName,String qName,Attributes attrs)
   {
    sb=new StringBuilder(qName+" [");
    for(int i=0;i<attrs.getLength();i++)
    {
     sb.append("("+attrs.getLocalName(i)+","+attrs.getValue(i)+")");
    }
    sb.append("]");
   }
   public void characters(char[] ch,int start,int length)
   {
    String text=new String(ch,start,length);
    if(text.trim().length()!=0)
     sb.append(" "+text);
   }
   public void endElement(String uri,String lName,String qName)
   {
    System.out.println(sb);
   }
  };
  parser.parse(new File("./logConfig.xml"),handler);//遍历XML文档

完整的代码如下:

import javax.xml.parsers.SAXParserFactory;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.ParserConfigurationException;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
import org.xml.sax.Attributes;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;

public class xmlTest
{
 public static void main(String[] args) throws ParserConfigurationException,SAXException,IOException
 {
  SAXParserFactory factory=SAXParserFactory.newInstance();
  SAXParser parser=factory.newSAXParser();
  DefaultHandler handler=new DefaultHandler(){
   StringBuilder sb;
   public void startElement(String uri,String lName,String qName,Attributes attrs)
   {
    sb=new StringBuilder(qName+" [");
    for(int i=0;i<attrs.getLength();i++)
    {
     sb.append("("+attrs.getLocalName(i)+","+attrs.getValue(i)+")");
    }
    sb.append("]");
   }
   public void characters(char[] ch,int start,int length)
   {
    String text=new String(ch,start,length);
    if(text.trim().length()!=0)
     sb.append(" "+text);
   }
   public void endElement(String uri,String lName,String qName)
   {
    System.out.println(sb);
   }
  };
  parser.parse(new File("./logConfig.xml"),handler);
 }
}
StAX解析器

步骤如下:

  1. 生成工厂对象
XMLInputFactory factory=XMLInputFactory.newInstance();
  1. 生成XMLStreamReader对象
XMLInputFactory factory=XMLInputFactory.newInstance();
XMLStreamReader parser=factory.createXMLStreamReader(Files.newInputStream(new File("./logConfig.xml").toPath()));
  1. 遍历XML文档
    XMLStreamReader对象就是由XML各个部分内容对应的事件组成的集合,通过遍历XMLStreamReader对象来达到遍历XML文档内容的目的,XMLStreamReader类似于迭代器对象,因此我们可以用类似的方法遍历XMLStreamReader对象,如下所示:
XMLInputFactory factory=XMLInputFactory.newInstance();
XMLStreamReader parser=factory.createXMLStreamReader(Files.newInputStream(new File("./logConfig.xml").toPath()));
while(parser.hasNext())//XMLStreamReader对象中是否还存在事件
{
int event=parser.next();//下一个事件
if(event==XMLStreamConstants.START_ELEMENT)//判断当前事件是否表示起始标签,即我们遍历位置在标签起始处。
{
StringBuilder sb=new StringBuilder();
sb.append(parser.getLocalName()+" [");//getLocalName()方法获取元素名
for(int i=0;i<parser.getAttributeCount();i++)//getAttributeCount()获取元素属性数量
{
if(i==0)
sb.append("("+parser.getAttributeName(i)+","+parser.getAttributeValue(i)+")");//getAttributeName(int i)获取索引位置i处的属性名,getAttributeValue(int i)获取获取索引位置i处的属性值
else
sb.append(",("+parser.getAttributeName(i)+","+parser.getAttributeValue(i)+")");
}
sb.append("]");
if(parser.getLocalName()!="Log")
sb.append(" "+parser.getElementText());//getElementText()方法获取元素内容,该方法会内部调用next()方法,故获取元素内容放在获取属性内容之后。该方法源码细节可以查看[JDK10文档](https://docs.oracle.com/javase/10/docs/api/javax/xml/stream/XMLStreamReader.html#getElementText%28%29)
System.out.println(sb);
}
}
}

完整的代码如下所示:

import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamReader;
import javax.xml.stream.XMLStreamConstants;
import javax.xml.stream.FactoryConfigurationError;
import javax.xml.stream.XMLStreamException;
import org.xml.sax.helpers.DefaultHandler;
import org.xml.sax.SAXException;
import java.nio.file.Files;
import java.io.File;
import java.io.IOException;
public class xmlTest
{
 public static void main(String[] args) throws FactoryConfigurationError,XMLStreamException,SAXException,IOException
{
XMLInputFactory factory=XMLInputFactory.newInstance();
XMLStreamReader parser=factory.createXMLStreamReader(Files.newInputStream(new File("./logConfig.xml").toPath()));
while(parser.hasNext())//XMLStreamReader对象中是否还存在事件
{
int event=parser.next();//下一个事件
if(event==XMLStreamConstants.START_ELEMENT)//判断当前事件是否表示起始标签,即我们遍历位置在标签起始处。
{
StringBuilder sb=new StringBuilder();
sb.append(parser.getLocalName()+" [");//getLocalName()方法获取元素名
for(int i=0;i<parser.getAttributeCount();i++)//getAttributeCount()获取元素属性数量
{
if(i==0)
sb.append("("+parser.getAttributeName(i)+","+parser.getAttributeValue(i)+")");//getAttributeName(int i)获取索引位置i处的属性名,getAttributeValue(int i)获取获取索引位置i处的属性值
else
sb.append(",("+parser.getAttributeName(i)+","+parser.getAttributeValue(i)+")");
}
sb.append("]");
if(parser.getLocalName()!="Log")
sb.append(" "+parser.getElementText());//getElementText()方法获取元素内容,该方法会内部调用next()方法,故获取元素内容放在获取属性内容之后。该方法源码细节可以查看[JDK10文档](https://docs.oracle.com/javase/10/docs/api/javax/xml/stream/XMLStreamReader.html#getElementText%28%29)
System.out.println(sb);
}
}
}
}