Java 中文官方教程 2022 版(三十九)(2)

简介: Java 中文官方教程 2022 版(三十九)

Java 中文官方教程 2022 版(三十九)(1)https://developer.aliyun.com/article/1488156

从任意数据结构生成 XML

原文:docs.oracle.com/javase/tutorial/jaxp/xslt/generatingXML.html

本节使用 XSLT 将任意数据结构转换为 XML。

这是整个过程的概述:

  1. 修改一个现有的读取数据的程序,使其生成 SAX 事件。(此时,该程序是一个真正的解析器还是一个数据过滤器都无关紧要)。
  2. 使用 SAX“解析器”构建一个SAXSource进行转换。
  3. 使用与上一个练习中创建的相同的StreamResult对象来显示结果。(但请注意,你也可以轻松地创建一个DOMResult对象来在内存中创建一个 DOM)。
  4. 使用转换器对象将源连接到结果,进行转换。

首先,你需要一个要转换的数据集和一个能够读取数据的程序。接下来的两个部分创建了一个简单的数据文件和一个读取它的程序。

创建一个简单的文件

这个例子使用了一个地址簿的数据集,PersonalAddressBook.ldif。如果你还没有这样做,下载 XSLT 示例并解压到install-dir/jaxp-1_4_2-release-date/samples目录中。这里显示的文件是通过在 Netscape Messenger 中创建一个新的地址簿,给它一些虚拟数据(一个地址卡),然后以 LDAP 数据交换格式(LDIF)格式导出而生成的。解压 XSLT 示例后,它位于xslt/data目录中。

下面的图示显示了创建的通讯录条目。

图示 通讯录条目


导出通讯录会生成一个类似下面显示的文件。我们关心的文件部分以粗体显示。

dn: cn=Fred Flintstone,mail=fred@barneys.house
modifytimestamp: 20010409210816Z
cn: Fred Flintstone
xmozillanickname: Fred
mail: Fred@barneys.house
xmozillausehtmlmail: TRUE
givenname: Fred
sn: Flintstone
telephonenumber: 999-Quarry
homephone: 999-BedrockLane
facsimiletelephonenumber: 888-Squawk
pagerphone: 777-pager
cellphone: 555-cell
xmozillaanyphone: 999-Quarry
objectclass: top
objectclass: person

请注意,文件的每一行都包含一个变量名,一个冒号和一个空格,后面跟着变量的值。sn变量包含人的姓氏(姓),变量cn包含通讯录条目中的DisplayName字段。

创建一个简单的解析器

下一步是创建一个解析数据的程序。


注意 - 本节讨论的代码在AddressBookReader01.java中,解压XSLT 示例install-dir/jaxp-1_4_2-release-date/samples目录后,可以在xslt目录中找到。


接下来显示程序的文本。这是一个非常简单的程序,甚至不会为多个条目循环,因为毕竟它只是一个演示。

import java.io.*; 
public class AddressBookReader01 { 
    public static void main(String argv[]) {
        // Check the arguments
        if (argv.length != 1) {
            System.err.println("Usage: java AddressBookReader01 filename");
            System.exit (1);
        }
        String filename = argv[0];
        File f = new File(filename);
        AddressBookReader01 reader = new AddressBookReader01();
        reader.parse(f);
    }
    // Parse the input file
    public void parse(File f) {
        try {
            // Get an efficient reader for the file
            FileReader r = new FileReader(f);
            BufferedReader br = new BufferedReader(r);
            // Read the file and display its contents.
            String line = br.readLine();
            while (null != (line = br.readLine())) {
                if (line.startsWith("xmozillanickname: "))
                    break;
            }
            output("nickname", "xmozillanickname", line);
            line = br.readLine();
            output("email",  "mail", line);
            line = br.readLine();
            output("html", "xmozillausehtmlmail", line);
            line = br.readLine();
            output("firstname","givenname", line);
            line = br.readLine();
            output("lastname", "sn", line);
            line = br.readLine();
            output("work", "telephonenumber", line);
            line = br.readLine();
            output("home", "homephone", line);
            line = br.readLine();
            output("fax", "facsimiletelephonenumber", line);
            line = br.readLine();
            output("pager", "pagerphone", line);
            line = br.readLine();
            output("cell", "cellphone", line);
        }
        catch (Exception e) {
            e.printStackTrace();
        }
    }
}

