目录
-
XML 文档解析
(图片来源网络,侵删)- SAX (Simple API for XML)
- DOM (Document Object Model)
- StAX (Streaming API for XML)
- JAXB (Java Architecture for XML Binding)
- 总结与选择建议
-
JSON 文档解析
- Jackson
- Gson
- org.json
- 总结与选择建议
-
HTML 文档解析
- Jsoup
- 总结与选择建议
-
CSV 文档解析
- OpenCSV
- Apache Commons CSV
- Java 8+ Stream (简单场景)
- 总结与选择建议
-
通用文档解析 (基于 POI)
(图片来源网络,侵删)
XML 文档解析
XML 是一种标记语言,在 Java 中有非常成熟的解析方案。
a. SAX (Simple API for XML)
- 核心思想:事件驱动的流式解析器,它不会将整个 XML 文档加载到内存中,而是从上到下逐行读取,当解析器遇到开始标签、结束标签或文本内容时,会触发相应的事件(如
startElement,endElement,characters),开发者需要通过实现ContentHandler接口来处理这些事件。 - 优点:
- 内存效率极高:因为它不构建整个文档树,只处理当前节点,所以非常适合解析非常大的 XML 文件。
- 速度快:解析过程是顺序的,没有复杂的对象构建。
- 缺点:
- 编程复杂:需要手动维护状态,如果需要访问文档的任意部分,会非常困难。
- 只读:只能读取数据,不能修改或创建 XML 文档。
- 适用场景:处理大型 XML 文件,且只需要顺序读取数据,例如日志文件、配置文件等。
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import java.io.InputStream;
public class SaxParserExample {
public static void main(String[] args) {
try {
SAXParserFactory factory = SAXParserFactory.newInstance();
SAXParser saxParser = factory.newSAXParser();
// 假设有一个 books.xml 文件在 classpath 下
InputStream inputStream = SaxParserExample.class.getResourceAsStream("/books.xml");
DefaultHandler handler = new DefaultHandler() {
boolean inBook = false;
boolean inTitle = false;
boolean inAuthor = false;
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
if (qName.equalsIgnoreCase("book")) {
inBook = true;
System.out.println("\n--- 开始解析一本书 ---");
String id = attributes.getValue("id");
System.out.println("Book ID: " + id);
} else if (inBook && qName.equalsIgnoreCase("title")) {
inTitle = true;
} else if (inBook && qName.equalsIgnoreCase("author")) {
inAuthor = true;
}
}
@Override
public void characters(char[] ch, int start, int length) throws SAXException {
if (inTitle) {
System.out.println("Title: " + new String(ch, start, length));
inTitle = false;
}
if (inAuthor) {
System.out.println("Author: " + new String(ch, start, length));
inAuthor = false;
}
}
@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
if (qName.equalsIgnoreCase("book")) {
inBook = false;
}
}
};
saxParser.parse(inputStream, handler);
} catch (Exception e) {
e.printStackTrace();
}
}
}
b. DOM (Document Object Model)
- 核心思想:将整个 XML 文档加载到内存中,构建一个树形结构(DOM 树),开发者可以通过操作这个树来访问、修改、添加或删除文档中的任何元素。
- 优点:
- 编程简单直观:可以像操作普通对象一样操作 XML,可以随意导航、查询和修改文档。
- 功能强大:支持 XPath 等查询语言,方便定位节点。
- 缺点:
- 内存消耗大:整个文档都加载到内存中,对于大型文件会导致性能问题和内存溢出。
- 速度较慢:加载和构建树需要时间。
- 适用场景:XML 文档较小,或者需要对文档进行复杂的随机访问和修改操作。
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.InputStream;
public class DomParserExample {
public static void main(String[] args) {
try {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
InputStream inputStream = DomParserExample.class.getResourceAsStream("/books.xml");
Document document = builder.parse(inputStream);
document.getDocumentElement().normalize();
System.out.println("Root element: " + document.getDocumentElement().getNodeName());
NodeList nodeList = document.getElementsByTagName("book");
for (int i = 0; i < nodeList.getLength(); i++) {
Node node = nodeList.item(i);
if (node.getNodeType() == Node.ELEMENT_NODE) {
Element element = (Element) node;
System.out.println("\n--- Book " + (i + 1) + " ---");
System.out.println("ID: " + element.getAttribute("id"));
System.out.println("Title: " + element.getElementsByTagName("title").item(0).getTextContent());
System.out.println("Author: " + element.getElementsByTagName("author").item(0).getTextContent());
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
c. StAX (Streaming API for XML)
- 核心思想:介于 SAX 和 DOM 之间,它也是一种流式 API,但它允许应用程序“拉取”事件,而不是像 SAX 那样由解析器“推送”事件,开发者可以控制解析流程,随时暂停和继续。
- 优点:
- 内存效率高:和 SAX 一样,不加载整个文档。
- 编程比 SAX 简单:应用程序有更多的控制权,状态管理更容易。
- 可读可写:既可以读取,也可以生成 XML。
- 缺点:
不如 DOM 灵活,随机访问能力有限。
- 适用场景:需要比 SAX 更多控制流的大型 XML 文件处理,或者需要同时读取和生成 XML。
import javax.xml.stream.XMLEventReader;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.events.XMLEvent;
import java.io.InputStream;
public class StaxParserExample {
public static void main(String[] args) {
try {
XMLInputFactory factory = XMLInputFactory.newInstance();
InputStream inputStream = StaxParserExample.class.getResourceAsStream("/books.xml");
XMLEventReader eventReader = factory.createXMLEventReader(inputStream);
while (eventReader.hasNext()) {
XMLEvent event = eventReader.nextEvent();
if (event.isStartElement()) {
String elementName = event.asStartElement().getName().getLocalPart();
if (elementName.equals("book")) {
System.out.println("\n--- 开始解析一本书 ---");
String id = event.asStartElement().getAttributeByName("id").getValue();
System.out.println("Book ID: " + id);
}
} else if (event.isEndElement()) {
String elementName = event.asEndElement().getName().getLocalPart();
if (elementName.equals("book")) {
System.out.println("--- 结束解析一本书 ---");
}
} else if (event.isCharacters()) {
String data = event.asCharacters().getData().trim();
if (!data.isEmpty()) {
// 简单判断父元素
// 实际项目中需要更复杂的状态管理
System.out.println("Content: " + data);
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
d. JAXB (Java Architecture for XML Binding)
- 核心思想:这是一种数据绑定技术,它能够将 Java 对象与 XML 文档自动进行映射,通过注解(如
@XmlRootElement,@XmlElement),你可以告诉 JAXB 如何将一个 Java 类序列化为 XML,或者将 XML 反序列化为 Java 对象。 - 优点:
- 开发效率极高:代码非常简洁,只需定义 POJO (Plain Old Java Object),无需编写解析逻辑。
- 面向对象:操作的是 Java 对象,而不是底层的 XML 节点,更符合 Java 编程思想。
- 缺点:
- 依赖 Java 9+ 的模块系统:在 Java 9 及更高版本中,JAXB 不再包含在 JDK 中,需要单独添加
jakarta.xml.bind-api和实现(如com.sun.xml.bind:jaxb-impl)依赖。 - 内存消耗大:本质上还是基于 DOM,需要将整个 XML 映射到对象树。
- 依赖 Java 9+ 的模块系统:在 Java 9 及更高版本中,JAXB 不再包含在 JDK 中,需要单独添加
- 适用场景:在 Web 服务(如 JAX-WS, JAX-RS)中处理 XML 数据,或者任何需要将 XML 与 Java 对象进行频繁转换的场景。
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Unmarshaller;
import java.io.InputStream;
import java.util.List;
// 1. 定义与 XML 结构对应的 Java 类
@XmlRootElement(name = "catalog")
class Catalog {
@XmlElement(name = "book")
private List<Book> books;
// getters and setters
}
class Book {
@XmlAttribute
private String id;
@XmlElement
private String title;
@XmlElement
private String author;
// getters and setters
}
// 2. 使用 JAXB 进行解析
public class JaxbParserExample {
public static void main(String[] args) {
try {
JAXBContext jaxbContext = JAXBContext.newInstance(Catalog.class);
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
InputStream inputStream = JaxbParserExample.class.getResourceAsStream("/books.xml");
Catalog catalog = (Catalog) unmarshaller.unmarshal(inputStream);
for (Book book : catalog.getBooks()) {
System.out.println("ID: " + book.getId() + ", Title: " + book.getTitle() + ", Author: " + book.getAuthor());
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
XML 解析总结与选择建议
| 特性 | SAX | DOM | StAX | JAXB |
|---|---|---|---|---|
| 内存占用 | 极低 | 高 | 低 | 高 |
| 速度 | 快 | 慢 | 较快 | 较快 |
| 易用性 | 复杂 | 简单 | 中等 | 非常简单 |
| 功能 | 只读 | 读写 | 读写 | 读写(对象映射) |
| 适用场景 | 大文件、顺序读取 | 小文件、随机访问、修改 | 大文件、需要控制流 | Web服务、对象与XML转换 |
- 选择 SAX:当处理几百兆甚至更大的 XML 文件,且你只需要从头到尾扫描一遍数据时。
- 选择 DOM:当 XML 文件很小(几兆以内),或者你需要频繁地在文档中跳转、查询、修改数据时。
- 选择 StAX:当你需要比 SAX 更好的控制流,或者同时需要读写 XML,但又不想承受 DOM 的内存开销时。
- 选择 JAXB:当你主要关心的是将 XML 数据转换成 Java 对象进行业务处理,而不想关心底层 XML 解析细节时,这是现代 Java 开发中最常用、最高效的方式之一。
JSON 文档解析
JSON 是目前最流行的数据交换格式,Java 生态中有三大主流库。
a. Jackson
- 核心思想:功能强大、性能极高、社区活跃的 JSON 处理库,它不仅能进行对象绑定,还支持树模型、流式 API 等多种处理方式。
- 优点:
- 性能卓越:在所有 JSON 库中通常是最快的。
- 功能全面:支持数据绑定、树模型、流式 API、注解、模块化扩展(如支持 Java 8 时间类型)。
- 生态好:Spring Framework 默认使用 Jackson。
- 缺点:
API 相对复杂,有陡峭的学习曲线。
- 适用场景:几乎所有场景,尤其是对性能有要求的 Web 应用、微服务、大数据处理。
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.File;
import java.io.InputStream;
// 1. 定义一个与 JSON 结构匹配的 Java 类
class User {
private String name;
private int age;
// getters and setters
}
public class JacksonExample {
public static void main(String[] args) {
try {
ObjectMapper mapper = new ObjectMapper();
// 从文件解析
// User user = mapper.readValue(new File("user.json"), User.class);
// 从字符串解析
String jsonString = "{\"name\":\"Alice\",\"age\":30}";
User user = mapper.readValue(jsonString, User.class);
System.out.println("Name: " + user.getName());
System.out.println("Age: " + user.getAge());
// 将对象转换为 JSON 字符串
String jsonOutput = mapper.writeValueAsString(user);
System.out.println("\nOutput JSON: " + jsonOutput);
} catch (Exception e) {
e.printStackTrace();
}
}
}
b. Gson
- 核心思想:由 Google 开发,以其简洁的 API 和易用性而闻名。
- 优点:
- API 简单直观:非常容易上手,代码可读性高。
- 轻量级:库体积小。
- 功能稳定:经过 Google 内部大规模项目的长期检验。
- 缺点:
性能略逊于 Jackson。
- 适用场景:中小型项目,或者对开发便利性要求高于极致性能的场景。
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import java.lang.reflect.Type;
import java.util.Map;
public class GsonExample {
public static void main(String[] args) {
Gson gson = new Gson();
String jsonString = "{\"name\":\"Bob\",\"age\":25}";
// 解析为对象
User user = gson.fromJson(jsonString, User.class);
System.out.println("Name: " + user.getName());
System.out.println("Age: " + user.getAge());
// 解析为 Map
Type mapType = new TypeToken<Map<String, Object>>() {}.getType();
Map<String, Object> dataMap = gson.fromJson(jsonString, mapType);
System.out.println("\nData as Map: " + dataMap);
// 对象转 JSON
String jsonOutput = gson.toJson(user);
System.out.println("\nOutput JSON: " + jsonOutput);
}
}
c. org.json
- 核心思想:一个非常轻量级的库,提供了
JSONObject和JSONArray两个核心类来直接操作 JSON 数据。 - 优点:
- 非常轻量:只有一个 JAR 文件。
- API 直接:直接操作 JSON 结构,无需创建 Java 类。
- 缺点:
- 需要手动处理类型转换,容易出错。
- 功能相对较少,没有数据绑定。
- 适用场景:只需要快速解析或生成简单的 JSON,而不想定义对应的 Java 类。
import org.json.JSONObject;
import org.json.JSONArray;
public class OrgJsonExample {
public static void main(String[] args) {
String jsonString = "{\"name\":\"Charlie\",\"age\":40,\"hobbies\":[\"reading\",\"swimming\"]}";
// 解析 JSONObject
JSONObject jsonObject = new JSONObject(jsonString);
String name = jsonObject.getString("name");
int age = jsonObject.getInt("age");
System.out.println("Name: " + name);
System.out.println("Age: " + age);
// 解析 JSONArray
JSONArray hobbies = jsonObject.getJSONArray("hobbies");
for (int i = 0; i < hobbies.length(); i++) {
System.out.println("Hobby " + (i + 1) + ": " + hobbies.getString(i));
}
// 构建 JSON
JSONObject newJson = new JSONObject();
newJson.put("city", "New York");
System.out.println("\nNew JSON: " + newJson.toString());
}
}
JSON 解析总结与选择建议
| 特性 | Jackson | Gson | org.json |
|---|---|---|---|
| 性能 | 高 | 中等 | 中等 |
| 易用性 | 中等 | 高 | 中等 |
| 功能 | 全面 | 良好 | 基础 |
| 生态 | 好 (Spring) | 好 | 一般 |
| 适用场景 | 高性能、企业级应用 | 中小型、快速开发 | 轻量级、简单操作 |
- 首选 Jackson:如果你在构建一个新的项目,特别是企业级应用或微服务,Jackson 是不二之选,它的性能和功能都无与伦比。
- 选择 Gson:如果你来自 Google 生态,或者项目对 API 的简洁性要求很高,Gson 是一个非常好的选择。
- 选择 org.json:当你只需要在一个小工具或一个方法中处理一下 JSON,不想引入复杂的对象映射关系时,它很方便。
HTML 文档解析
HTML 格式不严谨,用 XML 解析器(如 DOM)解析很容易失败,需要专门的 HTML 解析器。
Jsoup
- 核心思想:一个专门为 Java 设计的、非常方便的 HTML 解析库,它提供了类似 jQuery 的 CSS 选择器 API,可以非常轻松地提取和操作数据。
- 优点:
- API 极其友好:使用 CSS 选择器 (
select,id,class) 提取数据,直观高效。 - 容错性强:能很好地处理格式不规范的 HTML。
- 功能丰富:不仅能解析,还能修改、清理 HTML,并支持从 URL 或文件直接加载。
- API 极其友好:使用 CSS 选择器 (
- 缺点:
仅适用于 HTML,不适用于 XML 或 JSON。
- 适用场景:网页爬虫、数据抓取、从 HTML 字符串中提取信息。
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import java.io.IOException;
public class JsoupExample {
public static void main(String[] args) {
String html = "<html><head><title>Jsoup Test</title></head>"
+ "<body>"
+ "<div class='header'>Welcome</div>"
+ "<p>This is a paragraph.</p>"
+ "<ul><li>Item 1</li><li>Item 2</li></ul>"
+ "</body></html>";
// 从字符串解析
Document doc = Jsoup.parse(html);
// 1. 提取标题
String title = doc.title();
System.out.println("Title: " + title);
// 2. 使用 CSS 选择器提取元素
// 提取 class 为 'header' 的 div 的文本
Element header = doc.select("div.header").first();
System.out.println("Header text: " + header.text());
// 提取所有 <li> 元素
Elements listItems = doc.select("li");
System.out.println("\nList items:");
for (Element li : listItems) {
System.out.println("- " + li.text());
}
// 3. 从 URL 抓取 (需要网络权限)
try {
// Document onlineDoc = Jsoup.connect("https://en.wikipedia.org/wiki/Main_Page").get();
// String pageTitle = onlineDoc.title();
// System.out.println("\nOnline Page Title: " + pageTitle);
} catch (IOException e) {
System.out.println("无法连接到 URL: " + e.getMessage());
}
}
}
CSV 文档解析
CSV 是一种简单的表格数据格式。
a. OpenCSV
- 核心思想:一个功能强大且流行的 CSV 处理库。
- 优点:
- 功能全面:支持自动类型转换、自定义分隔符、处理带引号的字段等复杂情况。
- 易于使用:提供简单的 API 来读取和写入 CSV 文件。
- 缺点:
需要引入第三方依赖。
- 适用场景:任何需要处理 CSV 文件的场景,尤其是格式可能比较复杂的 CSV。
import com.opencsv.CSVReader;
import com.opencsv.exceptions.CsvValidationException;
import java.io.FileReader;
import java.io.IOException;
public class OpenCsvExample {
public static void main(String[] args) {
String csvFile = "data.csv"; // 假设文件内容为: name,age\nAlice,30\nBob,25
CSVReader reader = null;
try {
reader = new CSVReader(new FileReader(csvFile));
String[] nextLine;
// 读取表头
if ((nextLine = reader.readNext()) != null) {
System.out.println("Header: " + nextLine[0] + ", " + nextLine[1]);
}
// 读取数据行
while ((nextLine = reader.readNext()) != null) {
System.out.println("Name: " + nextLine[0] + ", Age: " + nextLine[1]);
}
} catch (IOException | CsvValidationException e) {
e.printStackTrace();
} finally {
try {
if (reader != null) {
reader.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
b. Apache Commons CSV
- 核心思想:由 Apache 软件基金会维护,提供了更现代和灵活的 API。
- 优点:
- API 设计优秀:使用
Record和Header等概念,代码更清晰。 - 可扩展性强:支持自定义格式和解析策略。
- API 设计优秀:使用
- 缺点:
API 比 OpenCSV 稍微复杂一点。
- 适用场景:与 OpenCSV 类似,但如果你更喜欢 Apache 的设计哲学,这是一个绝佳选择。
import org.apache.commons.csv.CSVFormat;
import org.apache.commons.csv.CSVParser;
import org.apache.commons.csv.CSVRecord;
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;
public class ApacheCommonsCsvExample {
public static void main(String[] args) {
String csvFile = "data.csv";
try (Reader reader = new FileReader(csvFile);
CSVParser csvParser = new CSVParser(reader, CSVFormat.DEFAULT.withFirstRecordAsHeader())) {
for (CSVRecord record : csvParser) {
String name = record.get("name"); // 使用表头作为键
String age = record.get("age");
System.out.println("Name: " + name + ", Age: " + age);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
c. Java 8+ Stream (简单场景)
- 核心思想:对于最简单的、没有引号、没有转义字符的 CSV 文件,可以直接使用 Java 8 的
StreamAPI 和BufferedReader来逐行处理。 - 优点:
- 零依赖:不需要引入任何第三方库。
- 缺点:
- 功能极其有限:无法处理包含逗号、换行符等复杂情况的 CSV 文件,容易出错。
- 适用场景:处理由程序自己生成、格式绝对简单的 CSV 文件。
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
public class JavaStreamCsvExample {
public static void main(String[] args) {
String csvFile = "simple_data.csv"; // 内容: name,age\nAlice,30\nBob,25
String line;
String csvSplitBy = ",";
try (BufferedReader br = new BufferedReader(new FileReader(csvFile))) {
// 读取并丢弃表头
br.readLine();
while ((line = br.readLine()) != null) {
String[] person = line.split(csvSplitBy);
System.out.println("Name: " + person[0] + ", Age: " + person[1]);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
CSV 解析总结与选择建议
| 特性 | OpenCSV | Apache Commons CSV | Java 8 Stream |
|---|---|---|---|
| 易用性 | 简单 | 优秀 | 极简 |
| 功能 | 强大 | 强大 | 极其有限 |
| 依赖 | 需要 | 需要 | 无 |
| 适用场景 | 通用、复杂 CSV | 通用、追求设计 | 超简单、无依赖 |
- 首选 OpenCSV 或 Apache Commons CSV:对于任何生产环境,都应该使用它们,两者都非常出色,Apache Commons CSV 的 API 设计可能更现代一些。
- 选择 Java Stream:仅在 100% 确认你的 CSV 文件格式极其简单,且无法引入外部依赖时使用。
通用文档解析 (基于 POI)
Apache POI (Poor Obfuscation Implementation) 是一个强大的 Java API,用于操作 Microsoft Office 格式文件,如 Word (.docx), Excel (.xlsx), PowerPoint (.pptx)。
- 核心思想:POI 提供了不同的模型来解析这些基于 ZIP 格式的 OOXML 文档。
- 用户模型:最直观的方式,将整个文档加载到内存中,形成一个对象树,类似于 DOM,易于使用,但内存消耗大。
- SAX (事件) 模型:流式解析,内存效率高,类似于 XML 的 SAX,适用于处理大型 Excel 文件。
- 适用场景:需要读取、创建或修改 Office 文档的应用程序,如报表生成、数据导入导出等。
// 这是一个 Excel (.xlsx) 解析的示例
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import java.io.FileInputStream;
import java.io.IOException;
public class PoiExample {
public static void main(String[] args) {
String excelFilePath = "data.xlsx";
try (FileInputStream fis = new FileInputStream(excelFilePath);
Workbook workbook = new XSSFWorkbook(fis)) {
Sheet sheet = workbook.getSheetAt(0); // 获取第一个工作表
for (Row row : sheet) {
for (Cell cell : row) {
switch (cell.getCellType()) {
case STRING:
System.out.print(cell.getStringCellValue() + "\t");
break;
case NUMERIC:
System.out.print(cell.getNumericCellValue() + "\t");
break;
case BOOLEAN:
System.out.print(cell.getBooleanCellValue() + "\t");
break;
default:
System.out.print("\t");
}
}
System.out.println();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
| 文档类型 | 推荐库 | 核心特点 |
|---|---|---|
| XML | JAXB (对象绑定), StAX (流式) | JAXB 简化开发,StAX 平衡性能与控制 |
| JSON | Jackson (性能之王), Gson (简洁易用) | Jackson 适合企业级,Gson 适合中小型项目 |
| HTML | Jsoup | CSS 选择器 API,极其方便,容错性强 |
| CSV | OpenCSV 或 Apache Commons CSV | 功能强大,能处理复杂格式,比手写健壮 |
| Office | Apache POI | 功能全面,支持 Word, Excel, PowerPoint 等 |
选择哪个库,最终取决于你的具体需求:文件大小、性能要求、开发效率、以及是否需要修改文档,对于大多数现代 Java 应用,Jackson (JSON) 和 Jsoup (HTML) 是最常遇到的组合。
