原文:Mkyong
如何在 Java 中读取文件–file inputstream
在 Java 中,我们使用 FileInputStream 从文件中读取字节,比如图像文件或二进制文件。
主题
- 文件输入流–读取文件
- 文件输入流–剩余字节
- 文件输入流–更好的性能
- 文件输入流 vs 缓冲输入流
- InputStreamReader–将文件输入流转换为阅读器
- FileInputStream – Read a Unicode file
注意
然而,下面所有的例子都使用FileInputStream
从文本文件中读取字节并打印出来。文本文件允许读者正确地“看到”输出。通常,我们使用Reader
从文本文件中读取字符。
1。file inputstream–读取文件
这个例子使用FileInputStream
从文件中读取字节并打印出内容。fis.read()
一次读取一个字节,如果到达文件末尾,它将返回一个-1
。
FileInputStreamExample1.java
package com.mkyong.io.api.inputstream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
public class FileInputStreamExample1 {
public static void main(String[] args) {
readFile("c:\\test\\file.txt");
}
private static void readFile(String fileName) {
try (FileInputStream fis = new FileInputStream(new File(fileName))) {
int content;
// reads a byte at a time, if it reached end of the file, returns -1
while ((content = fis.read()) != -1) {
System.out.println((char)content);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
2。文件输入流–剩余字节
我们可以使用fis.available()
来检查剩余的可以读取的字节。例如:
下面是一个包含 10 个字节的文本文件。
c:\test\file.txt
mkyong.com
FileInputStreamExample2.java
package com.mkyong.io.api.inputstream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
public class FileInputStreamExample2 {
public static void main(String[] args) {
readFile("c:\\test\\file.txt");
}
private static void readFile(String fileName) {
try (FileInputStream fis = new FileInputStream(new File(fileName))) {
// remaining bytes that can be read
System.out.println("Remaining bytes that can be read : " + fis.available());
int content;
// reads a byte at a time, if end of the file, returns -1
while ((content = fis.read()) != -1) {
System.out.println((char) content);
System.out.println("Remaining bytes that can be read : " + fis.available());
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
输出
Terminal
Remaining bytes that can be read : 10
m
Remaining bytes that can be read : 9
k
Remaining bytes that can be read : 8
y
Remaining bytes that can be read : 7
o
Remaining bytes that can be read : 6
n
Remaining bytes that can be read : 5
g
Remaining bytes that can be read : 4
.
Remaining bytes that can be read : 3
c
Remaining bytes that can be read : 2
o
Remaining bytes that can be read : 1
m
Remaining bytes that can be read : 0
对于一个包含 10 个字节的文件,fis.read()
将运行十次,每次读取一个字节。(看到这里的问题?)
3。文件输入流–更好的性能
3.1 查看FileInputStream#read()
源代码,每个read()
都会调用native
方法从磁盘中读取一个字节。
package java.io;
public class FileInputStream extends InputStream {
/**
* Reads a byte of data from this input stream. This method blocks
* if no input is yet available.
*
* @return the next byte of data, or <code>-1</code> if the end of the
* file is reached.
* @exception IOException if an I/O error occurs.
*/
public int read() throws IOException {
return read0();
}
private native int read0() throws IOException;
//...
}
3.2 我们可以使用read(byte b[])
将预定义的字节读入一个字节数组;这将显著提高读取性能。
FileInputStreamExample3.java
package com.mkyong.io.api.inputstream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
public class FileInputStreamExample3 {
public static void main(String[] args) {
readFileBetterPerformance("c:\\test\\file.txt");
}
private static void readFileBetterPerformance(String fileName) {
try (FileInputStream fis = new FileInputStream(new File(fileName))) {
// remaining bytes that can be read
System.out.println("Remaining bytes that can be read : " + fis.available());
// 8k a time
byte[] bytes = new byte[8192];
// reads 8192 bytes at a time, if end of the file, returns -1
while (fis.read(bytes) != -1) {
// convert bytes to string for demo
System.out.println(new String(bytes, StandardCharsets.UTF_8));
System.out.println("Remaining bytes that can be read : " + fis.available());
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
上面的例子会一次读取8192
个字节,对于一个包含 10 个字节的文件,只读取一次。
Terminal
Remaining bytes that can be read : 10
mkyong.com
Remaining bytes that can be read : 0
注意例如
,如果一个文件包含81920
个字节(80 kb),默认fis.read
将需要一个 81920 本地调用来从文件中读取所有字节;而fis.read(bytes)
(对于 8192 的大小),我们只需要 10 个本地调用。差别是巨大的。
4。文件输入流 vs 缓冲输入流
FileInputStream
一次读取一个字节,每个read()
将从磁盘进行本地读取。对于读取大文件,它会变慢。
BufferedInputStream
一次读取8192
个字节(默认),并缓冲它们,直到需要它们为止;BufferedInputStream#read()
仍然一次返回一个字节,但是其他剩余的字节在缓冲区中,为下一次读取保留。概念类似于上面的FileInputStreamExample3.java
通常的做法是使用BufferedInputStream
来包装FileInputStream
以提供缓冲区缓存来提高读取性能。
FileInputStreamExample4.java
package com.mkyong.io.api.inputstream;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
public class FileInputStreamExample4 {
public static void main(String[] args) {
readFileBetterPerformance2("c:\\test\\file.txt");
}
private static void readFileBetterPerformance2(String fileName) {
try (BufferedInputStream bis =
new BufferedInputStream(
new FileInputStream(new File(fileName)))) {
// remaining bytes that can be read
System.out.println("Remaining bytes that can be read : " + bis.available());
int content;
// reads 8192 bytes at a time and buffers them until they are needed,
// if end of the file, returns -1
while ((content = bis.read()) != -1) {
// convert bytes to string for demo
System.out.println((char) content);
System.out.println("Remaining bytes that can be read : " + bis.available());
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
输出
Terminal
Remaining bytes that can be read : 10
m
Remaining bytes that can be read : 9
k
Remaining bytes that can be read : 8
y
Remaining bytes that can be read : 7
o
Remaining bytes that can be read : 6
n
Remaining bytes that can be read : 5
g
Remaining bytes that can be read : 4
.
Remaining bytes that can be read : 3
c
Remaining bytes that can be read : 2
o
Remaining bytes that can be read : 1
m
Remaining bytes that can be read : 0
5。将文件输入流转换为阅读器
使用InputStreamReader
将InputStream
转换为Reader
也很常见。
这个例子展示了如何将一个FileInputStream
转换成BufferedReader
,并逐行读取。
private static void readFileBetterInputStreamReader(String fileName) {
try (BufferedReader br =
new BufferedReader(
new InputStreamReader(
new FileInputStream(new File(fileName))))) {
String line;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}
}
6. FileInputStream – Read a Unicode file
这个例子使用FileInputStream
来读取一个 Unicode 文件。例如:
一种包含几个中文字符的 Unicode 文件,每个 Unicode 编码字符包含两个或多个字节。
c:\test\file-unicode.txt
你好
我好
大家好
我们可以使用上面的例子 3 来读取 Unicode 文件并正确打印它们。
FileInputStreamExample3.java
private static void readFileBetterPerformance(String fileName) {
try (FileInputStream fis = new FileInputStream(new File(fileName))) {
// remaining bytes that can be read
System.out.println("Remaining bytes that can be read : " + fis.available());
// 8k a time
byte[] bytes = new byte[8192];
// reads 8192 bytes at a time, if end of the file, returns -1
while (fis.read(bytes) != -1) {
// convert bytes to string for demo
// convert bytes unicode to string
System.out.println(new String(bytes, StandardCharsets.UTF_8));
System.out.println("Remaining bytes that can be read : " + fis.available());
}
} catch (IOException e) {
e.printStackTrace();
}
}
输出
Terminal
Remaining bytes that can be read : 25
你好
我好
大家好
Remaining bytes that can be read : 0
此外,我们还可以使用示例 5 InputStreamReader 来读取和打印 Unicode 文件,默认情况下InputStreamReader
有一个默认的 UTF-8 字符集。
下载源代码
$ git 克隆https://github.com/mkyong/core-java
$ cd api/inputstream
参考文献
- 文件输入流 JavaDoc
- BufferedInputStream JavaDoc
- 文件输出流–写入文件
- buffered reader–读取文件
- 在 Java 中将 InputStream 转换为 buffered reader
- Java–读取 UTF-8 文件
如何用 Java 读取 UTF 8 文件
在 Java 中, InputStreamReader 接受一个字符集来将字节流解码成字符流。我们可以向InputStreamReader
构造函数传递一个StandardCharsets.UTF_8
来从 UTF-8 文件中读取数据。
import java.nio.charset.StandardCharsets;
//...
try (FileInputStream fis = new FileInputStream(file);
InputStreamReader isr = new InputStreamReader(fis, StandardCharsets.UTF_8);
BufferedReader reader = new BufferedReader(isr)
) {
String str;
while ((str = reader.readLine()) != null) {
System.out.println(str);
}
} catch (IOException e) {
e.printStackTrace();
}
在 Java 7+中,许多文件读取 API 开始接受charset
作为参数,使得读取 UTF-8 变得非常容易。
// Java 7
BufferedReader reader = Files.newBufferedReader(path, StandardCharsets.UTF_8);
// Java 8
List<String> list = Files.readAllLines(path, StandardCharsets.UTF_8);
// Java 8
Stream<String> lines = Files.lines(path, StandardCharsets.UTF_8);
// Java 11
String s = Files.readString(path, StandardCharsets.UTF_8);
1.UTF 八号档案
一个 UTF 8 编码文件c:\\temp\\test.txt
,有中文字符。
2.阅读 UTF-8 文件
这个例子展示了几种读取 UTF 8 文件的方法。
package com.mkyong.io.howto;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.stream.Stream;
public class UnicodeRead {
public static void main(String[] args) {
String fileName = "c:\\temp\\test.txt";
//readUnicodeJava11(fileName);
readUnicodeBufferedReader(fileName);
//readUnicodeFiles(fileName);
//readUnicodeClassic(fileName);
}
// Java 7 - Files.newBufferedReader(path, StandardCharsets.UTF_8)
// Java 8 - Files.newBufferedReader(path) // default UTF-8
public static void readUnicodeBufferedReader(String fileName) {
Path path = Paths.get(fileName);
// Java 8, default UTF-8
try (BufferedReader reader = Files.newBufferedReader(path)) {
String str;
while ((str = reader.readLine()) != null) {
System.out.println(str);
}
} catch (IOException e) {
e.printStackTrace();
}
}
public static void readUnicodeFiles(String fileName) {
Path path = Paths.get(fileName);
try {
// Java 11
String s = Files.readString(path, StandardCharsets.UTF_8);
System.out.println(s);
// Java 8
List<String> list = Files.readAllLines(path, StandardCharsets.UTF_8);
list.forEach(System.out::println);
// Java 8
Stream<String> lines = Files.lines(path, StandardCharsets.UTF_8);
lines.forEach(System.out::println);
} catch (IOException e) {
e.printStackTrace();
}
}
// Java 11, adds charset to FileReader
public static void readUnicodeJava11(String fileName) {
Path path = Paths.get(fileName);
try (FileReader fr = new FileReader(fileName, StandardCharsets.UTF_8);
BufferedReader reader = new BufferedReader(fr)) {
String str;
while ((str = reader.readLine()) != null) {
System.out.println(str);
}
} catch (IOException e) {
e.printStackTrace();
}
}
public static void readUnicodeClassic(String fileName) {
File file = new File(fileName);
try (FileInputStream fis = new FileInputStream(file);
InputStreamReader isr = new InputStreamReader(fis, StandardCharsets.UTF_8);
BufferedReader reader = new BufferedReader(isr)
) {
String str;
while ((str = reader.readLine()) != null) {
System.out.println(str);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
输出
Terminal
line 1
line 2
line 3
你好,世界
下载源代码
$ git 克隆https://github.com/mkyong/core-java
$ cd java-io
参考
如何在 Java 中读取 UTF-8 XML 文件—(SAX 解析器)
本文展示了如何使用 SAX 解析器读取或解析 UTF-8 XML 文件。
目录
1。SAX 解析器解析 UTF-8 XML 文件。
1.1 XML 文件包含 UTF-8 和中文字符。
staff.xml
<?xml version="1.0" encoding="utf-8"?>
<Company>
<staff id="1001">
<name>揚木金</name>
<role>support & code</role>
<salary currency="USD">5000</salary>
<bio><![CDATA[HTML tag <code>testing</code>]]></bio>
</staff>
<staff id="1002">
<name>yflow</name>
<role>admin</role>
<salary currency="EUR">8000</salary>
<bio><![CDATA[a & b]]></bio>
</staff>
</Company>
1.2 下面的例子显式设置了一个UTF-8
编码。
注
对于 SAX 处理程序PrintAllHandlerSax
,参考这篇文章。
ReadXmlSaxParser.java
package com.mkyong.xml.sax;
import com.mkyong.xml.sax.handler.PrintAllHandlerSax;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
public class ReadXmlSaxParser {
private static final String FILENAME = "src/main/resources/staff-unicode.xml";
public static void main(String[] args) {
SAXParserFactory factory = SAXParserFactory.newInstance();
try {
SAXParser saxParser = factory.newSAXParser();
PrintAllHandlerSax handler = new PrintAllHandlerSax();
XMLReader xmlReader = saxParser.getXMLReader();
xmlReader.setContentHandler(handler);
InputSource source = new InputSource(FILENAME);
// explicitly set a encoding
source.setEncoding(StandardCharsets.UTF_8.displayName());
xmlReader.parse(source);
} catch (ParserConfigurationException | SAXException | IOException e) {
e.printStackTrace();
}
}
}
输出
Terminal
Start Document
Start Element : Company
Start Element : staff
Staff id : 1001
Start Element : name
End Element : name
Name : 揚木金
Start Element : role
End Element : role
Role : support & code
Start Element : salary
Currency :USD
End Element : salary
Salary : 5000
Start Element : bio
End Element : bio
Bio : HTML tag <code>testing</code>
End Element : staff
Start Element : staff
Staff id : 1002
Start Element : name
End Element : name
Name : yflow
Start Element : role
End Element : role
Role : admin
Start Element : salary
Currency :EUR
End Element : salary
Salary : 8000
Start Element : bio
End Element : bio
Bio : a & b
End Element : staff
End Element : Company
End Document
2。XML 中的字符编码和代码
确保我们使用正确的编码来解析 XML 文件。
2.1 对于 XML 文件,最好声明encoding
属性。
<?xml version="1.0" encoding="character-encoding-here"?>
<Company>
</Company>
例如,下面是一个 UTF 8 编码的 XML 文件。
<?xml version="1.0" encoding="utf-8"?>
<Company>
</Company>
2.2 对于 SAX 解析器,我们可以通过XMLReader
设置编码。
SAXParserFactory factory = SAXParserFactory.newInstance();
try {
SAXParser saxParser = factory.newSAXParser();
PrintAllHandlerSax handler = new PrintAllHandlerSax();
XMLReader xmlReader = saxParser.getXMLReader();
xmlReader.setContentHandler(handler);
InputSource source = new InputSource(FILENAME);
// utf-8
source.setEncoding(StandardCharsets.UTF_8.displayName());
// utf-16
// source.setEncoding(StandardCharsets.UTF_16.displayName());
// ascii
// source.setEncoding(StandardCharsets.US_ASCII.displayName());
xmlReader.parse(source);
} catch (ParserConfigurationException | SAXException | IOException e) {
e.printStackTrace();
}
3。SAX 常见错误
下面是 SAX XML 解析中的一些常见错误。
3.1 1 字节 UTF-8 序列的无效字节 1
XML 文件包含无效的 UTF-8 字符,读这个。
3.2 序言中不允许出现内容
XML 文件在 XML 声明前包含无效文本或 BOM,读此。
3.3 实体名称必须紧跟在实体引用
中的“&
XML 文件中的&
是无效字符,请用&
替换或用 CDATA 括起来,例如<![CDATA[a & b]]>
。
4。下载源代码
$ git 克隆https://github.com/mkyong/core-java
$ cd java-xml
$ CD src/main/Java/com/mkyong/XML/sax/
5。参考文献
- 维基百科–UTF-8
- UTF-8 编码表和 Unicode 字符
- 阿帕奇·薛西斯
- Java SAX 解析器
- SAX–prolog 中不允许出现内容
- SAX–1 字节 UTF-8 序列的无效字节 1
- 如何在 Java (SAX 解析器)中读取 XML 文件
如何在 Java 中读取 XML 文件—(DOM 解析器)
本教程将向您展示如何使用 Java 内置的 DOM 解析器来读取 XML 文件。
注意
DOM 解析器在读取大型 XML 文档时速度很慢,并且会消耗大量内存,因为它将所有节点加载到内存中进行遍历和操作。
相反,我们应该考虑 SAX 解析器来读取大尺寸的 XML 文档,SAX 比 DOM 更快并且使用更少的内存。
1。什么是文档对象模型
文档对象模型(DOM)使用节点将 HTML 或 XML 文档表示为树结构。
下面是一个简单的 XML 文档:
<company>
<staff id="1001">
<firstname>yong</firstname>
<lastname>mook kim</lastname>
<nickname>mkyong</nickname>
<salary currency="USD">100000</salary>
</staff>
</company>
DOM 常用术语。
<company>
是根元素。<staff>, <firstname> and all <?>
是元素节点。- 文本节点是由元素节点包装的值;比如
<firstname>yong</firstname>
,yong
就是文本节点。 - 该属性是元素节点的一部分;例如,
<staff id="1001">``id
是staff
元素的属性。
延伸阅读
2。读取或解析 XML 文件
这个例子向您展示了如何使用 Java 内置的 DOM 解析器 API 来读取或解析 XML 文件。
2.1 查看下面的 XML 文件。
/users/mkyong/staff.xml
<?xml version="1.0"?>
<company>
<staff id="1001">
<firstname>yong</firstname>
<lastname>mook kim</lastname>
<nickname>mkyong</nickname>
<salary currency="USD">100000</salary>
</staff>
<staff id="2001">
<firstname>low</firstname>
<lastname>yin fong</lastname>
<nickname>fong fong</nickname>
<salary currency="INR">200000</salary>
</staff>
</company>
下面的 2.2 是一个解析或读取上述 XML 文件的 DOM 解析器示例。
ReadXmlDomParser.java
package com.mkyong.xml.dom;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
public class ReadXmlDomParser {
private static final String FILENAME = "/users/mkyong/staff.xml";
public static void main(String[] args) {
// Instantiate the Factory
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
try {
// optional, but recommended
// process XML securely, avoid attacks like XML External Entities (XXE)
dbf.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
// parse XML file
DocumentBuilder db = dbf.newDocumentBuilder();
Document doc = db.parse(new File(FILENAME));
// optional, but recommended
// http://stackoverflow.com/questions/13786607/normalization-in-dom-parsing-with-java-how-does-it-work
doc.getDocumentElement().normalize();
System.out.println("Root Element :" + doc.getDocumentElement().getNodeName());
System.out.println("------");
// get <staff>
NodeList list = doc.getElementsByTagName("staff");
for (int temp = 0; temp < list.getLength(); temp++) {
Node node = list.item(temp);
if (node.getNodeType() == Node.ELEMENT_NODE) {
Element element = (Element) node;
// get staff's attribute
String id = element.getAttribute("id");
// get text
String firstname = element.getElementsByTagName("firstname").item(0).getTextContent();
String lastname = element.getElementsByTagName("lastname").item(0).getTextContent();
String nickname = element.getElementsByTagName("nickname").item(0).getTextContent();
NodeList salaryNodeList = element.getElementsByTagName("salary");
String salary = salaryNodeList.item(0).getTextContent();
// get salary's attribute
String currency = salaryNodeList.item(0).getAttributes().getNamedItem("currency").getTextContent();
System.out.println("Current Element :" + node.getNodeName());
System.out.println("Staff Id : " + id);
System.out.println("First Name : " + firstname);
System.out.println("Last Name : " + lastname);
System.out.println("Nick Name : " + nickname);
System.out.printf("Salary [Currency] : %,.2f [%s]%n%n", Float.parseFloat(salary), currency);
}
}
} catch (ParserConfigurationException | SAXException | IOException e) {
e.printStackTrace();
}
}
}
输出
Terminal
Root Element :company
------
Current Element :staff
Staff Id : 1001
First Name : yong
Last Name : mook kim
Nick Name : mkyong
Salary [Currency] : 100,000.00 [USD]
Current Element :staff
Staff Id : 2001
First Name : low
Last Name : yin fong
Nick Name : fong fong
Salary [Currency] : 200,000.00 [INR]
3。读取或解析 XML 文件(Unicode)
在 DOM parser 中,读取普通 XML 文件和 Unicode XML 文件没有区别。
3.1 查看下面包含一些中文字符(Unicode)的 XML 文件。
src/main/resources/staff-unicode.xml
<?xml version="1.0"?>
<company>
<staff id="1001">
<firstname>揚</firstname>
<lastname>木金</lastname>
<nickname>mkyong</nickname>
<salary currency="USD">100000</salary>
</staff>
<staff id="2001">
<firstname>low</firstname>
<lastname>yin fong</lastname>
<nickname>fong fong</nickname>
<salary currency="INR">200000</salary>
</staff>
</company>
3.2 下面的示例解析上面的 XML 文件;它逐个循环所有的节点并打印出来。
ReadXmlDomParserLoop.java
package com.mkyong.xml.dom;
import org.w3c.dom.*;
import org.xml.sax.SAXException;
import javax.xml.XMLConstants;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.IOException;
import java.io.InputStream;
public class ReadXmlDomParserLoop {
public static void main(String[] args) {
// Instantiate the Factory
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
try (InputStream is = readXmlFileIntoInputStream("staff-unicode.xml")) {
// parse XML file
DocumentBuilder db = dbf.newDocumentBuilder();
// read from a project's resources folder
Document doc = db.parse(is);
System.out.println("Root Element :" + doc.getDocumentElement().getNodeName());
System.out.println("------");
if (doc.hasChildNodes()) {
printNote(doc.getChildNodes());
}
} catch (ParserConfigurationException | SAXException | IOException e) {
e.printStackTrace();
}
}
private static void printNote(NodeList nodeList) {
for (int count = 0; count < nodeList.getLength(); count++) {
Node tempNode = nodeList.item(count);
// make sure it's element node.
if (tempNode.getNodeType() == Node.ELEMENT_NODE) {
// get node name and value
System.out.println("\nNode Name =" + tempNode.getNodeName() + " [OPEN]");
System.out.println("Node Value =" + tempNode.getTextContent());
if (tempNode.hasAttributes()) {
// get attributes names and values
NamedNodeMap nodeMap = tempNode.getAttributes();
for (int i = 0; i < nodeMap.getLength(); i++) {
Node node = nodeMap.item(i);
System.out.println("attr name : " + node.getNodeName());
System.out.println("attr value : " + node.getNodeValue());
}
}
if (tempNode.hasChildNodes()) {
// loop again if has child nodes
printNote(tempNode.getChildNodes());
}
System.out.println("Node Name =" + tempNode.getNodeName() + " [CLOSE]");
}
}
}
// read file from project resource's folder.
private static InputStream readXmlFileIntoInputStream(final String fileName) {
return ReadXmlDomParserLoop.class.getClassLoader().getResourceAsStream(fileName);
}
}
输出
Terminal
Root Element :company
------
Node Name =company [OPEN]
Node Value =
揚
木金
mkyong
100000
low
yin fong
fong fong
200000
Node Name =staff [OPEN]
Node Value =
揚
木金
mkyong
100000
attr name : id
attr value : 1001
Node Name =firstname [OPEN]
Node Value =揚
Node Name =firstname [CLOSE]
Node Name =lastname [OPEN]
Node Value =木金
Node Name =lastname [CLOSE]
Node Name =nickname [OPEN]
Node Value =mkyong
Node Name =nickname [CLOSE]
Node Name =salary [OPEN]
Node Value =100000
attr name : currency
attr value : USD
Node Name =salary [CLOSE]
Node Name =staff [CLOSE]
Node Name =staff [OPEN]
Node Value =
low
yin fong
fong fong
200000
attr name : id
attr value : 2001
Node Name =firstname [OPEN]
Node Value =low
Node Name =firstname [CLOSE]
Node Name =lastname [OPEN]
Node Value =yin fong
Node Name =lastname [CLOSE]
Node Name =nickname [OPEN]
Node Value =fong fong
Node Name =nickname [CLOSE]
Node Name =salary [OPEN]
Node Value =200000
attr name : currency
attr value : INR
Node Name =salary [CLOSE]
Node Name =staff [CLOSE]
Node Name =company [CLOSE]
4。解析 Alexa API XML 响应
这个例子展示了如何使用 DOM 解析器解析来自 Alexa 的 API 的 XML 响应。
4.1 向下面的 Alexa API 发送请求。
Terminal
https://data.alexa.com/data?cli=10&url;=mkyong.com
4.2 Alexa API 将返回以下 XML 响应。Alexa 排名在POPULARITY
元素内,即TEXT
属性。
<!-- Need more Alexa data? Find our APIs here: https://aws.amazon.com/alexa/ -->
<ALEXA VER="0.9" URL="mkyong.com/" HOME="0" AID="=" IDN="mkyong.com/">
<SD>
<POPULARITY URL="mkyong.com/" TEXT="20162" SOURCE="panel"/>
<REACH RANK="14430"/>
<RANK DELTA="+947"/>
<COUNTRY CODE="IN" NAME="India" RANK="4951"/>
</SD>
</ALEXA>
4.3 我们使用一个 DOM 解析器直接选择POPULARITY
元素并打印出TEXT
属性的值。
ReadXmlAlexaApi.java
package com.mkyong.xml.dom;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import javax.xml.XMLConstants;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
public class ReadXmlAlexaApi {
private static final String ALEXA_API = "http://data.alexa.com/data?cli=10&url=";
private final DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
public static void main(String[] args) {
ReadXmlAlexaApi obj = new ReadXmlAlexaApi();
int alexaRanking = obj.getAlexaRanking("mkyong.com");
System.out.println("Ranking: " + alexaRanking);
}
public int getAlexaRanking(String domain) {
int result = 0;
String url = ALEXA_API + domain;
try {
URLConnection conn = new URL(url).openConnection();
try (InputStream is = conn.getInputStream()) {
// unknown XML better turn on this
dbf.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
DocumentBuilder dBuilder = dbf.newDocumentBuilder();
Document doc = dBuilder.parse(is);
Element element = doc.getDocumentElement();
// find this tag "POPULARITY"
NodeList nodeList = element.getElementsByTagName("POPULARITY");
if (nodeList.getLength() > 0) {
Element elementAttribute = (Element) nodeList.item(0);
String ranking = elementAttribute.getAttribute("TEXT");
if (!"".equals(ranking)) {
result = Integer.parseInt(ranking);
}
}
}
} catch (Exception e) {
e.printStackTrace();
throw new IllegalArgumentException("Invalid request for domain : " + domain);
}
return result;
}
}
域名mkyong.com
排名20162
。
Terminal
Ranking: 20162
注意
更多 DOM 解析器示例—Oracle—将 XML 数据读入 DOM
5。下载源代码
$ git 克隆https://github.com/mkyong/core-java
$ cd java-xml
$ CD src/main/Java/com/mkyong/XML/DOM/
6。参考文献
- 维基百科–用于 XML 处理的 Java API
- 维基百科–文档对象模型
- Oracle–用于 XML 处理的 Java API(JAXP)
- Oracle–文档对象模型
- OWASP–XML 外部实体(XXE)处理
- stack overflow——用 java 进行 DOM 解析的规范化——它是如何工作的?
- 如何在 Java 中获得 Alexa 排名
- 如何在 Java 中读取 XML 文件—(SAX 解析器)
- 如何在 Java 中读取 XML 文件—(JDOM 解析器)
如何在 Java 中读取 XML 文件—(JDOM 解析器)
JDOM 是构建在 DOM 和 SAX 之上的基于 Java 的开源 XML 解析器框架,也是 XML 文档的文档对象模型(DOM) 内存表示。
JDOM 通过提供更简单的 API 和标准的基于 Java 的集合接口,使得 XML 文档的导航更加容易。如果您不介意下载一个小库来解析 XML 文件,JDOM APIs 很容易使用。
1。下载 JDOM 库
JDOM 不是 Java 内置 API 的一部分,我们需要下载 JDOM 库。
Maven for JDOM。
pom.xml
<dependency>
<groupId>org.jdom</groupId>
<artifactId>jdom2</artifactId>
<version>2.0.6</version>
</dependency>
2。读取或解析 XML 文件(JDOM)
这个例子展示了如何使用 JDOM 来解析 XML 文件。
2.1 一个 XML 文件。
src/main/resources/staff.xml
<?xml version="1.0" encoding="utf-8"?>
<Company>
<staff id="1001">
<name>mkyong</name>
<role>support</role>
<salary currency="USD">5000</salary>
<!-- for special characters like < &, need CDATA -->
<bio><![CDATA[HTML tag <code>testing</code>]]></bio>
</staff>
<staff id="1002">
<name>yflow</name>
<role>admin</role>
<salary currency="EUR">8000</salary>
<bio><![CDATA[a & b]]></bio>
</staff>
</Company>
2.2 解析并打印出所有 XML 元素和节点的 JDOM 示例。
ReadXmlJDomParser.java
package com.mkyong.xml.jdom;
import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.JDOMException;
import org.jdom2.input.SAXBuilder;
import java.io.File;
import java.io.IOException;
import java.util.List;
public class ReadXmlJDomParser {
private static final String FILENAME = "src/main/resources/staff.xml";
public static void main(String[] args) {
try {
SAXBuilder sax = new SAXBuilder();
// XML is a local file
Document doc = sax.build(new File(FILENAME));
Element rootNode = doc.getRootElement();
List<Element> list = rootNode.getChildren("staff");
for (Element target : list) {
String id = target.getAttributeValue("id");
String name = target.getChildText("name");
String role = target.getChildText("role");
String salary = target.getChildText("salary");
String currency = "";
if (salary != null && salary.length() > 1) {
// access attribute
currency = target.getChild("salary").getAttributeValue("currency");
}
String bio = target.getChildText("bio");
System.out.printf("Staff id : %s%n", id);
System.out.printf("Name : %s%n", name);
System.out.printf("Role : %s%n", role);
System.out.printf("Salary [Currency] : %,.2f [%s]%n", Float.parseFloat(salary), currency);
System.out.printf("Bio : %s%n%n", bio);
}
} catch (IOException | JDOMException e) {
e.printStackTrace();
}
}
}
输出
Terminal
Staff id : 1001
Name : mkyong
Role : support
Salary [Currency] : 5,000.00 [USD]
Bio : HTML tag <code>testing</code>
Staff id : 1002
Name : yflow
Role : admin
Salary [Currency] : 8,000.00 [EUR]
Bio : a & b
3。读取或解析远程 XML 文件(JDOM)
这个示例使用 JDOM 解析器来读取或解析远程或基于网站的 XML 文件。
3.1 访问 Alexa API https://data.alexa.com/data?cli=10&url=mkyong.com
,它会返回下面的 XML 文件。
<ALEXA VER="0.9" URL="mkyong.com/" HOME="0" AID="=" IDN="mkyong.com/">
<SD>
<POPULARITY URL="mkyong.com/" TEXT="20152" SOURCE="panel"/>
<REACH RANK="14434"/>
<RANK DELTA="+728"/>
<COUNTRY CODE="IN" NAME="India" RANK="5322"/>
</SD>
</ALEXA>
3.2 JDOM 获取网站 Alexa 排名的例子。
ReadXmlAlexaApiJDomParser.java
package com.mkyong.xml.jdom;
import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.JDOMException;
import org.jdom2.input.SAXBuilder;
import java.io.IOException;
import java.net.URL;
import java.util.List;
public class ReadXmlAlexaApiJDomParser {
private static final String REMOTE_URL
= "https://data.alexa.com/data?cli=10&url=mkyong.com";
public static void main(String[] args) {
try {
SAXBuilder sax = new SAXBuilder();
// XML is in a web-based location
Document doc = sax.build(new URL(REMOTE_URL));
Element rootNode = doc.getRootElement();
List<Element> list = rootNode.getChildren("SD");
for (Element target : list) {
Element popularity = target.getChild("POPULARITY");
String url = popularity.getAttributeValue("URL");
String rank = popularity.getAttributeValue("TEXT");
System.out.printf("URL : %s%n", url);
System.out.printf("Alexa Rank : %s%n", rank);
}
} catch (IOException | JDOMException e) {
e.printStackTrace();
}
}
}
输出
Terminal
URL : mkyong.com/
Alexa Rank : 20152
4。包含 XML 的字符串
这个例子展示了 JDOM 如何解析包含 XML 的String
。
ReadXmlStringJDomParser.java
package com.mkyong.xml.jdom;
import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.JDOMException;
import org.jdom2.input.SAXBuilder;
import java.io.IOException;
import java.io.StringReader;
public class ReadXmlStringJDomParser {
public static void main(String[] args) {
String XML = "<root><url>mkyong</url></root>";
try {
SAXBuilder sax = new SAXBuilder();
// String that contains XML
Document doc = sax.build(new StringReader(XML));
Element rootNode = doc.getRootElement();
System.out.println(rootNode.getChildText("url"));
} catch (IOException | JDOMException e) {
e.printStackTrace();
}
}
}
输出
Terminal
mkyong
注
更多 JDOM2 示例——JDOM 2 入门
5。下载源代码
$ git 克隆https://github.com/mkyong/core-java
$ cd java-xml
$ CD src/main/Java/com/mkyong/XML/JDOM/
6。参考文献
- JDOM 网站
- JDOM2 文档
- 维基百科–JDOM
- 维基百科–用于 XML 处理的 Java API
- Oracle–用于 XML 处理的 Java API(JAXP)
- 如何在 Java 中读取 XML 文件—(DOM 解析器)
- 如何在 Java 中读取 XML 文件—(SAX 解析器)
- JAXB hello world 示例
如何在 Java 中读取 XML 文件(SAX 解析器)
本教程将向您展示如何使用 Java 内置的 SAX 解析器来读取和解析 XML 文件。
1。什么是 XML 的简单 API(SAX)
1.1XML 的简单 API(SAX)是一个推送 API,一个观察者模式,事件驱动,顺序地串行访问 XML 文件元素。这个 SAX 解析器从头到尾读取 XML 文件,在遇到一个元素时调用一个方法,或者在找到特定的文本或属性时调用不同的方法。
SAX 快速高效,比 DOM 需要更少的内存,因为 SAX 不像 DOM 那样创建 XML 数据的内部表示(树结构)。
注意
SAX 解析器比 DOM 解析器更快,使用的内存更少。SAX 适合顺序读取 XML 元素;DOM 适合于 XML 操作,比如创建、修改或删除 XML 元素。
1.2 一些常见的 SAX 事件:
startDocument()
和endDocument()
–在 XML 文档的开头和结尾调用的方法。startElement()
和endElement()
–在 XML 元素的开头和结尾调用的方法。characters()
–用 XML 元素开始和结束之间的文本内容调用的方法。
1.3 下面是一个简单的 XML 文件。
<name>mkyong</name>
SAX 解析器读取上面的 XML 文件,并依次调用以下事件或方法:
startDocument()
startElement()
–<name>
characters()
–mkyong
endElement()
–</name>
endDocument()
2。读取或解析 XML 文件(SAX)
这个例子向您展示了如何使用 Java 内置的 SAX 解析器 API 来读取或解析 XML 文件。
下面是一个 XML 文件。
src/main/resources/staff.xml
<?xml version="1.0" encoding="utf-8"?>
<Company>
<staff id="1001">
<name>mkyong</name>
<role>support</role>
<salary currency="USD">5000</salary>
<!-- for special characters like < &, need CDATA -->
<bio><![CDATA[HTML tag <code>testing</code>]]></bio>
</staff>
<staff id="1002">
<name>yflow</name>
<role>admin</role>
<salary currency="EUR">8000</salary>
<bio><![CDATA[a & b]]></bio>
</staff>
</Company>
P.S 在 XML 文件中,对于像<
或者&
这样的特殊字符,我们需要用CDATA
将其包裹起来。
2.2 创建一个类来扩展org.xml.sax.helpers.DefaultHandler
,并覆盖startElement
、endElement
和characters
方法来打印所有的 XML 元素、属性、注释和文本。
PrintAllHandlerSax.java
package com.mkyong.xml.sax.handler;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
public class PrintAllHandlerSax extends DefaultHandler {
private StringBuilder currentValue = new StringBuilder();
@Override
public void startDocument() {
System.out.println("Start Document");
}
@Override
public void endDocument() {
System.out.println("End Document");
}
@Override
public void startElement(
String uri,
String localName,
String qName,
Attributes attributes) {
// reset the tag value
currentValue.setLength(0);
System.out.printf("Start Element : %s%n", qName);
if (qName.equalsIgnoreCase("staff")) {
// get tag's attribute by name
String id = attributes.getValue("id");
System.out.printf("Staff id : %s%n", id);
}
if (qName.equalsIgnoreCase("salary")) {
// get tag's attribute by index, 0 = first attribute
String currency = attributes.getValue(0);
System.out.printf("Currency :%s%n", currency);
}
}
@Override
public void endElement(String uri,
String localName,
String qName) {
System.out.printf("End Element : %s%n", qName);
if (qName.equalsIgnoreCase("name")) {
System.out.printf("Name : %s%n", currentValue.toString());
}
if (qName.equalsIgnoreCase("role")) {
System.out.printf("Role : %s%n", currentValue.toString());
}
if (qName.equalsIgnoreCase("salary")) {
System.out.printf("Salary : %s%n", currentValue.toString());
}
if (qName.equalsIgnoreCase("bio")) {
System.out.printf("Bio : %s%n", currentValue.toString());
}
}
// http://www.saxproject.org/apidoc/org/xml/sax/ContentHandler.html#characters%28char%5B%5D,%20int,%20int%29
// SAX parsers may return all contiguous character data in a single chunk,
// or they may split it into several chunks
@Override
public void characters(char ch[], int start, int length) {
// The characters() method can be called multiple times for a single text node.
// Some values may missing if assign to a new string
// avoid doing this
// value = new String(ch, start, length);
// better append it, works for single or multiple calls
currentValue.append(ch, start, length);
}
}
2.3 SAXParser
解析 XML 文件。
ReadXmlSaxParser.java
package com.mkyong.xml.sax;
import com.mkyong.xml.sax.handler.PrintAllHandlerSax;
import org.xml.sax.SAXException;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import java.io.IOException;
public class ReadXmlSaxParser {
private static final String FILENAME = "src/main/resources/staff.xml";
public static void main(String[] args) {
SAXParserFactory factory = SAXParserFactory.newInstance();
try {
SAXParser saxParser = factory.newSAXParser();
PrintAllHandlerSax handler = new PrintAllHandlerSax();
saxParser.parse(FILENAME, handler);
} catch (ParserConfigurationException | SAXException | IOException e) {
e.printStackTrace();
}
}
}
输出
Terminal
Start Document
Start Element : Company
Start Element : staff
Staff id : 1001
Start Element : name
End Element : name
Name : mkyong
Start Element : role
End Element : role
Role : support
Start Element : salary
Currency :USD
End Element : salary
Salary : 5000
Start Element : bio
End Element : bio
Bio : HTML tag <code>testing</code>
End Element : staff
Start Element : staff
Staff id : 1002
Start Element : name
End Element : name
Name : yflow
Start Element : role
End Element : role
Role : admin
Start Element : salary
Currency :EUR
End Element : salary
Salary : 8000
Start Element : bio
End Element : bio
Bio : a & b
End Element : staff
End Element : Company
End Document
3。将 XML 文件转换成对象
这个例子解析一个 XML 文件并把它转换成一个对象的List
。它有效,但不推荐,试试 JAXB
3.1 检查同一个 XML 文件。
src/main/resources/staff.xml
<?xml version="1.0" encoding="utf-8"?>
<Company>
<staff id="1001">
<name>mkyong</name>
<role>support</role>
<salary currency="USD">5000</salary>
<!-- for special characters like < &, need CDATA -->
<bio><![CDATA[HTML tag <code>testing</code>]]></bio>
</staff>
<staff id="1002">
<name>yflow</name>
<role>admin</role>
<salary currency="EUR">8000</salary>
<bio><![CDATA[a & b]]></bio>
</staff>
</Company>
3.2 我们想将上面的 XML 文件转换成下面的Staff
对象。
Staff.java
package com.mkyong.xml.sax.model;
import java.math.BigDecimal;
public class Staff {
private Long id;
private String name;
private String role;
private BigDecimal salary;
private String Currency;
private String bio;
//... getters, setters...toString
}
3.3 下面的类将完成 XML 到对象的转换。
MapStaffObjectHandlerSax.java
package com.mkyong.xml.sax.handler;
import com.mkyong.xml.sax.model.Staff;
import org.xml.sax.Attributes;
import org.xml.sax.helpers.DefaultHandler;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
public class MapStaffObjectHandlerSax extends DefaultHandler {
private StringBuilder currentValue = new StringBuilder();
List<Staff> result;
Staff currentStaff;
public List<Staff> getResult() {
return result;
}
@Override
public void startDocument() {
result = new ArrayList<>();
}
@Override
public void startElement(
String uri,
String localName,
String qName,
Attributes attributes) {
// reset the tag value
currentValue.setLength(0);
// start of loop
if (qName.equalsIgnoreCase("staff")) {
// new staff
currentStaff = new Staff();
// staff id
String id = attributes.getValue("id");
currentStaff.setId(Long.valueOf(id));
}
if (qName.equalsIgnoreCase("salary")) {
// salary currency
String currency = attributes.getValue("currency");
currentStaff.setCurrency(currency);
}
}
public void endElement(String uri,
String localName,
String qName) {
if (qName.equalsIgnoreCase("name")) {
currentStaff.setName(currentValue.toString());
}
if (qName.equalsIgnoreCase("role")) {
currentStaff.setRole(currentValue.toString());
}
if (qName.equalsIgnoreCase("salary")) {
currentStaff.setSalary(new BigDecimal(currentValue.toString()));
}
if (qName.equalsIgnoreCase("bio")) {
currentStaff.setBio(currentValue.toString());
}
// end of loop
if (qName.equalsIgnoreCase("staff")) {
result.add(currentStaff);
}
}
public void characters(char ch[], int start, int length) {
currentValue.append(ch, start, length);
}
}
3.4 运行它。
ReadXmlSaxParser2.java
package com.mkyong.xml.sax;
import com.mkyong.xml.sax.handler.MapStaffObjectHandlerSax;
import com.mkyong.xml.sax.model.Staff;
import org.xml.sax.SAXException;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
public class ReadXmlSaxParser2 {
public static void main(String[] args) {
SAXParserFactory factory = SAXParserFactory.newInstance();
try (InputStream is = getXMLFileAsStream()) {
SAXParser saxParser = factory.newSAXParser();
// parse XML and map to object, it works, but not recommend, try JAXB
MapStaffObjectHandlerSax handler = new MapStaffObjectHandlerSax();
saxParser.parse(is, handler);
// print all
List<Staff> result = handler.getResult();
result.forEach(System.out::println);
} catch (ParserConfigurationException | SAXException | IOException e) {
e.printStackTrace();
}
}
// get XML file from resources folder.
private static InputStream getXMLFileAsStream() {
return ReadXmlSaxParser2.class.getClassLoader().getResourceAsStream("staff.xml");
}
}
输出
Terminal
Staff{id=1001, name='揚木金', role='support', salary=5000, Currency='USD', bio='HTML tag <code>testing</code>'}
Staff{id=1002, name='yflow', role='admin', salary=8000, Currency='EUR', bio='a & b'}
4。SAX 错误处理程序
这个例子展示了如何为 SAX 解析器注册一个定制的错误处理程序。
4.1 创建一个类并扩展org.xml.sax.ErrorHandler
。阅读代码进行自我解释。它只是包装了原始错误消息。
CustomErrorHandlerSax.java
package com.mkyong.xml.sax.handler;
import org.xml.sax.ErrorHandler;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import java.io.PrintStream;
public class CustomErrorHandlerSax implements ErrorHandler {
private PrintStream out;
public CustomErrorHandlerSax(PrintStream out) {
this.out = out;
}
private String getParseExceptionInfo(SAXParseException spe) {
String systemId = spe.getSystemId();
if (systemId == null) {
systemId = "null";
}
String info = "URI=" + systemId + " Line="
+ spe.getLineNumber() + ": " + spe.getMessage();
return info;
}
public void warning(SAXParseException spe) throws SAXException {
out.println("Warning: " + getParseExceptionInfo(spe));
}
public void error(SAXParseException spe) throws SAXException {
String message = "Error: " + getParseExceptionInfo(spe);
throw new SAXException(message);
}
public void fatalError(SAXParseException spe) throws SAXException {
String message = "Fatal Error: " + getParseExceptionInfo(spe);
throw new SAXException(message);
}
}
4.2 我们使用saxParser.getXMLReader()
来获得org.xml.sax.XMLReader
,它提供了更多配置 SAX 解析器的选项。
ReadXmlSaxParser3.java
package com.mkyong.xml.sax;
import com.mkyong.xml.sax.handler.CustomErrorHandlerSax;
import com.mkyong.xml.sax.handler.MapStaffObjectHandlerSax;
import com.mkyong.xml.sax.model.Staff;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
public class ReadXmlSaxParser3 {
public static void main(String[] args) {
SAXParserFactory factory = SAXParserFactory.newInstance();
try (InputStream is = getXMLFileAsStream()) {
SAXParser saxParser = factory.newSAXParser();
// parse XML and map to object, it works, but not recommend, try JAXB
MapStaffObjectHandlerSax handler = new MapStaffObjectHandlerSax();
// try XMLReader
//saxParser.parse(is, handler);
// more options for configuration
XMLReader xmlReader = saxParser.getXMLReader();
// set our custom error handler
xmlReader.setErrorHandler(new CustomErrorHandlerSax(System.err));
xmlReader.setContentHandler(handler);
InputSource source = new InputSource(is);
xmlReader.parse(source);
// print all
List<Staff> result = handler.getResult();
result.forEach(System.out::println);
} catch (ParserConfigurationException | SAXException | IOException e) {
e.printStackTrace();
}
}
// get XML file from resources folder.
private static InputStream getXMLFileAsStream() {
return ReadXmlSaxParser2.class.getClassLoader().getResourceAsStream("staff.xml");
}
}
4.3 更新staff.xml
,去掉bio
元素中的CDATA
,放一个&
,SAX 解析器会报错。
src/main/resources/staff.xml
<?xml version="1.0" encoding="utf-8"?>
<Company>
<staff id="1001">
<name>mkyong</name>
<role>support</role>
<salary currency="USD">5000</salary>
<!-- for special characters like < &, need CDATA -->
<bio>&</bio>
</staff>
</Company>
4.4 用上面的自定义错误处理程序运行它。
xmlReader.setErrorHandler(new CustomErrorHandlerSax(System.err));
输出
Terminal
org.xml.sax.SAXException: Fatal Error: URI=null Line=8: The entity name must immediately follow the '&' in the entity reference.
at com.mkyong.xml.sax.handler.CustomErrorHandlerSax.fatalError(CustomErrorHandlerSax.java:41)
at java.xml/com.sun.org.apache.xerces.internal.util.ErrorHandlerWrapper.fatalError(ErrorHandlerWrapper.java:181)
at java.xml/com.sun.org.apache.xerces.internal.impl.XMLErrorReporter.reportError(XMLErrorReporter.java:400)
at java.xml/com.sun.org.apache.xerces.internal.impl.XMLErrorReporter.reportError(XMLErrorReporter.java:327)
at java.xml/com.sun.org.apache.xerces.internal.impl.XMLScanner.reportFatalError(XMLScanner.java:1471)
//...
4.5 在没有自定义错误处理程序的情况下运行它。
// xmlReader.setErrorHandler(new CustomErrorHandlerSax(System.err));
输出
Terminal
[Fatal Error] :8:15: The entity name must immediately follow the '&' in the entity reference.
org.xml.sax.SAXParseException; lineNumber: 8; columnNumber: 15; The entity name must immediately follow the '&' in the entity reference.
at java.xml/com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.parse(AbstractSAXParser.java:1243)
at java.xml/com.sun.org.apache.xerces.internal.jaxp.SAXParserImpl$JAXPSAXParser.parse(SAXParserImpl.java:635)
at com.mkyong.xml.sax.ReadXmlSaxParser2.main(ReadXmlSaxParser2.java:44)
5。SAX 和 Unicode
对于包含 Unicode 字符的 XML 文件,默认情况下,SAX 可以遵循 XML 编码(默认的 UTF-8)并正确解析内容。
5.1 我们可以在 XML 文件的顶部定义编码,encoding="encoding-code"
;例如,下面是一个使用UTF-8
编码的 XML 文件。
<?xml version="1.0" encoding="utf-8"?>
<Company>
<staff id="1001">
<name>揚木金</name>
<role>support</role>
<salary currency="USD">5000</salary>
<bio><![CDATA[HTML tag <code>testing</code>]]></bio>
</staff>
<staff id="1002">
<name>yflow</name>
<role>admin</role>
<salary currency="EUR">8000</salary>
<bio><![CDATA[a & b]]></bio>
</staff>
</Company>
5.2 或者,我们可以在InputSource
中定义一个指定的编码。
XMLReader xmlReader = saxParser.getXMLReader();
xmlReader.setContentHandler(handler);
InputSource source = new InputSource(is);
// set encoding
source.setEncoding(StandardCharsets.UTF_8.toString());
//source.setEncoding(StandardCharsets.UTF_16.toString());
xmlReader.parse(source);
注意
更多 SAX 解析器示例—Oracle—简单 XML API(SAX)
6。下载源代码
$ git 克隆https://github.com/mkyong/core-java
$ cd java-xml
$ CD src/main/Java/com/mkyong/XML/sax/
7。参考文献
- 维基百科–用于 XML 处理的 Java API
- 维基百科–XML 的简单 API
- 维基百科–文档对象模型
- 维基百科–观察者模式
- Oracle–用于 XML 处理的 Java API(JAXP)
- Oracle–XML 简单应用编程接口(SAX)
- Oracle–文档对象模型(DOM)
- 如何在 Java 中读取 XML 文件—(DOM 解析器)
- 如何在 Java 中读取 XML 文件—(JDOM 解析器)
- JAXB hello world 示例
如何在 Java 中读取 XML 文件(StAX 解析器)
本教程展示了如何使用 Streaming API for XML (StAX) 解析器来读取或解析 XML 文档。
目录
- 1。StAX 是什么
- 2。StAX 游标 API 和迭代器 API
- 3。一个 XML 文件
- 4。StAX 游标 API 读取 XML 文件
- 5。StAX 迭代器 API 读取 XML 文件
- 6。将 XML 转换成 Java 对象?
- 7 .。下载源代码
- 8。参考文献
注
P . S XML 的流 API(StAX)API 从 Java 1.6 开始提供,内置 JDK XML 库。
下面所有的例子都是用 Java 11 测试的。
1。StAX
是什么
StAX 代表Streaming API for XML (StAX)
,一个处理 XML 文档的pull API
。
有两种处理 XML 文档的编程模型,流式和文档对象模型(DOM)。对于 DOM 模型,我们可以使用 DOM 解析器;对于流式模型,我们可以使用 SAX 解析器或者 StAX 解析器。
1.1 SAX 和 StAX 的区别?
Simple API for XML (SAX)
是一个push API
;这个 SAX 解析器不断地向客户机发送(推送)XML 数据。在 SAX 中,客户机无法控制何时接收 XML 数据。
例如,我们注册了一个定制的DefaultHandler
实现来处理 SAX 解析器发送的 XML 数据。阅读完整的 SAX 示例。
// SAX
SAXParser saxParser = factory.newSAXParser();
// DefaultHandler implementation
PrintAllHandlerSax handler = new PrintAllHandlerSax();
saxParser.parse(FILENAME, handler);
Streaming API for XML (StAX)
是一个pull API
;客户机调用 StAX 解析器库的方法,手动一个接一个地获取(拉取)XML 数据。在 StAX 中,控制何时获取(拉取)XML 数据的客户机。
// StAX Iterator API examples
// next event
XMLEvent event = xmlEventReader.nextEvent();
// moves to next event
event = xmlEventReader.nextEvent();
// moves to next event
event = xmlEventReader.nextEvent();
延伸阅读
2。StAX 游标 API 和迭代器 API
StAX 包含两个 API 集:一个游标 API 和一个迭代器 API。
2.1 StAX 游标 API
StAX Cursor API 包含两个主要接口XMLStreamReader
和XMLStreamWriter
。XMLStreamReader.getEventType()
将返回一个int
,我们需要手动映射事件类型。
// StAX Cursor API
XMLStreamReader reader = xmlInputFactory.createXMLStreamReader(
new FileInputStream(path.toFile()));
// this is int! we need to map the eventType manually
int eventType = reader.getEventType();
while (reader.hasNext()) {
eventType = reader.next();
if (eventType == XMLEvent.START_ELEMENT) {
}
//...
}
2.2 StAX 迭代器 API
StAX 迭代器 API 包含两个主要接口XMLEventReader
和XMLEventWriter
,我们使用 XMLEvent 。
// StAX Iterator API
XMLEventReader reader = xmlInputFactory.createXMLEventReader(
new FileInputStream(path.toFile()));
// event iterator
while (reader.hasNext()) {
XMLEvent event = reader.nextEvent();
if (event.isStartElement()) {
}
//...
}
2.3 哪一个?游标还是迭代器 API?
- 与迭代器 API 相比,游标 API 的代码更小,效率更高,性能也更好。适合高性能应用程序或移动应用程序。
- 迭代器 API 提供了 XML 事件,这种事件更加灵活、可扩展、易于编码,适合企业应用程序。
延伸阅读
3。一个 XML 文件
下面是一个 XML 文档,稍后我们使用 StAX 解析器读取 XML 数据并打印出来。
src/main/resources/staff.xml
<?xml version="1.0" encoding="utf-8"?>
<Company>
<staff id="1001">
<name>mkyong</name>
<role>support</role>
<salary currency="USD">5000</salary>
<!-- for special characters like < &, need CDATA -->
<bio><![CDATA[HTML tag <code>testing</code>]]></bio>
</staff>
<staff id="1002">
<name>yflow</name>
<role>admin</role>
<salary currency="EUR">8000</salary>
<bio><![CDATA[a & b]]></bio>
</staff>
</Company>
4。StAX 游标 API 读取 XML 文件
以下示例使用 StAX Cursor API 读取或解析上述 XML 文件,以获取 XML 元素、属性、CDATA 等。
ReadXmlStAXCursorParser.java
package com.mkyong.xml.stax;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import javax.xml.stream.events.XMLEvent;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.nio.file.Path;
import java.nio.file.Paths;
public class ReadXmlStAXCursorParser {
private static final String FILENAME = "src/main/resources/staff.xml";
public static void main(String[] args) {
try {
printXmlByXmlCursorReader(Paths.get(FILENAME));
} catch (FileNotFoundException | XMLStreamException e) {
e.printStackTrace();
}
}
private static void printXmlByXmlCursorReader(Path path)
throws FileNotFoundException, XMLStreamException {
XMLInputFactory xmlInputFactory = XMLInputFactory.newInstance();
XMLStreamReader reader = xmlInputFactory.createXMLStreamReader(
new FileInputStream(path.toFile()));
int eventType = reader.getEventType();
System.out.println(eventType); // 7, START_DOCUMENT
System.out.println(reader); // xerces
while (reader.hasNext()) {
eventType = reader.next();
if (eventType == XMLEvent.START_ELEMENT) {
switch (reader.getName().getLocalPart()) {
case "staff":
String id = reader.getAttributeValue(null, "id");
System.out.printf("Staff id : %s%n", id);
break;
case "name":
eventType = reader.next();
if (eventType == XMLEvent.CHARACTERS) {
System.out.printf("Name : %s%n", reader.getText());
}
break;
case "role":
eventType = reader.next();
if (eventType == XMLEvent.CHARACTERS) {
System.out.printf("Role : %s%n", reader.getText());
}
break;
case "salary":
String currency = reader.getAttributeValue(null, "currency");
eventType = reader.next();
if (eventType == XMLEvent.CHARACTERS) {
String salary = reader.getText();
System.out.printf("Salary [Currency] : %,.2f [%s]%n",
Float.parseFloat(salary), currency);
}
break;
case "bio":
eventType = reader.next();
if (eventType == XMLEvent.CHARACTERS) {
System.out.printf("Bio : %s%n", reader.getText());
}
break;
}
}
if (eventType == XMLEvent.END_ELEMENT) {
// if </staff>
if (reader.getName().getLocalPart().equals("staff")) {
System.out.printf("%n%s%n%n", "---");
}
}
}
}
}
输出
Terminal
7
com.sun.org.apache.xerces.internal.impl.XMLStreamReaderImpl@18be83e4
Staff id : 1001
Name : mkyong
Role : support
Salary [Currency] : 5,000.00 [USD]
Bio : HTML tag <code>testing</code>
---
Staff id : 1002
Name : yflow
Role : admin
Salary [Currency] : 8,000.00 [EUR]
Bio : a & b
---
下面是光标 API 事件类型及其int
的代码助手
5。StAX 迭代器 API 读取 XML 文件
下面的例子使用 StAX 迭代器 API 来读取或解析上面的 XML 文件,以获得 XML 元素、属性、CDATA 等。
ReadXmlStAXEventParser.java
package com.mkyong.xml.stax;
import javax.xml.namespace.QName;
import javax.xml.stream.XMLEventReader;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.events.Attribute;
import javax.xml.stream.events.EndElement;
import javax.xml.stream.events.StartElement;
import javax.xml.stream.events.XMLEvent;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.nio.file.Path;
import java.nio.file.Paths;
public class ReadXmlStAXEventParser {
private static final String FILENAME = "src/main/resources/staff.xml";
public static void main(String[] args) {
try {
printXmlByXmlEventReader(Paths.get(FILENAME));
} catch (FileNotFoundException | XMLStreamException e) {
e.printStackTrace();
}
}
private static void printXmlByXmlEventReader(Path path)
throws FileNotFoundException, XMLStreamException {
XMLInputFactory xmlInputFactory = XMLInputFactory.newInstance();
XMLEventReader reader = xmlInputFactory.createXMLEventReader(
new FileInputStream(path.toFile()));
// event iterator
while (reader.hasNext()) {
XMLEvent event = reader.nextEvent();
if (event.isStartElement()) {
StartElement element = event.asStartElement();
switch (element.getName().getLocalPart()) {
// if <staff>
case "staff":
// id='1001'
Attribute id = element.getAttributeByName(new QName("id"));
System.out.printf("Staff id : %s%n", id.getValue());
break;
case "name":
// throws StartElementEvent cannot be cast to class javax.xml.stream.events.Characters
// element.asCharacters().getData()
// this is still '<name>' tag, need move to next event for the character data
event = reader.nextEvent();
if (event.isCharacters()) {
System.out.printf("Name : %s%n", event.asCharacters().getData());
}
break;
case "role":
event = reader.nextEvent();
if (event.isCharacters()) {
System.out.printf("Role : %s%n", event.asCharacters().getData());
}
break;
case "salary":
// currency='USD'
Attribute currency = element.getAttributeByName(new QName("currency"));
event = reader.nextEvent();
if (event.isCharacters()) {
String salary = event.asCharacters().getData();
System.out.printf("Salary [Currency] : %,.2f [%s]%n",
Float.parseFloat(salary), currency);
}
break;
case "bio":
event = reader.nextEvent();
if (event.isCharacters()) {
// CDATA, no problem.
System.out.printf("Bio : %s%n", event.asCharacters().getData());
}
break;
}
}
if (event.isEndElement()) {
EndElement endElement = event.asEndElement();
// if </staff>
if (endElement.getName().getLocalPart().equals("staff")) {
System.out.printf("%n%s%n%n", "---");
}
}
}
}
}
输出
Terminal
Staff id : 1001
Name : mkyong
Role : support
Salary [Currency] : 5,000.00 [currency='USD']
Bio : HTML tag <code>testing</code>
---
Staff id : 1002
Name : yflow
Role : admin
Salary [Currency] : 8,000.00 [currency='EUR']
Bio : a & b
---
6。将 XML 转换成 Java 对象?
是的,我们可以使用 StAX API 将 XML 转换成 Java 对象。对于上面的例子,我们已经可以获得 XML 数据,创建一个类似于Staff.java
的 POJO 并手动设置值。
Jakarta XML Binding (JAXB) 是一个推荐的库,用于将 XML 与 Java 对象相互转换。
7。下载源代码
$ git 克隆https://github.com/mkyong/core-java
$ cd java-xml
$ CD src/main/Java/com/mkyong/XML/StAX/