这个程序包含三个方法:

main

main方法从命令行获取文件名,创建解析器的实例,并开始解析文件。当我们将程序转换为 SAX 解析器时,这个方法将消失。(这是将解析代码放入单独方法的一个原因)。

parse

此方法在主例程发送的 File 对象上运行。正如您所看到的,这非常简单。唯一的效率让步是使用 BufferedReader,当您开始操作大文件时,这可能变得重要。

output

输出方法包含了一行结构的逻辑。它接受三个参数。第一个参数给出一个要显示的方法名称,因此它可以将 html 输出为一个变量名,而不是 xmozillausehtmlmail。第二个参数给出存储在文件中的变量名(xmozillausehtmlmail)。第三个参数给出包含数据的行。然后该例程从行的开头剥离变量名并输出所需的名称,加上数据。

运行 AddressBookReader01 示例

  1. 导航到 samples 目录。
% cd *install-dir*/jaxp-1_4_2-*release-date*/samples.
  1. 点击此链接下载 XSLT 示例 并将其解压缩到 install-dir/jaxp-1_4_2-release-date/samples 目录中。
  2. 导航到 xslt 目录。
cd xslt
  1. 编译 AddressBookReader01 示例。
    输入以下命令:
% javac AddressBookReader01.java
  1. 在数据文件上运行 AddressBookReader01 示例。
    在下面的情况下,AddressBookReader01 在上面显示的文件 PersonalAddressBook.ldif 上运行,在解压缩示例包后在 xslt/data 目录中找到。
% java AddressBookReader01 data/PersonalAddressBook.ldif
  1. 您将看到以下输出:
nickname: Fred
email: Fred@barneys.house
html: TRUE
firstname: Fred
lastname: Flintstone
work: 999-Quarry
home: 999-BedrockLane
fax: 888-Squawk
pager: 777-pager
cell: 555-cell
  1. 这比 创建简单文件 中显示的文件更易读。

创建生成 SAX 事件的解析器

本节展示了如何使解析器生成 SAX 事件,以便您可以将其用作 XSLT 转换中的 SAXSource 对象的基础。


注意 - 本节讨论的代码在 AddressBookReader02.java 中,该文件在您将 XSLT examples 解压缩到 install-dir/jaxp-1_4_2-release-date/samples 目录后的 xslt 目录中找到。AddressBookReader02.java 是从 AddressBookReader01.java 改编而来,因此这里只讨论两个示例之间的代码差异。


AddressBookReader02 需要以下未在 AddressBookReader01 中使用的突出显示的类。

import java.io.*; 
import org.xml.sax.*;
import org.xml.sax.helpers.AttributesImpl;

该应用程序还扩展了 XmlReader。此更改将应用程序转换为生成适当的 SAX 事件的解析器。

public class AddressBookReader02 implements XMLReader { /* ... */ }

AddressBookReader01 示例不同,此应用程序没有 main 方法。

以下全局变量将在本节后面使用:

public class AddressBookReader02 implements XMLReader {
    ContentHandler handler;
    String nsu = "";  
    Attributes atts = new AttributesImpl();
    String rootElement = "addressbook";
    String indent = "\n ";
    // ...
}

SAX ContentHandler 是将获得解析器生成的 SAX 事件的对象。为了将应用程序转换为 XmlReader,应用程序定义了一个 setContentHandler 方法。处理程序变量将保存在调用 setContentHandler 时发送的对象的引用。

当解析器生成 SAX 元素事件时,它将需要提供命名空间和属性信息。因为这是一个简单的应用程序,它为这两者定义了空值。

应用程序还为数据结构(addressbook)定义了一个根元素,并设置了一个缩进字符串以提高输出的可读性。

此外,解析方法被修改为接受 InputSource(而不是 File)作为参数,并考虑了它可能生成的异常:

