假设我们希望存储对我们的程序有意义的结构的信息。此外,我们希望这些信息在某种程度上是人类可读的,有时甚至是人类可编辑的。为了实现这一点,我们经常转向 XML。
Java 为我们提供了处理、读取和写入 XML 原始文本和文件的强大工具。然而,正如许多强大工具的情况一样,我们要学习如何使用它们会有一点知识开销。在本章中,我们将首先了解如何使用 Java 将 XML 文件加载到 Java 对象中。接下来,我们将介绍如何使用 Java 解析 XML 数据。最后,我们将看到编写和修改 XML 数据的 Java 代码。
本章将介绍以下主题:
- 用于读取 XML 数据的 Java 代码
- 解析 XML 数据
- 编写和修改 XML 数据
在本节中,我们将完成一项非常简单的任务,开始学习 Java 如何与 XML 交互。我们将使用本书代码文件中提供的cars.xml
文件中的 XML 信息。这个文件应该存储在我们 Java 项目的当前目录中,因此当我们运行 Java 程序时,它将能够访问cars.xml
,而不需要任何额外的路径。我们将编辑以下 Java 程序来加载cars.xml
文件:
package loadinganxmlfile;
import java.io.*;
import javax.xml.parsers.*;
import javax.xml.transform.*;
import javax.xml.transform.dom.*;
import javax.xml.transform.stream.*;
import org.w3c.dom.*;
import org.xml.sax.*;
public class LoadingAnXMLFile {
public static void main(String[] args) {
try {
//Write code that can throw errors here
}
catch (ParserConfigurationException pce) {
System.out.println(pce.getMessage());
}
catch (SAXException se) {
System.out.println(se.getMessage());
}
catch (IOException ioe) {
System.err.println(ioe.getMessage());
}
}
private static void PrintXmlDocument(Document xml)
{
try{
Transformer transformer =
TransformerFactory.newInstance().newTransformer();
StreamResult result = new StreamResult
(new StringWriter());
DOMSource source = new DOMSource(xml);
transformer.transform(source, result);
System.out.println(result.getWriter().toString());
}
catch(TransformerConfigurationException e)
{
System.err.println("XML Printing Failed");
}
catch(TransformerException e)
{
System.err.println("XML Printing Failed");
}
}
}
在我们开始之前,请注意这个程序需要大量的导入。我们导入的transform
类对于我们要编写的任何东西都不是必需的;我已经编写了一个名为PrintXmlDocument()
的函数,如果我们成功加载它,它将把 XML 文档打印到控制台窗口。如果您遵循本节中的代码,我建议您首先从一开始就导入这些transform
类。然后,在使用其他功能时,继续使用 NetBeans 的修复导入功能,以准确地查看工具所使用的库的来源。
让我们开始吧。这里我们的最终目标是拥有一个Document
类的对象,该对象包含cars.xml
文件中的信息。一旦我们有了这个Document
对象,我们需要做的就是在控制台窗口中查看信息,在Document
实例上调用PrintXmlDocument()
函数。
不幸的是,创建这个Document
对象并不像说Document dom = new Document();
语句那么简单。相反,我们需要以结构化和过程化的方式创建它,以适当地保留 XML 文件的可解析性。为此,我们将使用两个额外的类:DocumentBuilder
和DocumentBuilderFactory
类。
信不信由你,DocumentBuilder
类将负责为我们实际构建文档。DocumentBuilder
类作为独立于Document
对象的实体存在,因此作为程序员,我们可以从逻辑上分离我们可以在文档本身上执行的方法,以及首先创建该文档所需的其他方法。与Document
类类似,我们不能只实例化DocumentBuilder
类。相反,我们将使用第三个类来获得DocumentBuilder
,即DocumentBuilderFactory
类。我将创建Document
对象(存储 XML 文件)所需的代码分为三部分:
DocumentBuilderFactory
类包含一个名为newInstance()
的静态方法。让我们向main()
方法中的第一个try
块添加以下方法调用。这将实例化DocumentBuilderFactory
供我们从以下位置工作:
DocumentBuilderFactory factory =
DocumentBuilderFactory.newInstance();
- 一旦我们有了
DocumentBuilderFactory
,我们就可以为自己获得一个新的DocumentBuilder
对象。为此,我们将调用工厂的newDocumentBuilder()
方法。让我们将其添加到我们的 try 块:
DocumentBuilder builder = factory.newDocumentBuilder();
- 最后,我们需要指示
DocumentBuilder
为我们构建一个Document
对象,并且该对象应该反映我们的cars.xml
文件的结构。我们将简单地用try
块中的值实例化Document
对象。我们将通过builder
的parse()
方法获取该值。此方法的参数之一是引用文件名的字符串。如果 Java 程序中有一个被引用的文件对象,我们也可以使用它:
Document dom = builder.parse("cars.xml");
所以现在我们的main()
方法如下:
public static void main(String[] args) {
DocumentBuilderFactory factory =
DocumentBuilderFactory.newInstance();
try {
// Write code that can throw errors here...
DocumentBuilder builder =
factory.newDocumentBuilder();
Document dom = builder.parse("cars.xml");
PrintXmlDocument(dom);
}
catch (ParserConfigurationException pce) {
System.out.println(pce.getMessage());
}
catch (SAXException se) {
System.out.println(se.getMessage());
}
catch (IOException ioe) {
System.err.println(ioe.getMessage());
}
}
现在是时候检查我们的代码是否有效了。我们使用DocumentBuilderFactory
类的静态方法获取DocumentBuilderFactory
对象,这将创建一个全新的实例。通过DocumentBuilderFactory
,我们创建了一个新的DocumentBuilder
对象,它将能够智能地解析 XML 文件。在解析 XML 文件时,DocumentBuilder
对象了解其中包含的信息的性质,并能够将其存储在 XML 文档或Document
对象模型元素中。运行此程序时,我们将原始 XML 文档的原始文本视图作为输出:
因为像这样加载 XML 文件有很多步骤,所以我想把它放在它自己的部分中。这样,当我们作为程序员学习如何从 XML 中操作和读取有价值的信息时,我们就不会被我们在这里看到的所有语法所困扰。
Document
类为我们在对象中存储格式化信息提供了一种简单的方法。在上一节的程序中,我们将cars.xml
文件中的信息读入 JavaDocument
对象。以下是cars.xml
文件的外观:
<?xml version="1.0"?>
<cars>
<owner name="Billy">
<car vin="LJPCBLCX11000237">
<make>Ford</make>
<model>Fusion</model>
<year>2014</year>
<color>Blue</color>
</car>
<car vin="LGHIALCX89880011">
<make>Toyota</make>
<model>Tacoma</model>
<year>2013</year>
<color>Green</color>
</car>
<car vin="GJSIALSS22000567">
<make>Dodge</make>
<model>Charger</model>
<year>2013</year>
<color>Red</color>
</car>
</owner>
<owner name="Jane">
<car vin="LLOKAJSS55548563">
<make>Nissan</make>
<model>Altima</model>
<year>2000</year>
<color>Green</color>
</car>
<car vin="OOKINAFS98111001">
<make>Dodge</make>
<model>Challenger</model>
<year>2013</year>
<color>Red</color>
</car>
</owner>
</cars>
该文件的根节点是cars
节点,该节点中包含两个owner
节点,即 Billy 和 Jane,每个节点中都有多个car
节点。这些car
元素中存储的信息由前面的 Java 类可以存储的信息镜像。
本节中我们的目标是从cars.xml
中获取车辆信息,在本例中,仅针对特定的车主 Jane,并将此信息存储在我们的自定义Car
类中,以便我们可以利用Car
类的toString()
覆盖以良好的格式将 Jane 的所有车辆打印到我们的控制台。
通过我们已经设置的代码,我们的Document
对象dom
以相同的格式镜像cars.xml
中存储的信息,所以我们只需要弄清楚如何问这个Document
对象这个问题:Jane 拥有什么车?为了弄清楚如何编写代码,您需要了解一点 XML 术语。在本节中,我们将讨论术语“元素”和“节点”。
在 XML 中,元素是一个具有开始和结束标记的实体,它还包含其中的所有信息。当我们的Document
对象返回信息时,它通常会以节点的形式返回信息。节点是 XML 文档的构建块,我们几乎可以将其视为一种继承关系,其中所有元素都是节点,但并非所有节点都是元素。节点可以比整个 XML 元素更简单、更小。
本节将帮助我们使用以下代码访问有关 Jane 拥有的汽车的信息。我将要添加到main()
函数中的代码分为六部分:
-
So, in the pursuit of finding all the cars owned by Jane, let's see what functionality our XML document provides for us right off the bat. If we take a look at code completion to quickly scan through our list of methods, we can call
dom
from ourDocument
instance. We're going to see thegetDocumentElement()
method return an element for us:
这可能是一个很好的开始。此方法返回 XML 中的顶级元素;在本例中,我们将获得cars
元素,它包含我们需要的所有信息。它还包含一些我们不需要的信息,比如比利的车,但我们会在访问后解析出来。一旦我们导入了正确的库,我们就可以使用Element
类在代码中直接引用 XML 元素的概念。我们可以创建一个新的Element
对象,并将其值分配给 XML 文档的根元素:
Element doc = dom.getDocumentElement();
当然,我们需要更深入。XML 文档cars
的根级别对我们没有直接的帮助;我们需要其中包含的信息。我们只需要一个owner
节点的信息(包含有关 Jane 汽车的信息)。但是由于 XML 解析的工作方式,我们可能需要首先获取这两个所有者节点,然后找到我们真正感兴趣的节点。
为了获取这两个节点,我们可以对刚刚创建并存储在doc
中的根 XML 元素调用一个方法。XML 元素可以包含其中的其他元素;在本例中,我们的根元素包含许多owner
元素。getElementsByTagName()
方法允许我们收集大量这些内部元素。XML 元素的标记名正是您所期望的;它是我们给 XML 中特定元素的名称。在本例中,如果我们要求文档根元素中包含标记名为owner
的所有元素,我们将进一步缩小正在使用的 XML 的数量,从而更接近我们想要的小部分。
但是,getElementsByTagName()
方法返回的不是单个元素。即使在最高级别,本节也有两个不同的元素,即两个所有者:Billy
和Jane
。因此,getElementsByTagLineName()
方法不返回单个元素;相反,它返回一个NodeList
对象,它是 XML 节点的集合:
NodeList ownersList = doc.getElementsByTagName("owner");
我们现在不再处理根节点;我们只有它的内容。现在是我们缩小搜索范围的时候了。我们的NodeList
对象包含多个所有者,但只有当与该所有者关联的属性名恰好为Jane
时,我们才需要一个所有者。为了找到这个特定的元素,如果它存在,我们只需通过NodeList
循环,检查它包含的每个元素的属性。请注意,ownersList
不是传统的数组。它是一个NodeList
对象,它自己的类型的对象。所以,我们不能对它使用普通数组语法。幸运的是,它向我们展示了模仿正常数组语法的方法。例如,getLength()
方法会告诉我们ownersList
中有多少个对象:
for(int i = 0; i < ownersList.getLength(); i++)
{
}
- 类似地,当我们试图创建一个新的
Element
对象并将该值分配给ownersList
的当前循环部分时,我们将无法使用数组的常规语法。然而,ownersList
再次为我们提供了一种做同样事情的方法。item()
方法提供或请求索引作为输入。
注意,ownersList
是NodeList
,但是虽然元素是节点,但并非所有节点都是元素,所以我们需要在这里做出决定。我们可以检查此函数返回的对象的性质,并确保它们实际上是 XML 元素。但是为了让事情继续发展,我们假设我们的 XML 格式正确,我们只是让 Java 知道,item()
方法返回的节点实际上是一个元素;“开始”标记和“结束”标记中的其他元素可以是:
Element owner = (Element)ownersList.item(i);
一旦我们成功地从我们的所有者列表中访问了一个元素,就应该检查并查看这是否是我们正在寻找的所有者;因此,我们需要一个条件语句。XML 元素向我们公开了getAttribute()
方法,我们感兴趣的属性是name
属性。因此,下面的代码将询问当前的owner
,“您的name
属性的值是多少?”如果该值等于Jane
,那么我们就知道访问了正确的 XML 元素。
在 Jane 的 XML 元素中,现在我们只有一些car
元素。所以,再一次,是时候创建NodeList
并用这些car
元素填充它了。我们现在需要调用Jane
上的getElementByTagName()
方法,我们当前的所有者。如果我们使用顶级文档调用此函数,我们将获得文档中的所有car
元素,甚至 Billy 的:
if(owner.getAttribute("name").equals("Jane"))
{
NodeList carsList =
owner.getElementsByTagName("car");
- 这个
main()
方法有点紧张了;这就是我愿意用一种方法走多远。我们的代码已经深入了两个层次,而且我们编写的代码并不简单。我认为现在是我们将下一位解析为它自己的方法的时候了。让我们简单地声明我们将有一个PrintCars()
方法,这个函数将使用car
元素中的NodeList
来打印 car 节点:
PrintCars(carsList);
我们的main
方法如下:
public static void main(String[] args) {
DocumentBuilderFactory factory =
DocumentBuilderFactory.newInstance();
try {
DocumentBuilder docBuilder =
factory.newDocumentBuilder();
Document dom = docBuilder.parse("cars.xml");
// Now, print out all of Jane's cars
Element doc = dom.getDocumentElement();
NodeList ownersList =
doc.getElementsByTagName("owner");
for(int i = 0; i < ownersList.getLength(); i++)
{
Element owner = (Element)ownersList.item(i);
if(owner.getAttribute("name").equals("Jane"))
{
NodeList carsList =
owner.getElementsByTagName("car");
PrintCars(carsList);
}
}
}
catch (ParserConfigurationException pce) {
System.out.println(pce.getMessage());
}
catch (SAXException se) {
System.out.println(se.getMessage());
}
catch (IOException ioe) {
System.err.println(ioe.getMessage());
}
}
现在,离开我们的main()
方法,我们将定义新的PrintCars()
方法。我将PrintCars()
函数的定义分为八个部分:
- 因为我们在程序的 entry 类中,
PrintCars()
方法被静态main()
方法调用,所以它可能是一个static
函数。它所要做的就是打印到我们的控制台上,所以void
是一个合适的返回类型。我们已经知道它将以NodeList
辆车作为输入:
public static void PrintCars(NodeList cars)
{
}
- 一旦我们输入了这个函数,我们就知道我们有一个
car
XML 元素列表可供使用。但是为了把每一个都打印出来,我们必须对它们进行循环。我们已经在程序中循环使用了 XMLNodeList
,所以我们将使用一些非常类似的语法。让我们来看看这个新代码需要改变什么。好吧,我们不再循环通过ownersList
;我们有一个新的NodeList
对象要循环通过cars
的NodeList
:
for(int i = 0; i < cars.getLength(); i++)
{
}
- 我们知道汽车仍然是
Element
实例,所以我们在这里设置一个快捷方式仍然是合适的,但我们可能希望将我们循环使用的每个car
变量重命名为类似carNode
的变量。每次我们循环一辆车时,我们都要创建一个新的Car
对象,并将信息存储在该车的 XML 中,存储在这个实际的 Java 对象中:
Element carNode = (Element)cars.item(i);
- 因此,除了访问
car
XML,我们还声明一个Car
对象,并将其实例化为一个新的Car
对象:
Car carObj = new Car();
- 现在我们将通过从
carNode
中读取carObj
中存储的值来建立这些值。如果我们快速跳回 XML 文件并查看存储在car
元素中的信息,我们将看到它将make
、model
、year
和color
存储为 XML 节点。车辆识别号vin
实际上是一个属性。让我们简单地看看我们的 T8 课:
package readingxml;
public class Car {
public String vin;
public String make;
public String model;
public int year;
public String color;
public Car()
{
}
@Override
public String toString()
{
return String.format("%d %s %s %s, vin:%s", year,
color, make, model, vin);
}
}
让我们先从简单的部分开始;所以,make
、model
和color
都是存储在Car
类中的字符串,它们恰好都是car
元素中的节点。
回到PrintCars()
函数,我们已经知道如何访问元素中的节点。我们将再次使用carNode
和getElementsByTagName()
函数。如果我们得到标签名为color
的所有元素,我们应该得到一个列表,其中只包含一个元素,即我们感兴趣的元素,它告诉我们汽车的颜色。不幸的是,我们这里确实有一个列表,所以我们不能直接操作该元素,除非我们从列表中提取它。不过,再一次,我们知道如何做到这一点。如果我们确信 XML 格式正确,我们就知道我们将得到一个只包含一项的列表。因此,如果我们在该列表的第 0 个索引处获得该项,那么这将是我们要查找的 XML 元素。
但是,存储在这个 XML 元素中的颜色信息不是属性;这是内在的文本。因此,我们将了解 XML 元素公开了哪些方法,并查看是否有合适的方法来获取内部文本。有一个getTextContent()
函数,它将为我们提供所有实际上不属于 XML 元素标记的内部文本。在这种情况下,它将为我们提供汽车的颜色。
仅仅获取这些信息是不够的;我们需要储存它。幸运的是,carObj
的所有属性都是公共的,因此我们可以在创建car
对象后自由地为它们赋值。如果这些字段是没有 setter 的私有字段,我们可能必须在构造carObj
之前完成这些信息,然后将它们传递给构造函数,它希望有:
carObj.color =
carNode.getElementsByTagName("color").item(0).getTextContent();
我们将为make
和model
做几乎完全相同的事情。我们唯一需要更改的是在查找元素时提供的关键字:
carObj.make =
carNode.getElementsByTagName("make").item(0).getTextContent();
carObj.model =
carNode.getElementsByTagName("model").item(0).getTextContent();
- 现在,我们可以继续对我们的车的
year
使用相同的总体策略,但我们应该注意,就carObj
而言,year
是一个整数。就我们的 XML 元素而言,year
和其他元素一样,是一个TextContent
字符串。幸运的是,将一个string
转换成一个integer
,只要它是格式良好的,这是我们将在这里做的一个假设,并不太困难。我们将简单地使用Integer
类并调用其parseInt()
方法。这将尽最大努力将字符串值转换为整数。我们将其分配到carObj
的year
字段:
carObj.year =
Integer.parseInt(carNode.getElementsByTagName
("year").item(0).getTextContent());
- 这只给我们留下了一个领域。注意,
carObj
有一个车辆识别号字段。该字段实际上不是整数;车辆识别号可以包含字母,因此该值存储为字符串。这对我们来说会有点不同,因为它不是一个内部元素,而是car
元素本身的属性。再一次,我们知道如何从carNode
获取属性;我们只需获取名为vin
的属性并将其分配给carObj
:
carObj.vin = carNode.getAttribute("vin");
- 所有这些都完成后,我们的
carObj
对象应该在其所有成员中都具有合理的价值。现在是使用carObj
的时候了,因为它存在的原因:覆盖toString()
功能。对于我们循环通过的每辆车,让我们调用carObj
的toString()
函数并将结果打印到控制台:
System.out.println(carObj.toString());
我们的PrintCars()
函数现在如下所示:
public static void PrintCars(NodeList cars)
{
for(int i = 0; i < cars.getLength(); i++)
{
Element carNode = (Element)cars.item(i);
Car carObj = new Car();
carObj.color =
carNode.getElementsByTagName
("color").item(0).getTextContent();
carObj.make =
carNode.getElementsByTagName
("make").item(0).getTextContent();
carObj.model = carNode.getElementsByTagName
("model").item(0).getTextContent();
carObj.year =
Integer.parseInt(carNode.getElementsByTagName
("year").item(0).getTextContent());
carObj.vin = carNode.getAttribute("vin");
System.out.println(carObj.toString());
}
}
我们应该善于编译我们的程序。现在,当我们运行它时,希望它能打印出 Jane 的所有汽车,利用被重写的carObj
方法,很好地格式化输出。当我们运行这个程序时,我们得到两辆车作为输出打印出来,如果我们转到 XML 并查看分配给 Jane 的车,我们将看到这些信息确实与存储在这些车中的信息相匹配:
XML 和 Java 的结合非常强大。XML 是人类可读的。我们可以理解它,甚至可以作为人对它进行修改,但它也包含关于它的结构的真正有价值的信息。这也是编程语言(如 Java)可以理解的。我们在这里编写的程序确实有它的怪癖,需要一定的知识来编写。与来自原始文本文件的类似程序相比,它编写起来容易得多,程序员理解和维护起来也容易得多。
能够读取 XML 信息是非常好的,但要使该语言对我们真正有用,我们的 Java 程序可能还需要能够写出 XML 信息。以下程序是对同一 XML 文件进行读取和写入的程序的基本模型:
package writingxml;
import java.io.*;
import javax.xml.parsers.*;
import javax.xml.transform.*;
import javax.xml.transform.dom.*;
import javax.xml.transform.stream.*;
import org.w3c.dom.*;
import org.xml.sax.*;
public class WritingXML {
public static void main(String[] args) {
File xmlFile = new File("cars.xml");
Document dom = LoadXMLDocument(xmlFile);
WriteXMLDocument(dom, xmlFile);
}
private static void WriteXMLDocument
(Document doc, File destination)
{
try{
// Write doc to destination file here...
}
catch(TransformerConfigurationException e)
{
System.err.println("XML writing failed.");
}
catch(TransformerException e)
{
System.err.println("XML writing failed.");
}
}
private static Document LoadXMLDocument(File source)
{
try {
DocumentBuilderFactory factory =
DocumentBuilderFactory.newInstance();
DocumentBuilder builder =
factory.newDocumentBuilder();
Document dom = builder.parse(source);
}
catch (ParserConfigurationException e) {
System.err.println("XML loading failed.");
}
catch (SAXException e) {
System.err.println("XML loading failed.");
}
catch (IOException e) {
System.err.println("XML loading failed.");
}
return dom;
}
}
它的main()
方法非常简单。它获取一个文件,然后从该文件中读取 XML,并将其存储在 XML 文档的树对象中。然后,这个程序调用WriteXMLDocument()
将 XML 写回同一个文件。目前,我们已经实现了读取 XML 的方法(LoadXMLDocument()
;然而,写出 XML 的方法还没有完成。让我们看看将 XML 信息写入文档需要做些什么。我已经将WriteXMLDocument()
功能的代码分为四个部分。
以下是编写 XML 数据要执行的步骤:
- 由于 XML 文档的存储方式,我们需要先将其转换为不同的格式,然后才能将其打印到与原始 XML 相同格式的文件中。为此,我们将使用一个名为
Transformer
的 XML 特定类。与我们在文档模型中处理 XML 时处理的许多类一样,Transformer
实例最好使用工厂创建。在本例中,工厂名为TransformerFactory
,与许多工厂一样,它公开了newInstance()
方法,允许我们在需要时创建一个。为了获得新的Transformer
对象,这将允许我们将Document
对象转换为可以发送到文件的可流化对象,我们只需调用TransformerFactory
的newTransformer()
方法:
TransformerFactory tf = TransformerFactory.newInstance();
Transformer transformer = tf.newTransformer();
- 现在,在
Transformer
可以将 XML 文档转换为其他内容之前,它需要知道我们希望它将 XML 文档的信息转换为什么。该类为StreamResult
类;它是存储在当前Document
对象中的信息的目标。该流是一个原始二进制信息泵,可以发送到任意数量的目标。在这种情况下,我们的目标是提供给StreamResult
构造函数的目标文件:
StreamResult result = new StreamResult(destination);
- 然而,我们的
Transformer
对象并没有自动链接到 XML 文档,它希望我们以一种独特的方式引用 XML 文档:作为DOMSource
对象。请注意,我们的source
对象(定义如下)与result
对象配对。Transformer
对象,当我们同时提供这两个对象时,将知道如何将一个对象转换为另一个对象。现在,要创建DOMSource
对象,只需传入 XML 文档:
DOMSource source = new DOMSource(doc);
- 最后,当所有设置完成后,我们可以执行代码的功能部分。让我们抓取我们的
Transformer
对象,让它将我们的源(即DOMSource
对象)转换为针对目标文件的可流化结果:
transformer.transform(source, result);
以下是我们的WriteXMLDocument()
功能:
private static void WriteXMLDocument
(Document doc, File destination)
{
try{
// Write doc to destination file here
TransformerFactory tf =
TransformerFactory.newInstance();
Transformer transformer = tf.newTransformer();
StreamResult result = new StreamResult(destination);
DOMSource source = new DOMSource(doc);
transformer.transform(source, result);
}
catch(TransformerConfigurationException e)
{
System.err.println("XML writing failed.");
}
catch(TransformerException e)
{
System.err.println("XML writing failed.");
}
}
当我们运行这个程序时,我们会在文件中得到一些 XML,但是我说的时候你必须相信我:这是我们以前拥有的同一个 XML,这是我们首先读入 XML,然后打印回来的结果。
为了真正测试我们的程序是否正常工作,我们需要在 Java 代码中对Document
对象进行一些更改,然后看看是否可以将这些更改打印到此文件中。让我们换一下车主的名字。让我们把所有汽车的交易转给一个叫迈克的车主。
XML I/O 系统的强大之处在于,在加载和写入 XML 文档之间,我们可以自由修改存储在内存中的Document
对象dom
。此外,我们对 Java 内存中的对象所做的更改将写入我们的永久 XML 文件。因此,让我们从做一些更改开始:
- 我们将使用
getElementsByTagName()
获取 XML 文档中的所有owner
元素。这将返回一个NodeList
对象,我们将其称为owners
:
NodeList owners = dom.getElementsByTagName("owner");
- 要将所有这些所有者的姓名转换为
Mike
,我们必须循环浏览此列表。作为复习,我们可以通过调用owners
的getLength()
函数,即我们的NodeList
对象来获取列表中的项数。要访问当前正在迭代的项,我们将使用owners
的item()
函数并传入迭代变量i
以获取该索引处的项。让我们把这个值存储在一个变量中,这样我们就可以很容易地使用它;再一次,我们将假设我们的 XML 格式良好,并告知 Java,实际上,此时我们正在处理一个成熟的 XML 元素。
接下来,XML 元素公开了许多方法,允许我们修改它们。其中一个元素是setAttribute()
方法,这就是我们将在这里使用的。注意,setAttribute()
接受两个字符串作为输入。首先,它想知道我们想要修改什么属性。我们将修改name
属性(这是我们在这里唯一可用的属性),并将其值分配给Mike
:
for(int i = 0; i < owners.getLength(); i++)
{
Element owner = (Element)owners.item(i);
owner.setAttribute("name", "Mike");
}
现在我们的main()
方法如下:
public static void main(String[] args) {
File xmlFile = new File("cars.xml");
Document dom = LoadXMLDocument(xmlFile);
NodeList owners = dom.getElementsByTagName("owner");
for(int i = 0; i < owners.getLength(); i++)
{
Element owner = (Element)owners.item(i);
owner.setAttribute("name", "Mike");
}
WriteXMLDocument(dom, xmlFile);
}
当我们运行程序并检查 XML 文件时,我们将看到Mike
现在是所有这些汽车的所有者,如以下屏幕截图所示:
现在,将这两个 XML 元素组合起来,使Mike
只是一个所有者,而不是将其拆分为两个,这可能是有意义的。这有点超出了本节的范围,但这是一个有趣的问题,我想鼓励你们反思一下,也许现在就可以尝试一下。
在本章中,我们看到了将 XML 文件读入Document
对象的 Java 代码。我们还了解了如何使用 Java 解析 XML 数据。最后,我们了解了如何在 Java 中编写和修改 XML 数据。
祝贺您现在是一名 Java 程序员。