XML技术全面解析:从基础到实践
立即解锁
发布时间: 2025-08-18 00:08:32 阅读量: 1 订阅数: 9 

### XML 技术全面解析:从基础到实践
#### 1. XML 简介
在软件开发中,我们常常需要描述程序的配置信息。以往,我们会使用属性文件,它包含一组键值对,例如:
```plaintext
fontname=Times Roman
fontsize=12
windowsize=400 200
color=0 50 100
```
我们可以使用 `Properties` 类通过一个方法调用读取这样的文件。但属性文件存在一些局限性:
- **结构表达能力有限**:很多时候,我们要描述的信息结构比属性文件能处理的更复杂。比如,将字体名称和大小合并为一个条目 `font=Times Roman 12` 会使解析变得困难,需要确定字体名称和大小的边界。
- **层次结构单一**:属性文件是扁平的层次结构,虽然程序员可以通过一些技巧(如 `title.fontname=Helvetica`)来绕过这个限制,但并不理想。
- **键的唯一性问题**:键必须唯一,存储值序列时需要额外的处理,如 `menu.item.1=Times Roman`。
而 XML 格式可以解决这些问题,它能够表达层次结构,比属性文件的扁平结构更加灵活。一个描述程序配置的 XML 文件可能如下所示:
```xml
<configuration>
<title>
<font>
<name>Helvetica</name>
<size>36</size>
</font>
</title>
<body>
<name>Times Roman</name>
<size>12</size>
</body>
<window>
<width>400</width>
<height>200</height>
</window>
<color>
<red>0</red>
<green>50</green>
<blue>100</blue>
</color>
<menu>
<item>Times Roman</item>
<item>Helvetica</item>
<item>Goudy Old Style</item>
</menu>
</configuration>
```
XML 和 HTML 都源于标准通用标记语言(SGML),但它们之间存在重要差异:
| 对比项 | XML | HTML |
| ---- | ---- | ---- |
| 大小写敏感性 | 区分大小写,如 `<H1>` 和 `<h1>` 是不同的标签 | 不区分大小写 |
| 结束标签 | 不能省略结束标签 | 某些情况下可以省略结束标签 |
| 单标签元素 | 单标签元素必须以 `/` 结尾,如 `<img src="coffeecup.png"/>` | 无此要求 |
| 属性值引号 | 属性值必须用引号括起来 | 引号可选 |
| 属性值要求 | 所有属性必须有值 | 可以有不带值的属性名 |
#### 2. XML 文档结构
一个 XML 文档通常以头部开始,例如:
```xml
<?xml version="1.0"?>
```
或者
```xml
<?xml version="1.0" encoding="UTF-8"?>
```
虽然头部是可选的,但强烈建议添加。
头部之后通常是文档类型声明,例如:
```xml
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN"
"http://java.sun.com/j2ee/dtds/web-app_2_2.dtd">
```
文档类型声明是确保文档正确性的重要机制,但不是必需的。
最后,XML 文档的主体包含根元素,根元素可以包含其他元素。例如:
```xml
<?xml version="1.0"?>
<!DOCTYPE configuration ...>
<configuration>
<title>
<font>
<name>Helvetica</name>
<size>36</size>
</font>
</title>
...
</configuration>
```
一个元素可以包含子元素、文本或两者。例如,`font` 元素有两个子元素 `name` 和 `size`,`name` 元素包含文本 `"Helvetica"`。
XML 元素可以包含属性,如 `<size unit="pt">36</size>`。在 XML 设计中,关于何时使用元素和何时使用属性存在一些争议。一般来说,属性应仅用于修改值的解释,而不是指定值。如果对某个设置是否是对值解释的修改存在疑问,建议使用元素而不是属性。许多有用的 DTD 根本不使用属性。
此外,XML 文档中还有一些其他的标记指令:
- **字符引用**:形式为 `&#d;` 或 `&#xh;`,其中 `d` 是十进制 Unicode 值,`h` 是十六进制 Unicode 值,如 `é` 表示字符 `é`,`™` 表示字符 `™`。
- **实体引用**:形式为 `&name;`,如 `<` 表示小于号,`>` 表示大于号等。可以在文档类型定义(DTD)中定义其他实体引用。
- **CDATA 部分**:由 `<![CDATA[` 和 `]]>` 分隔,用于包含包含 `<`、`>`、`&` 等字符的字符串,而不将它们解释为标记,例如 `<![CDATA[< & > are my favorite delimiters]]>`。
- **处理指令**:由 `<?` 和 `?>` 分隔,例如 `<?xml-stylesheet href="mystyle.css" type="text/css"?>`,用于为处理 XML 文档的应用程序提供信息。
- **注释**:由 `<!--` 和 `-->` 分隔,例如 `<!-- This is a comment. -->`,注释仅用于人类读者,不应包含隐藏命令。
#### 3. 解析 XML 文档
要处理 XML 文档,需要对其进行解析。XML 解析器有两种类型:
- **文档对象模型(DOM)解析器**:将 XML 文档读入树结构。
- **简单 XML API(SAX)解析器**:在读取 XML 文档时生成事件。
对于大多数情况,DOM 解析器更易于使用。如果处理非常长的文档,其树结构会占用大量内存,或者只对少数元素感兴趣而不关心其上下文,则可以考虑使用 SAX 解析器。
以下是使用 DOM 解析器读取 XML 文档的步骤:
1. 获取 `DocumentBuilderFactory` 实例:
```java
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
```
2. 获取 `DocumentBuilder` 实例:
```java
DocumentBuilder builder = factory.newDocumentBuilder();
```
3. 读取文档:
- 从文件读取:
```java
File f = ...
Document doc = builder.parse(f);
```
- 从 URL 读取:
```java
URL u = ...
Document doc = builder.parse(u);
```
- 从输入流读取:
```java
InputStream in = ...
Document doc = builder.parse(in);
```
`Document` 对象是 XML 文档树结构的内存表示,它由实现 `Node` 接口及其各种子接口的对象组成。
要分析文档内容,可以通过以下步骤:
1. 获取根元素:
```java
Element root = doc.getDocumentElement();
```
2. 获取元素的标签名:
```java
String tagName = root.getTagName();
```
3. 获取元素的子节点:
```java
NodeList children = root.getChildNodes();
```
在分析子节点时需要注意,解析器可能会包含空白字符。例如,对于以下文档:
```xml
<font>
<name>Helvetica</name>
<size>36</size>
</font>
```
`font` 元素的子节点可能包含空白字符,实际会有 5 个节点。如果只关心子元素,可以忽略空白字符:
```java
for (int i = 0; i < children.getLength(); i++) {
Node child = children.item(i);
if (child instanceof Element) {
Element childElement = (Element) child;
// 处理子元素
}
}
```
要获取元素中的文本内容,可以使用以下方法:
```java
for (int i = 0; i < children.getLength(); i++) {
Node child = children.item(i);
if (child instanceof Element) {
Element childElement = (Element) child;
Text textNode = childElement.getFirstChild();
String text = textNode.getData().trim();
if (childElement.getTagName().equals("name")) {
name = text;
} else if (childElement.getTagName().equals("size")) {
size = Integer.parseInt(text);
}
}
}
```
还可以通过 `getLastChild` 获取最后一个子节点,通过 `getNextSibling` 获取下一个兄弟节点。遍历子节点的另一种方式是:
```java
for (Node childNode = element.getFirstChild();
childNode != null;
childNode = childNode.getNextSibling()) {
// 处理子节点
}
```
要枚举节点的属性,可以使用 `getAttributes` 方法:
```java
NamedNodeMap attributes = element.getAttributes();
for (int i = 0; i < attributes.getLength(); i++) {
Node attribute = attributes.item(i);
String name = attribute.getNodeName();
String value = attribute.getNodeValue();
// 处理属性
}
```
或者直接通过属性名获取属性值:
```java
String unit = element.getAttribute("unit");
```
以下是一个完整的示例代码(`DOMTreeTest.java`),用于将 XML 文档显示为树:
```java
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.tree.*;
import javax.xml.parsers.*;
import org.w3c.dom.*;
import org.xml.sax.*;
/**
* This program displays an XML document as a tree.
*/
public class DOMTreeTest {
public static void main(String[] args) {
JFrame frame = new DOMTreeFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.show();
}
}
/**
* This frame contains a tree that displays
```
0
0
复制全文
相关推荐