public void parse(InputSource input) throws IOException, SAXException

现在,不再像在 AddressBookReader01 中那样创建新的 FileReader 实例,而是将读取器封装在 InputSource 对象中:

try {
    java.io.Reader r = input.getCharacterStream();
    BufferedReader Br = new BufferedReader(r);
    // ...
}

注意 - 下一节将展示如何创建输入源对象,实际放入其中的将是一个缓冲读取器。但是 AddressBookReader 可能会被其他人在某个地方使用。这一步确保处理将是高效的,无论给定的读取器是什么。


下一步是修改解析方法以生成文档和根元素的 SAX 事件。以下突出显示的代码实现了这一点:

public void parse(InputSource input) {
    try {
        // ...
        String line = br.readLine();
        while (null != (line = br.readLine())) {
            if (line.startsWith("xmozillanickname: ")) 
                break;
        }
        if (handler == null) {
            throw new SAXException("No content handler");
        }
        handler.startDocument(); 
        handler.startElement(nsu, rootElement, rootElement, atts);
        output("nickname", "xmozillanickname", line);
        // ...
        output("cell", "cellphone", line);
        handler.ignorableWhitespace("\n".toCharArray(), 
            0,  // start index
            1   // length
        ); 
        handler.endElement(nsu, rootElement, rootElement);
        handler.endDocument(); 
    }
    catch (Exception e) {
        // ...
    }
}

在这里,应用程序检查解析器是否正确配置了 ContentHandler。(对于这个应用程序,我们不关心其他任何内容)。然后生成文档和根元素的开始事件,并通过发送根元素的结束事件和文档的结束事件来完成。

此时有两个值得注意的地方:

  • setDocumentLocator 事件未发送,因为这是可选的。如果它很重要,那么该事件将在 startDocument 事件之前立即发送。
  • 在根元素结束之前生成了一个 ignorableWhitespace 事件。这也是可选的,但它极大地提高了输出的可读性,很快就会看到。(在这种情况下,空白包括一个单独的换行符,它以与将字符发送到字符方法相同的方式发送:作为字符数组、起始索引和长度)。

现在生成了文档和根元素的 SAX 事件,下一步是修改输出方法以为每个数据项生成适当的元素事件。删除对 System.out.println(name + ": " + text) 的调用,并添加以下突出显示的代码来实现:

void output(String name, String prefix, String line) 
    throws SAXException {
    int startIndex = 
    prefix.length() + 2;   // 2=length of ": "
    String text = line.substring(startIndex);
    int textLength = line.length() - startIndex;
    handler.ignorableWhitespace (indent.toCharArray(), 
        0,    // start index
        indent.length()
    );
    handler.startElement(nsu, name, name /*"qName"*/, atts);
    handler.characters(line.toCharArray(), 
        startIndex,
        textLength;
    );
    handler.endElement(nsu, name, name);
}

因为 ContentHandler 方法可以将 SAXExceptions 发送回解析器,所以解析器必须准备好处理它们。在这种情况下,不会有任何异常,所以如果出现任何异常,应用程序将简单地允许失败。

然后计算数据的长度,再次生成一些可忽略的空白以提高可读性。在这种情况下,只有一个数据级别,所以我们可以使用一个固定的缩进字符串。(如果数据结构更加结构化,我们将不得不根据数据的嵌套计算缩进空间)。


注意 - 缩进字符串对数据本身没有影响,但会使输出更容易阅读。如果没有该字符串,所有元素将被连接在一起:

<addressbook>
<nickname>Fred</nickname>
<email>...

接下来,以下方法配置解析器,以接收其生成的事件的 ContentHandler

void output(String name, String prefix, String line)
    throws SAXException {
    //  ...
}
// Allow an application to register a content event handler.
public void setContentHandler(ContentHandler handler) {
    this.handler = handler;
}  
// Return the current content handler.
public ContentHandler getContentHandler() {
    return this.handler;
}

还必须实现几个其他方法以满足 XmlReader 接口的要求。为了这个练习,所有这些方法都生成了空方法。然而,一个生产应用程序需要实现错误处理程序方法以生成更健壮的应用程序。不过,在这个例子中,以下代码为它们生成了空方法:

