本文只是个人总结,不是结论,因此不保证文章内容的正确性,仅是自己学习过程中的一些个人理解而已...
JAXP简介
JAXP全称Java API for XML Processing,最开始的时候(JAXP1.0)是叫Java API for XML Parsing,因为那个时候JAXP还仅支持XML的解析,后来JAXP不断进化,其支持的内容不断增加,也就改名为Processing了。
JAXP利用标准解析器Simple API for XML Parsing (SAX) 和 Document Object Model (DOM) 使我们可以在将数据作为事件流来解析或者构建出文档对象模型来解析中作出选择;JAXP 支持 Extensible Stylesheet Language Transformations (XSLT) 标准, 使我们能够将数据转换成其他的XML文档或其他格式,如HTML;从JAXP1.4版本开始,Streaming API for XML (StAX,JSR-173) 被加入到JAXP家庭中来。
JAXP与XML语法解析器
严格来说,JAXP 是 API,但是将其称为抽象层更准确。它不提供处理 XML 的新方式,不补充 SAX 或 DOM,也不向 Java 和 XML 处理提供新功能。它只是使通过 DOM 和 SAX 处理一些困难任务更容易。如果在使用 DOM 和 SAX API 时遇到特定于供应商的任务,它还使通过独立于供应商的方式处理这些任务成为可能。
需要牢记一点:JAXP 不提供语法分析功能 !使用JAXP时如果没有 SAX、DOM 或另一个 XML 语法分析 API,就 无法分析 XML 语法。虽然JAXP不提供语法分析,但JAXP提供到达语法分析器的方式;JAXP可以看作是一套规范,它可以使用不同提供商的XML解析器,后面会介绍如果切换其它的XML解析器。
现在,JAXP在SAX和DOM解析方式中默认采用Apache的xerces项目中的XML语法分析器;而对于StAX解析方式则默认采用Sun自己提供的语法分析器。这些语法分析器不属于JAXP API的一部分,如前面所述,JAXP不提供XML语法分析功能,这可能容易令人困惑。举个例子来说,这与JDOM使用Apache的Xerces语法分析器,但Xerces并不属于JDOM的一部分是一样的意思。
明白了没?JAXP API和XML语法分析器是相互独立的。
JAXP相关API简介
不说明具体的API及各API如何使用,仅说明一下各解析方式都有哪些相关的API以及在JDK中是如何组织的,具体使用需要去查JDK提供的API文档。
下面所用图片及内容均来源于Java官方网站,可以看这里:http://docs.oracle.com/javase/tutorial/jaxp/TOC.html
相关API总览
- javax.xml.parsers: JAXP相关API,为不同SAX and DOM 解析提供商提供通用接口
- org.w3c.dom: 文档对象模型(DOM)相关的接口
- org.xml.sax: 定义基本的SAX相关API
- avax.xml.transform:定义了XSLT相关 API,使我们可以将XML转换成其它形式
- javax.xml.stream:提供StAX相关的 API
SAX相关API
SAX解析API大纲图:
SAX相关包概览
- org.xml.sax:定义了SAX相关接口
- org.xml.sax.ext:定义了用于更复杂的SAX处理的扩展类,例如处理DTD或者查看文件详细语法
- org.xml.sax.helpers:包含了使SAX应用更简单的帮助类,比如,定义包含空方法的DefaultHandler
- javax.xml.parsers:定义了SAXParserFactory 类,用于返回SAXParser解析器;另外定义了用于报告解析错误的异常
DOM解析相关API
DOM解析API大纲图:
DOM相关包概览
- org.w3c.dom:为XML文档定义了DOM编程接口,由W3C制定
- javax.xml.parsers:定义了DocumentBuilderFactory 类和 DocumentBuilder 类,DocumentBuilder 返回一个代表W3C Document 的接口。包中也定义了用于报告错误的ParserConfigurationException异常类
XSLT相关API
XSLT API概览图:
XSLT相关包说明:
- javax.xml.transform:定义了TransformerFactory 类和 Transformer 类, 你可以用于得到有转换能力的对象,得到转换对象后用输入(source)和输出(result)参数调用transform() 方法
- javax.xml.transform.dom:包含由DOM创建输入和输出对象的类
- javax.xml.transform.sax:包含由SAX解析器创建输入对象和由SAX事件处理器创建输出对象的类
- javax.xml.transform.stream:包含由I/O流创建输入和输出对象的类
StAX API简介
StAX更多具体介绍可以看这里: http://blog.youkuaiyun.com/zhangyihui1986/article/details/8528649相关包说明:
- javax.xml.stream:定义了用于迭代XML文档元素的XMLStreamReader 接口和规定如何写XML的XMLStreamWriter 接口
- javax.xml.transform.stax:提供与StAX转换相关的 APIs.
JAXP各相关API特征对比
特征 | StAX | SAX | DOM | TrAX |
---|---|---|---|---|
API Type | Pull, streaming | Push, streaming | In memory tree | XSLT Rule |
Ease of Use | High | Medium | High | Medium |
XPath Capability | No | No | Yes | Yes |
CPU and Memory Efficiency | Good | Good | Varies | Varies |
Forward Only | Yes | Yes | No | No |
Read XML | Yes | Yes | Yes | Yes |
Write XML | Yes | No | Yes | Yes |
Create, Read, Update, Delete | No | No | Yes | No |
JAXP更改语法分析器
上面提到JAXP是抽象而非具体的API,它可以使用符合规范的其它XML语法分析器;其实切换语法分析器很简单,就是切换获取语法分析的工厂类而已,因为所有 SAXParser 和 DocumentBuilder 实例都来自这些类工厂。
更改语法分析器通过设置Java系统属性来实现。可以通过设置 Java 系统属性 javax.xml.parsers.SAXParserFactory 来更改要使用的 SAXParserFactory 接口实现;如果没有定义该属性,则返回缺省实现。相同原理适用于 DocumentBuilderFactory 实现,在这种情况下,将查询 javax.xml.parsers.DocumentBuilderFactory 系统属性。
你可以通过阅读相关的JDK源码来证明(以SAXParserFactory为例):
首先看一下SAXParserFactory类的newInstance方法:
package javax.xml.parsers;
// imports
public abstract class SAXParserFactory {
/** The default property name according to the JAXP spec */
private static final String DEFAULT_PROPERTY_NAME = "javax.xml.parsers.SAXParserFactory";
protected SAXParserFactory () {}
public static SAXParserFactory newInstance() {
try {
return (SAXParserFactory) FactoryFinder.find(
/* The default property name according to the JAXP spec */
"javax.xml.parsers.SAXParserFactory",
/* The fallback implementation class name */
"com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl");
} catch (FactoryFinder.ConfigurationError e) {
throw new FactoryConfigurationError(e.getException(), e.getMessage());
}
}
}
跟着去FactoryFinder类(该修饰符不是public,因此在JDK的API文档中找不到)看一下欺find()方法:
static Object find(String factoryId, String fallbackClassName) throws ConfigurationError {
// 首先根据factoryId去系统属性中查找实现类, 存在则将其实例化并返回
try {
String systemProp = ss.getSystemProperty(factoryId);
if (systemProp != null) {
return newInstance(systemProp, null, true);
}
} catch (SecurityException se) {
if (debug) se.printStackTrace();
}
// 读取$java.home/lib/jaxp.properties文件, 如果存在键为factoryId的属性, 实例化并返回
try {
String factoryClassName = null;
if (firstTime) {
synchronized (cacheProps) {
if (firstTime) {
String configFile = ss.getSystemProperty("java.home") + File.separator +
"lib" + File.separator + "jaxp.properties";
File f = new File(configFile);
firstTime = false;
if (ss.doesFileExist(f)) {
cacheProps.load(ss.getFileInputStream(f));
}
}
}
}
factoryClassName = cacheProps.getProperty(factoryId);
if (factoryClassName != null) {
return newInstance(factoryClassName, null, true);
}
} catch (Exception ex) {
if (debug) ex.printStackTrace();
}
// Try Jar Service Provider Mechanism
Object provider = findJarServiceProvider(factoryId);
if (provider != null) {
return provider;
}
if (fallbackClassName == null) {
throw new ConfigurationError("Provider for " + factoryId + " cannot be found", null);
}
// 最后实例化默认的Factory并返回
return newInstance(fallbackClassName, null, true);
}
这个方法虽然很长,但结构很清晰。结合SAXParserFactory类的newInstance方法可以看出,系统首先拿着javax.xml.parsers.SAXParserFactory作为键去系统属性中查找实现类, 存在则将其实例化并返回;如果系统属性中没有指定,则读取$java.home/lib/jaxp.properties配置文件, 如果存在键为factoryId的属性值, 由实例化并返回;如果最后一直没有找到合适的Factory,则实例化默认的Factory,在这里是调用FacotyrFinder类中的find方法传入的第二个参数com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl,由此也可以看出,默认采用的是Apache的Xerces语法分析器。
同样的分析可以应用于DocumentBuilderFactory类,这里不予赘述。
其实StAX解析方式也是类似的,只不过它分了XMLInputFactory和XMLOutputFactory两个类,对应的系统属性值分别是javax.xml.stream.XMLInputFactory和javax.xml.stream.XMLOutputFactory,而默认实现分别是com.sun.xml.internal.stream.XMLInputFactoryImpl和com.sun.xml.internal.stream.XMLOutputFactoryImpl。这里也可以看出是Sun内部的语法分析器。