`
躁动的绵羊
  • 浏览: 95950 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

XML教程——JAXP详解(二)

阅读更多

在 JAXP 的早期版本中,该首字母缩写代表 Java API for XML Parsing。在 第 1 部分 中了解到,JAXP 是位于 SAX 和 DOM 之上的层,它允许 Java 程序员执行开发商中立的 XML 解析。最初,这是 JAXP 的全部特性。不过俗话说的好,过去是过去,现在是现在。

过去,Java 和 XML 组合本身主要用于解析。Java 应用程序只需读入 XML 文档,然后按程序处理文档的数据。但随着 XML 消费应用程序流行起来,很显然,各种应用程序所执行的操作有许多重叠。对于所有优秀的软件,重叠将导致规范(而且每次都产生新的有用的 API)。

XML 的广泛使用所产生的第一个规范是 XSL(参阅 参考资料)。应用程序不断提取 XML 数据,添加一些格式化,然后将其显示在用户界面上——通常是作为 HTML、XHTML 或 WML。XSL 完成此任务,并在其上构建规范,从而允许应用程序抛弃其所有的专用转换代码。XSL 规范产生之后,XML 转换 API(Transformation API for XML,TrAX)随之出现(参阅 参考资料)。TrAX 提供了在 Java 应用程序中使用 XSL 的简单一致的方法。目前,JAXP——相当长的链(和介绍)中的最后一链——已经将 TrAX 合并到核心 Java 开发环境中。按照所有的发展,以及最近的添加(比如扩展验证和 XPath 支持),JAXP 现在代表 Java API for XML Processing。本文重点介绍使用 JAXP 进行处理而非解析。

由此及彼

理解 XSL 的基本程序流对于掌握 JAXP 如何处理转换是非常关键的。如果对 XSL 十分陌生,则需要快速回顾一下 XSL 基本概念。即使您是 XSL 专家也要忍受我如此絮叨。

来源 (XML)

使用 XSL 时,必须从 XML 开始。我知道这听起来理所当然,但还是值得说明一下。您可能习惯以 XML 文件开始(比如 phonebook.xml),并将其传递到 XSL 处理器中。JAXP 不仅允许您传递文件,它还允许您做好多事,在下一节 输入和输出 中将学习相关内容。

样式表 (XSL)

可能吸引大多数设计人员的是 XSL 样式表。样式表是一个指令集合,它指定特定类型的数据作为输入,并指定其他一组数据和格式化作为输出。但是切记,样式表应该对入站 XML 的结构进行操作,而不是对文档中的特定数据进行操作。这就确保样式表处理任何给定格式的 XML,而非特定的实例文档。

目标 (*ML)

最后需要记住,只能从 XSL 中输出格式良好的标记语言。不能输出 Microsoft Word 文档或 PDF。一定要使用标记语言,比如 XML、XHTML、WML 或其他良好的 *ML(标记语言)变种。

当然,对此我听到异议——已经看到了从 XML 输出 PDF 的应用程序,或将 XML 转换为 Excel 的应用程序。而且,可以接受特定格式的 XML 并将其转换为二进制格式的引擎确实存在。 但这并不属于 XSL 的领域;它是转换后处理。JAXP 会帮助转换 XML,但它不允许转换为二进制格式。


输入和输出

通过简单的回顾可能已经了解到,许多 XML 转换只是关于输入和输出的。导入 XML,对它进行操作,然后输出 *ML。在处理所有中间位(我意识到这是最有趣的地方)之前,将展示如何将数据输入到 JAXP 和如何将其输出返回。

JAXP 的灵活性

JAXP 不是一般的转换引擎。它不能将 Java 属性文件或一次性文本格式转换为 XML 或其他格式的标记语言。事实上,JAXP 甚至不接受 HTML,除非它是格式良好的(因此产生了一些 XHTML)。所以在试图使 JAXP 成为更一般的 API 之前,应该意识到必须使用格式良好的 XML,否则其他一切都没用。

JAXP 的灵活性在于可以如何表示 XML。显而易见的原因是,JAXP 可以将 XML 接受为文件或包装文件的流。但它还可以将输入接受为 DOM Document(这表示不可能存在于磁盘上的 XML 文档),或者接受为一系列 SAX 事件(这也表示 XML 文档)。使用这个附加的灵活性,可以将 JAXP 插入到任何 XML 事件链中。例如,如果具有一段代码,该代码使用 SAX 读入 XML 文档,然后将该数据的特定部分传递给另一个应用程序,则可以简单地在 SAX 代码和其他应用程序组件之间插入 JAXP。它可以消费 SAX 事件,按需转换数据,并将结果传递给接收应用程序组件。

这个故事的寓意是,可以以任何标准格式将 XML 传递给 JAXP,并以同样多的格式将其输出。即使目前不需要这种灵活性,或无法想像怎么可能使用所有这些不同格式,但是当您的需要变得更高级时,JAXP 已经准备好为您服务了。

输入来源

接口编程 101

如果使用接口对您来说相当陌生,则要注意,清单 1 总是把接口放在等号 (=) 左边,把特定实现类放在右边。所以 Source 在左边,StreamSourceSAXSource 等实现类在右边。

javax.xml.transform.Source 接口是 JAXP 的所有输入和转换 API 的基础。该接口只定义了两个方法——getSystemId()setSystemId(String systemId)。实际上,您将不会像处理 JAXP 提供的具体实现那样,过多地直接处理该接口:

  • javax.xml.transform.dom.DOMSource 将 DOM Node(及其孩子)传递给 JAXP。
  • javax.xml.transform.sax.SAXSource 将 SAX 回调结果(来自 XMLReader)传递给 JAXP。
  • javax.xml.transform.stream.StreamSource 将包装在 FileInputStreamReader 中的 XML 传递给 JAXP。

清单 1 展示了几种用于创建转换中使用的 Source 的方法:


清单 1. 使用 Source 接口的实现

				
// Create a Source from a file on disk
Source fileSource = 
  new StreamSource(new File("phonebook.xml"));
 
// Create a Source from a DOM tree
Document myDomDocument = getDocument();
Source domSource = new DOMSource(myDomDocument);
// Create a Source from an InputStream
BufferedInputStream bis = 
  new BufferedInputStream(getInputStream());
Source streamSource = new StreamSource(bis);
// Create a Source from a reader and SAX InputSource
XMLReader myXMLReader = getXMLReader();
InputSource myInputSource = getInputSource();
Source saxSource = new SAXSource(myXMLReader, myInputSource);

 

清单 1 几乎是自解释的。一旦获得 Source,就可以将 XML 输入 JAXP 的 XSL 处理部分。

输出结果

在讲述转换本身之前,将简单介绍一下 Source 的输出对应物——javax.xml.transform.Result。它甚至具有与 Source 相同的两个基本方法——getSystemId()setSystemId(String systemId)

与其输入对应物一样,一般使用 JAXP 的具体 Result 实现:

  • javax.xml.transform.dom.DOMResult 将转换后的内容传递到 DOM Node 中。
  • javax.xml.transform.sax.SAXResult 将转换的结果传递到 SAX ContentHandler 中。
  • javax.xml.transform.stream.StreamResult 将转换后的 *ML 传递到 FileOutputStreamWriter 中。

清单 2 展示了一些简单的例子,与 清单 1Source 的例子十分相似:


清单 2. 使用 Result 接口的实现

				
// Write to a file on disk
Result fileResult = 
  new StreamResult(new File("output.xml"));
 
// Write a Result to a DOM tree (inserted into the supplied Document)
Document myDomDocument = getDocument();
Result domResult = new DOMResult(myDomDocument);
// Create a Result from an OutputStream
BufferedOutputStream bos = 
  new BufferedOutputStream(getOutputStream());
Result streamResult = new StreamResult(bos);
// Create a Result to write to a SAX ContentHandler
ContentHandler myContentHandler = new MyContentHandler();
Result saxResult = new SAXResult(myContentHandler);

 

一旦理解了 SourceResult 接口以及与 JAXP 绑定在一起的实现之后,就差不多已经掌握了 XML 转换。


使用 JAXP 执行转换

如果阅读 第 1 部分 距今有一段时间了,或者谈到 JAXP 和解析时仍有些生疏,就应该花时间回顾一下 SAXParserFactoryDOMBuilderFactory 类。您将发现,如果知道如何使用这些类,就已经能够完全理解 JAXP 转换如何工作了。

获得工厂

转换如此简单,以至于有点微不足道。首先,需要设置输入和输出接收器。将 Source 包装在输入 XML 文档和 XSL 样式表中。然后,创建一个接收器,以写入转换后的结果——然后将其包装在 Result 中。

其次,需要使用静态 newInstance() 方法创建 TransformerFactory。清单 3 展示了所有详细信息:


清单 3. 创建新 TransformerFactory 实例

				
try {
  // Set up input documents
  Source inputXML = new StreamSource(
    new File("phonebook.xml"));
  Source inputXSL = new StreamSource(
    new File("phonebook.xsl"));
  // Set up output sink
  Result outputXHTML = new StreamResult(
    new File("output.html"));
  // Setup a factory for transforms
  TransformerFactory factory = TransformerFactory.newInstance();
} catch (TransformerConfigurationException e) {
  System.out.println("The underlying XSL processor " +
    "does not support the requested features.");
} catch (TransformerException e) {
  System.out.println("Error occurred obtaining " +
    "XSL processor.");
}

 

这一步没有太多内容。异常处理与代码本身所用时间一样多。对于 SAX 和 DOM 工厂类,一个异常处理已请求的但不受支持的特性,另一个异常处理实例化错误。

恒等转换

TransformerFactory.newTransformer() 的一个版本不接受任何参数(因此无 XSL 样式表)。这允许您执行恒等转换,它简单地将输入 XML 从一种形式(比如流)转换为另一种形式(比如 DOM 树)。您以一种格式提供 XML 作为 Source,然后以另一种格式将其推出作为 Result。应该记住这个有用的技巧。

工厂类本身用于获得 Transformer 的实例(在 下一小节 中讨论),并执行简单配置。可以使用 setFeature(String feature, boolean value) 方法来调用处理器上的特性。当然,工厂上设置的任何特性都应用于由此工厂创建的所有 Transformer 实例。

创建 Transformer

下一步是获得对象来执行实际转换。这是另一段相当令人厌烦的代码:只在工厂上调用 newTransformer(),并为该方法提供要使用的 XSL 样式表。清单 4 展示了详细操作:


清单 4. 使用 TransformerFactory 创建 Transformer

				
try {
  // Set up input documents
  Source inputXML = new StreamSource(
    new File("phonebook.xml"));
  Source inputXSL = new StreamSource(
    new File("phonebook.xsl"));
  // Set up output sink
  Result outputXHTML = new StreamResult(
    new File("output.html"));
  // Setup a factory for transforms
  TransformerFactory factory = TransformerFactory.newInstance();
  // Get a transformer for this XSL
  Transformer transformer = factory.newTransformer(inputXSL);

} catch (TransformerConfigurationException e) {
  System.out.println("The underlying XSL processor " +
    "does not support the requested features.");
} catch (TransformerException e) {
  System.out.println("Error occurred obtaining " +
    "XSL processor.");
}

 

此处没有太多值得注意的地方;惟一需要确保具有的就是 Transformer 与特定样式表之间的连接。因为样式表用于创建 Transformer,这是惟一可以用于该实例的 XSL。如果想要使用不同的样式表执行附加转换,可以重用 TransformerFactory,但必须创建不同的 Transformer 实例,以与新样式表连接。

执行转换

一切就绪之后,只需要一行代码来执行转换。清单 5 展示了如何使用 transform() 方法。只需为它提供输入 XML 和输出接收器;样式表已经连接到要使用的 Transformer 实例上:


清单 5. 使用 transform() 方法

				
try {
  // Set up input documents
  Source inputXML = new StreamSource(
    new File("phonebook.xml"));
  Source inputXSL = new StreamSource(
    new File("phonebook.xsl"));
  // Set up output sink
  Result outputXHTML = new StreamResult(
    new File("output.html"));
  // Setup a factory for transforms
  TransformerFactory factory = TransformerFactory.newInstance();
  // Get a transformer for this XSL
  Transformer transformer = factory.newTransformer(inputXSL);
  // Perform the transformation
  transformer.transform(inputXML, outputXHTML);

} catch (TransformerConfigurationException e) {
  System.out.println("The underlying XSL processor " +
    "does not support the requested features.");
} catch (TransformerException e) {
  System.out.println("Error occurred obtaining " +
    "XSL processor.");
}

 

调用该方法之后,转换的结果写出到所提供的 Result 中。在 清单 5 中,这是一个文件,但还可以将输出发送到 SAX ContentHandler 或 DOM Node 中。如果想要全部尝试,绑定的文件提供了简单的 XML 文件、XSL 样式表和源代码(参阅 下载)。


高速缓存 XSL 样式表

这一切都很简单,但这样使用 JAXP 有两个明显的限制:

  • 每次执行 transform()Transformer 对象都要处理 XSL 样式表。
  • Transformer 的实例不是线程安全的。在多个线程之间不能使用相同的实例。

这两个限制源自同一问题:Transformer 每次执行转换时,都必须重新处理 XSL。如果该处理出现在多个线程中,就可能发生严重问题。在线程问题之上,必须一再地付出处理 XSL 样式表的成本。毫无疑问,您非常希望知道如何解决这些问题,那就继续读下去。

加载模板

尚未讨论过的接口 javax.xml.transform.Templatesjavax.xml.transform.Transformer 相近。Templates 接口是线程安全的(解决了第二个限制),并代表已编译的样式表(解决第一个限制)。在讨论相关概念之前,请查看清单 6:


清单 6. 使用 JAXP Templates 接口

				
try {
  // Set up input documents
  Source inputXML = new StreamSource(
    new File("phonebook.xml"));
  Source inputXSL = new StreamSource(
    new File("phonebook.xsl"));
  // Set up output sink
  Result outputXHTML = new StreamResult(
    new File("output-templates.html"));
  // Setup a factory for transforms
  TransformerFactory factory = TransformerFactory.newInstance();
  // Pre-compile instructions
  Templates templates = factory.newTemplates(inputXSL);
  // Get a transformer for this XSL
  Transformer transformer = templates.newTransformer();
  // Perform the transformation
  transformer.transform(inputXML, outputXHTML);
} catch (TransformerConfigurationException e) {
  System.out.println("The underlying XSL processor " +
    "does not support the requested features.");
} catch (TransformerException e) {
  System.out.println("Error occurred obtaining " +
    "XSL processor.");
}

 

清单 6 中的黑体行表示需要从 清单 5 进行的惟一更改。不是使用工厂来直接获得 Transformer,而是使用 newTemplates() 方法;这将返回一个 Templates 对象,它是线程安全的。可以将该对象传递到其他线程中的其他方法,而且根本无需担心。因为它将从传递它的 XSL 中预编译转换指令,所以传递给其他方法甚至线程是安全的。

然后,从 Templates.newTransformer() 方法中获得 Transformer 实例。在这个阶段无需指定 XSL,因为 Transformer 已经处理过了(事实上,它是已编译的 XSL,所以可以不更改样式表,如果您愿意的话)。除了多出的一行以及对现有行的一个更改之外,再无其他新内容。相当酷,考虑一下您的代码因为这个小小的更改变得有多好。

从 Transformer 到 Templates

最后一个值得考虑的问题是,何时使用从工厂直接获得的 Transformer 和何时使用 Templates 对象。我几乎总是选择使用 Templates 对象,因为使用 XSL 时,我总是重复使用相同的样式表。与其花时间在 XSL 上进行多次传递,我宁愿将指令预编译到 Templates 对象中,完成 XSL 处理。

也就是说,在一些情况下,最好是直接从 TransformerFactory 中拖出 Transformer。如果您知道您将只使用特定样式表执行单个转换,那么不预编译到 Templates 对象中会比较快,因为预编译需要略多一点的开销。但是,需要确保没有重用。在我的(完全不科学,使用了简短的样例)测试中,我发现如果使用一个 XSL 样式表两次,使用 Templates 对象和直接使用 Transformer 不分上下。 一旦使用三次以上,Templates 方法就要好多了。您还需要确保将没有任何线程技术问题;但这是一件简单的事情,所以我把它留给您应用在编程中。 一般地,使用 Templates 对象通常更安全。


更改 XSL 处理器

第 1 部分 中已经看到,通过更改系统属性,可以用自己的实现替换默认 JAXP 解析器实现。同一原则适用于 XSL 处理器。JAXP 预包装有 Xalan-J(参阅 参考资料),我总是使用它。但灵活性总是好事,而 JAXP 提供了这一点。

如果想要使用除 Xalan 之外的处理器,请为名为 javax.xml.transform.TransformerFactory 的系统属性提供一个值。需要为该属性分配要实例化的类的名称。该类应继承 javax.xml.transform.TransformerFactory(当然,这也是要设置的系统属性的名称),并填充方法其余的抽象。仅使用如下代码:

java -Djavax.xml.transform.TransformerFactory
	=[transformer.impl.class] TestTransformations 
    simple.xml simple.xsl

 

全部结束!


结束语

在早期版本中,JAXP 不过是位于 SAX 和 DOM 以及那些 API 过时版本之上的小薄层。现在,使用 JAXP 1.3 可以解析、验证并转换 XML,而无需编写一行特定于开发商的代码。虽然追溯到 SAX 代码、使用类似 DTDParser(参阅 参考资料)的工具或甚至自己处理转换,这些都是有意义的,但在 API 和工具的仓库中您需要 JAXP。甚至可能比开发商中立性更重要的一点是,具有最新的 Java 虚拟机 (JVM) 的所有客户和客户机都将具有 JAXP。所以使用它,好好使用它,经常使用它。

<!-- CMA ID: 162427 --><!-- Site ID: 10 --><!-- XSLT stylesheet used to transform this file: dw-article-6.0-beta.xsl -->

 

分享到:
评论

相关推荐

    XML的Jaxp解析示例和简单项目

    本文将深入探讨XML的解析方式之一——JAXP(Java API for XML Processing),并提供一个简单的JAXP解析XML项目的概述。 ### JAXP简介 JAXP是Java平台上的标准API,它提供了处理XML的两种主要方法:解析和转换。在...

    Android创建与解析XML(二)——详解Dom方式

    这种方式利用了DOM(文档对象模型)的API,其中JAXP(Java API for XML Processing)提供的DocumentBuilder类是用来创建和解析XML文档的关键工具。DOM是一种以树形结构表示XML文档的模型,它将XML文档解析成一个个...

    XML_JAVA指南.rar_WORKING_java Tutorial_java xml_jaxp_xml

    "XML_JAVA指南.rar_WORKING_java Tutorial_java xml_jaxp_xml"这个资源可能包含了一个关于如何在Java项目中使用JAXP的教程。通常,这样的教程会涵盖如何创建XML文档,使用DOM和SAX解析XML,以及如何执行XSLT转换。它...

    使用JAXP之SAX读取XML内容

    在`parse`方法中,第一个参数是待解析的XML文件路径,第二个参数是我们之前创建的事件处理器实例。解析过程将在`parse`方法调用时启动,根据XML文档的内容,事件处理器中的对应方法会被调用。 在实际应用中,你可以...

    jaxp的四个jar包

    Java API for XML Processing(JAXP)是Java平台上的一个标准接口,用于处理XML文档。JAXP提供了解析XML文档和转换XML数据的能力,它包括三个主要组件:SAX(Simple API for XML)、DOM(Document Object Model)和...

    XML——sax解析 极速入门易懂示例

    2. **SAXParserFactory**:这是JAXP(Java API for XML Processing)的一部分,用于创建SAX解析器。通过`SAXParserFactory.newInstance()`获取工厂实例,然后调用`newSAXParser()`生成`SAXParser`,这个解析器同样...

    JAXP 专述

    **JAXP详解** JAXP,全称Java API for XML Processing,是Sun Microsystems为Java平台推出的一个标准接口,用于处理XML文档。JAXP的主要目的是提供一个抽象层,使得开发者能够在不同的XML解析器之间进行切换,而...

    JAXP 专述――Sun 的 Java API for XML 语法分析

    JAXP并非旨在革新XML处理的方式,也不提供额外的语法分析功能,相反,它扮演着一个重要的角色——一个抽象层,旨在简化通过DOM(Document Object Model)和SAX(Simple API for XML)处理XML文档的难度,同时确保跨...

    j-sim安装程序xml补丁xml-java-packages.zip

    在本教程中,我们将详细探讨J-Sim的安装过程,并特别关注其中的XML补丁——xml-java-packages.zip,以及它所包含的xalan.jar、crimson.jar和jaxp.jar这三个关键组件。 首先,让我们理解J-Sim的安装程序。J-Sim是一...

    Java and XML, 3rd Edition

    《Java与XML》(第三版)的内容涵盖了所有主要的Java XML处理库程序,全面讲解了SAX、DOM、StAX、JDOM以及dom4j的应用程序编程接口,同时还囊括了最新版本的用于XML处理的Java应用程序编程接口(JAXP)和用于XML绑定...

    Jaxp_xml.rar_J2ME 程序_j2me_j2me xml_j2me 网络

    "www.pudn.com.txt"可能是一个文档,包含了关于这个示例的说明或教程,而"Jaxp_xml"可能是包含具体代码的类或文件。这个示例可能涵盖了以下内容: 1. **XML解析器初始化**:在J2ME中,需要实例化SAX解析器并设置...

    解析xml解析xml解析xml解析xml解析xml

    1. JAXP:Java API for XML Processing,Java平台的标准XML处理API,包括DOM、SAX和XPath接口。 2. lxml库:Python中的一个强大的XML处理库,提供了基于C的高性能解析器,支持DOM、SAX和lxml对象模型。 3. libxml2...

    dom4j解析xml详解

    ### DOM4J解析XML详解 #### 一、DOM4J简介与特性 DOM4J是一个由dom4j.org开发的开源XML解析包,专为Java平台设计,它不仅支持DOM、SAX和JAXP标准,还巧妙地融入了Java集合框架,使其成为Java开发者在处理XML数据时...

    使用JAXP处理XML文件.pdf

    ### 使用JAXP处理XML文件的关键知识点 #### JAXP简介 JAXP(Java API for XML Processing)是Java平台中处理XML文档的标准API之一。它主要包括两大块:DOM(Document Object Model)和SAX(Simple API for XML)。...

    J2EE Web Services: XML SOAP WSDL UDDI WS-I JAX-RPC JAXR SAAJ JAXP

    **J2EE Web服务:XML、SOAP、WSDL、UDDI、WS-I、JAX-RPC、JAXR、SAAJ、JAXP详解** 在IT领域,尤其是在Java企业级应用开发中,Web服务扮演着至关重要的角色。J2EE(Java 2 Platform, Enterprise Edition)提供了一...

    java解析XML详解.pdf

    Java解析XML技术详解: 在信息技术领域,XML(Extensible Markup Language,可扩展标记语言)是一种被广泛使用的标记语言,用于存储和传输数据。它与HTML相似,也是一种标记语言,但是HTML用于网页的格式化,而XML...

    基于AjaxP的通用查询

    **AjaxP通用查询技术详解** Ajax(Asynchronous JavaScript and XML)是一种在无需重新加载整个网页的情况下,能够更新部分网页的技术。AjaxP则是Ajax的一种扩展,它允许开发者创建更高效、更具交互性的Web应用程序...

Global site tag (gtag.js) - Google Analytics