// Allow an application to register an error event handler.
public void setErrorHandler(ErrorHandler handler) { } 
// Return the current error handler.
public ErrorHandler getErrorHandler() { 
    return null; 
}

然后,以下代码为剩余的 XmlReader 接口生成了空方法。(其中大部分对于真实的 SAX 解析器是有价值的,但对于像这样的数据转换应用程序却没有太大影响)。

// Parse an XML document from a system identifier (URI).
public void parse(String systemId) throws IOException, SAXException 
{ } 
// Return the current DTD handler.
public DTDHandler getDTDHandler() { return null; } 
// Return the current entity resolver.
public EntityResolver getEntityResolver() { return null; } 
// Allow an application to register an entity resolver.
public void setEntityResolver(EntityResolver resolver) { } 
// Allow an application to register a DTD event handler.
public void setDTDHandler(DTDHandler handler) { } 
// Look up the value of a property.
public Object getProperty(String name) { return null; } 
// Set the value of a property.
public void setProperty(String name, Object value) { }  
// Set the state of a feature.
public void setFeature(String name, boolean value) { } 
// Look up the value of a feature.
public boolean getFeature(String name) { return false; }

现在您有一个可以用来生成 SAX 事件的解析器。在下一节中,您将使用它来构造一个 SAX 源对象,从而将数据转换为 XML。

使用解析器作为 SAXSource

给定一个用作事件源的 SAX 解析器,您可以构造一个转换器来生成一个结果。在本节中,TransformerApp 将被更新以生成一个流输出结果,尽管它也可以轻松地生成一个 DOM 结果。


注意 - 注意:本节讨论的代码位于 TransformationApp03.java 中,在将 XSLT 示例 解压缩到 install-dir/jaxp-1_4_2-release-date/samples 目录后,可以在 xslt 目录中找到。


要开始,TransformationApp03TransformationApp02 的不同之处在于构建 SAXSource 对象所需导入的类。这些类如下所示。在这一点上,DOM 类已不再需要,因此已被丢弃,尽管保留它们不会造成任何伤害。

import org.xml.sax.SAXException; 
import org.xml.sax.SAXParseException; 
import org.xml.sax.ContentHandler;
import org.xml.sax.InputSource;
import javax.xml.transform.sax.SAXSource; 
import javax.xml.transform.stream.StreamResult;

接下来,应用程序不再创建 DOM DocumentBuilderFactory 实例,而是创建了一个 SAX 解析器,即 AddressBookReader 的实例:

public class TransformationApp03 {
    static Document document;  
    public static void main(String argv[]) {
        // ...
        // Create the sax "parser".
        AddressBookReader saxReader = new AddressBookReader();
        try {
            File f = new File(argv[0]);
            // ...
        }
        // ...
    }
}

然后,以下突出显示的代码构造了一个 SAXSource 对象

// Use a Transformer for output
// ...
Transformer transformer = tFactory.newTransformer();
// Use the parser as a SAX source for input
FileReader fr = new FileReader(f);
BufferedReader br = new BufferedReader(fr);
InputSource inputSource = new InputSource(br);
SAXSource source = new SAXSource(saxReader, inputSource);
StreamResult result = new StreamResult(System.out);
transformer.transform(source, result);

在这里,TransformationApp03 构造了一个缓冲读取器(如前所述),并将其封装在一个输入源对象中。然后创建了一个 SAXSource 对象,将读取器和 InputSource 对象传递给它,然后将其传递给转换器。

当应用程序运行时,转换器将自身配置为 SAX 解析器(AddressBookReader)的 ContentHandler,并告诉解析器对 inputSource 对象进行操作。解析器生成的事件然后传递给转换器,转换器执行适当的操作并将数据传递给结果对象。

最后,TransformationApp03 不会生成异常,因此在 TransformationApp02 中看到的异常处理代码不再存在。

运行 TransformationApp03 示例

  1. 导航到 samples 目录。
