Java核心技术·卷Ⅱ:高级特性(原书第10版)
上QQ阅读APP看书,第一时间看更新

3.8 XSL转换

XSL转换(XSLT)机制可以指定将XML文档转换为其他格式的规则,例如,转换为纯文本、XHTML或任何其他的XML格式。XSLT通常用来将某种机器可读的XML格式转译为另一种机器可读的XML格式,或者将XML转译为适于人类阅读的表示格式。

你需要提供XSLT样式表,它描述了XML文档向某种其他格式转换的规则。XSLT处理器将读入XML文档和这个样式表,并产生所要的输出(参见图3-7)。

图3-7 应用XSL转换

XSLT规范很复杂,已经有很多书描述了该主题。我们不可能讨论XSLT的全部特性,所以我们只能介绍一个有代表性的例子。你可以在Don Box等人合著的《Essential XML》一书中找到更多的信息。XSLT规范可以在http://www.w3.org/TR/xslt获得。

假设我们想要把有雇员记录的XML文件转换成HTML文件。请看这个输入文件:

我们希望的输出是一张HTML表格:

具有转换模板的样式表形式如下:

在我们的例子中,xsl:output元素将方法设定为HTML。而其他有效的方法设置是xml和text。

下面是一个典型的模板:

match属性的值是一个XPath表达式。该模板声明:每当看到XPath集/staff/employee中的一个节点时,将做以下操作:

1)产生字符串<tr>。

2)在处理其子节点时,持续应用该模板。

3)当处理完所有子节点后,产生字符串</tr>。

换句话说,该模板会生成围绕每条雇员记录的HTML表格的行标记。

XSLT处理器以检查根元素开始其处理过程。每当一个节点匹配某个模板时,就会应用该模板(如果匹配多个模板,就会使用最佳匹配的那个,详情请参见http://www.w3.org/TR/xslt)。如果没有匹配的模板,处理器会执行默认操作。对于文本节点,默认操作是把它的内容囊括到输出中去。对于元素,默认操作是不产生任何输出,但会继续处理其子节点。

下面是一个用来转换雇员记录文件中的name节点的模板:

正如你所见,模板产生定界符<td>...</td>,并且让处理器递归访问name元素的子节点。它只有一个子节点,即文本节点。当处理器访问该节点时,它会提取出其中的文本内容(当然,前提是没有其他匹配的模板)。

如果想要把属性值复制到输出中去,就不得不再做一些稍微复杂的操作了。下面是一个例子:

当处理hiredate节点时,该模板会产生:

1)字符串<td>

2)year属性的值

3)一个连字符

4)month属性的值

5)一个连字符

6)day属性的值

7)字符串</td>

xsl:value-of语句用于计算节点集的字符串值,其中,节点集由select属性的XPath值指定。在这个例子中,路径是相对于当前正在处理的节点的相对路径。节点集通过将各个节点的字符串值连接起来而被转换成一个字符串。属性节点的字符串值就是它的值,文本节点的字符串值是它的内容,元素节点的字符串值是它的所有子节点(而不是其属性)的字符串值的连接。

程序清单3-13包含了将带有雇员记录的XML文件转换成HTML表格的样式表。

程序清单3-13 transform/makehtml.xsl

程序清单3-14显示了一组不同的转换。其输入是相同的XML文件,其输出是我们熟悉的属性文件格式的纯文本。

程序清单3-14 transform/makeprop.xsl

该示例使用position()函数来产生以其父节点的角度来看的当前节点的位置。我们只要切换样式表就可以得到一个完全不同的输出。这样,就可以安全地使用XML来描述数据了,即便一些应用程序需要的是其他格式的数据,我们只要用XSLT来产生对应的可替代格式即可。

在Java平台下产生XML的转换极其简单,只需为每个样式表设置一个转换器工厂,然后得到一个转换器对象,并告诉它把一个源转换成结果。

transform方法的参数是Source和Result接口的实现类的对象。Source接口有4个实现类:

你可以从一个文件、流、阅读器或URL中构建StreamSource对象,或者从DOM树节点中构建DOMSource对象。例如,在上一节中,我们调用了如下的标识转换:

在示例程序中,我们做了一些更有趣的事情。我们并不是从一个现有的XML文件开始工作,而是产生一个SAX XML阅读器,通过产生适合的SAX事件,给人以解析XML文件的错觉。实际上,XML阅读器读入的是一个如第2章所描述的扁平文件,输入文件看上去是这样的:

处理输入时,XML阅读器将产生SAX事件。下面是实现了XMLReader接口的EmployeeReader类的parse方法的一部分代码:

用于转换器的SAXSource是从XML阅读器中构建的:

这是一个将非XML的遗留数据转换成XML的一个小技巧。当然,大多数XSLT应用程序都已经有了XML格式的输入数据,只需要在一个StreamSource对象上调用transform方法即可,例如:

其转换结果是Result接口的实现类的一个对象。Java库提供了3个类:

要把结果存储到DOM树中,请使用DocumentBuilder产生一个新的文档节点,并将其包装到DOMResult中:

要将输出保存到文件中,请使用StreamResult:

程序清单3-15包含了完整的源代码。

程序清单3-15 transform/TransformTest.java

javax.xml.transform.TransformerFactory 1.4

·Transformer newTransformer(Source styleSheet)

返回一个transformer类的实例,用来从指定的源中读取样式表。

javax.xml.transform.stream.StreamSource 1.4

·StreamSource(File f)

·StreamSource(InputStream in)

·StreamSource(Reader in)

·StreamSource(String systemID)

自一个文件、流、阅读器或系统ID(通常是相对或绝对URL)构建一个数据流源。

javax.xml.transform.sax.SAXSource 1.4

·SAXSource(XMLReader reader,InputSource source)

构建一个SAX数据源,以便从给定输入源中获取数据,并使用给定的阅读器来解析输入数据。

org.xml.sax.XMLReader 1.4

·void setContentHandler(ContentHandler handler)

设置在输入被解析时会被告知解析事件的处理器。

·void parse(InputSource source)

解析来自给定输入源的输入数据,并将解析事件发送到内容处理器。

javax.xml.transform.dom.DOMResult 1.4

·DOMResult(Node n)

自给定节点构建一个数据源。通常,n是一个新文档节点。

org.xml.sax.helpers.AttributesImpl 1.4

·void addAttribute(String uri,String lname,String qname,String type,String value)

将一个属性添加到该属性集合。

参数:uri 名字空间的URI

lname 无前缀的本地名

qname 带前缀的限定名

type 类型,“CDATA”、“ID”、“IDREF”、“IDREFS”、“NMTOKEN”、“NMTOKENS”、“ENTITY”、“ENTITIES”或“NOTATION”之一

value 属性值

·void clear()

删除属性集合中的所有属性。

我们以该示例结束对Java库中的XML支持特性的讨论。现在,你应该对XML的强大功能有了很好的了解,尤其是它的自动解析、验证和强大的转换机制。当然,所有这些技术只有在你很好地设计了XML格式之后才能发挥作用。你必须确保那些格式足够丰富,能够表达全部业务需求,随着时间的推移也依旧稳定,你的业务伙伴也愿意接受你的XML文档。这些问题要远比处理解析器、DTD或转换更具挑战。

在下一章,我们将讨论在Java平台上的网络编程,从最基础的网络套接字开始,逐渐过渡到用于E-mail和万维网的更高层协议。