% cd *install-dir*/jaxp-1_4_2-*release-date*/samples.
  1. 点击此链接下载 XSLT 示例 并将其解压缩到 install-dir/jaxp-1_4_2-release-date/samples 目录中。
  2. 导航到 xslt 目录。
cd xslt
  1. 编译TransformationApp03示例。
    输入以下命令:
% javac TransformationApp03.java
  1. 运行TransformationApp03示例,转换您希望转换为 XML 的数据文件。
    在下面的情况下,在您解压缩示例包后,在xslt/data目录中找到的PersonalAddressBook.ldif文件上运行TransformationApp03
% java TransformationApp03 
  data/PersonalAddressBook.ldif
  1. 您将看到以下输出:
<?xml version="1.0" encoding="UTF-8"?>
<addressbook>
    <nickname>Fred</nickname>
    <email>Fred@barneys.house</email>
    <html>TRUE</html>
    <firstname>Fred</firstname>
    <lastname>Flintstone</lastname>
    <work>999-Quarry</work>
    <home>999-BedrockLane</home>
    <fax>888-Squawk</fax>
    <pager>777-pager</pager>
    <cell>555-cell</cell>
</addressbook>
  1. 正如您所看到的,LDIF 格式文件PersonalAddressBook已被转换为 XML!

使用 XSLT 转换 XML 数据

原文:docs.oracle.com/javase/tutorial/jaxp/xslt/transformingXML.html

可以使用可扩展样式表语言转换(XSLT)API 来实现多种目的。例如,通过一个足够智能的样式表,你可以从 XML 数据生成 PDF 或 PostScript 输出。但通常情况下,XSLT 用于生成格式化的 HTML 输出,或者创建数据的另一种 XML 表示。

在这一部分中,使用 XSLT 转换将 XML 输入数据转换为 HTML 输出。


注意 - XSLT 规范庞大而复杂,因此本教程只能触及表面。它将为您提供一些背景知识,以便您理解简单的 XSLT 处理任务,但不会详细讨论如何编写 XSLT 转换,而是集中于如何使用 JAXP 的 XSLT 转换 API。要深入了解 XSLT,请参考一本好的参考手册,例如 Michael Kay 的XSLT 2.0 和 XPath 2.0:程序员参考(Wrox,2008)。

Java 中文官方教程 2022 版(三十九)(3)https://developer.aliyun.com/article/1488159

相关文章
|
6天前
|
Web App开发 Java 测试技术
《手把手教你》系列技巧篇(五十六)-java+ selenium自动化测试-下载文件-上篇(详细教程)
【5月更文挑战第20天】本文介绍了自动化测试中如何实现无弹窗下载文件,主要针对Firefox浏览器。作者指出,通常的下载操作包括点击下载按钮,但这里讨论的是避免下载弹窗直接保存文件的方法。文章详细讲解了通过设置Firefox参数(如`browser.download.dir`、`browser.helperApps.neverAsk.saveToDisk`等)来实现这一功能,并给出了Java Selenium的示例代码,展示了如何创建FirefoxProfile并进行相关设置,以及如何启动浏览器和执行下载操作。
23 0
《手把手教你》系列技巧篇(五十六)-java+ selenium自动化测试-下载文件-上篇(详细教程)
|
11天前
|
机器学习/深度学习 移动开发 测试技术
《手把手教你》系列技巧篇(五十一)-java+ selenium自动化测试-字符串操作-下篇(详解教程)
【5月更文挑战第15天】自动化测试中进行断言的时候,我们可能经常遇到的场景。从一个字符串中找出一组数字或者其中的某些关键字,而不是将这一串字符串作为结果进行断言。这个时候就需要我们对字符串进行操作,宏哥这里介绍两种方法:正则和字符串切片函数split()。
29 2
|
1天前
|
机器人 Java 测试技术
《手把手教你》系列技巧篇(六十)-java+ selenium自动化测试 - 截图三剑客 -中篇(详细教程)
【6月更文挑战第1天】本文介绍了使用Java和Selenium进行自动化测试时的另一种截图方法,即利用Robot类实现全屏截图。Robot类能够捕获屏幕上的所有内容,包括任务栏和浏览器元素。测试场景包括访问指定网站、调用截图方法和保存截图。示例代码展示了如何使用Robot创建全屏截图并保存到特定文件夹。在运行代码前,需确保指定的保存路径存在,否则会报错。
13 4
|
1天前
|
存储 Java 测试技术
滚雪球学Java(47):String类教程:如何在Java中使用字符串操作
【6月更文挑战第1天】🏆本文收录于「滚雪球学Java」专栏,专业攻坚指数级提升,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!!
12 2
滚雪球学Java(47):String类教程:如何在Java中使用字符串操作
|
2天前
|
Oracle 安全 Java
JAVA用Mail发送API的方法步骤教程
使用Java和Mail发送API实现自动化邮件发送,提高效率。步骤包括:1. 安装JDK并配置JAVA_HOME,2. 添加JavaMail库(可通过Maven或官网下载)。配置邮件发送涉及邮件服务器地址、端口和认证信息。创建邮件会话、邮件消息对象,然后使用Transport发送。示例代码展示完整流程。注意处理认证失败、连接问题和邮件发送失败等常见问题。
|
3天前
|
Java 测试技术 Python
《手把手教你》系列技巧篇(五十九)-java+ selenium自动化测试 - 截图三剑客 -上篇(详细教程)
【5月更文挑战第23天】本文介绍了使用Java和Selenium进行自动化测试时如何实现截图操作。原本计划讲解远程测试内容,但因服务器网络问题无法进行,因此转而分享Selenium的截图方法。文章分为三部分,首先简述背景,然后重点介绍了TakeScreenshout类,这是一个用于获取浏览器窗口内容的截图工具,不包含浏览器菜单和任务栏。接着,列举了一个测试场景:访问百度首页并截图保存。最后,提供了代码示例,展示了如何设计自动化测试代码以及截图保存到指定目录,并附有代码运行效果展示。
17 2
|
4天前
|
Java 测试技术 Python
《手把手教你》系列技巧篇(五十八)-java+ selenium自动化测试-分页测试(详细教程)
【5月更文挑战第22天】本文介绍了自动化测试分页的实现方法。首先,文章提出了测试分页时关注的三个关键点:总页数、当前页数和页码导航的可用性。接着,作者分享了一个实用网站([https://www.jq22.com/](https://www.jq22.com/))以找到示例进行实践。在代码部分,展示了使用Java和Selenium进行自动化测试的示例代码,包括获取总页数、遍历所有页面及判断当前页面等操作。最后,简要总结了分页自动化测试的实现过程。
18 1
|
5天前
|
Web App开发 Java 测试技术
《手把手教你》系列技巧篇(五十七)-java+ selenium自动化测试-下载文件-下篇(详细教程)
【5月更文挑战第21天】本文介绍了自动化测试中如何实现无弹窗下载文件,特别针对Chrome浏览器。通过设置`download.default_directory`和`profile.default_content_settings.popups`,可以避免下载弹窗并指定下载路径。示例代码展示了如何使用Java和Selenium实现这一功能,包括导入相关库、设置ChromeOptions和执行下载操作。最后,文章提到虽然没有介绍IE浏览器的下载方法,但已有Chrome和Firefox的方法已足够应对大多数需求。
23 0
|
6天前
|
Java 编译器
<JAVA> java入门面向0基础教程(数据类型,运算符)
<JAVA> java入门面向0基础教程(数据类型,运算符)
16 1
<JAVA> java入门面向0基础教程(数据类型,运算符)
|
7天前
|
Java 测试技术 API
《手把手教你》系列技巧篇(五十五)-java+ selenium自动化测试-上传文件-下篇(详细教程)
【5月更文挑战第19天】本文介绍了在Web自动化中处理文件上传的挑战,由于Selenium WebDriver不直接支持文件上传,因此需要借助外部工具。文章提到了两种主要的上传方式:基于input框的上传和非input控件的上传。对于非input控件的上传,推荐使用AutoIt,这是一个支持Windows GUI自动化的工具。
38 9