Mkyong 中文博客翻译(十九)

原文:Mkyong

协议:CC BY-NC-SA 4.0

如何在 Java 中读取文件–file inputstream

原文:http://web.archive.org/web/20230101150211/https://mkyong.com/java/how-to-read-file-in-java-fileinputstream/

在 Java 中,我们使用 FileInputStream 从文件中读取字节,比如图像文件或二进制文件。

主题

  1. 文件输入流–读取文件
  2. 文件输入流–剩余字节
  3. 文件输入流–更好的性能
  4. 文件输入流 vs 缓冲输入流
  5. InputStreamReader–将文件输入流转换为阅读器
  6. 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。将文件输入流转换为阅读器

使用InputStreamReaderInputStream转换为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

参考文献

如何用 Java 读取 UTF 8 文件

原文:http://web.archive.org/web/20230101150211/https://mkyong.com/java/how-to-read-utf-8-encoded-data-from-a-file-java/

在 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
你好,世界 

延伸阅读
如何用 Java 写一个 UTF-8 文件

下载源代码

$ git 克隆https://github.com/mkyong/core-java

$ cd java-io

参考

如何在 Java 中读取 UTF-8 XML 文件—(SAX 解析器)

原文:http://web.archive.org/web/20230101150211/https://mkyong.com/java/how-to-read-utf-8-xml-file-in-java-sax-parser/

本文展示了如何使用 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 &amp; 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 文件中的&是无效字符,请用&amp;替换或用 CDATA 括起来,例如<![CDATA[a & b]]>

4。下载源代码

$ git 克隆https://github.com/mkyong/core-java

$ cd java-xml

$ CD src/main/Java/com/mkyong/XML/sax/

5。参考文献

如何在 Java 中读取 XML 文件—(DOM 解析器)

原文:http://web.archive.org/web/20230101150211/https://mkyong.com/java/how-to-read-xml-file-in-java-dom-parser/

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

本教程将向您展示如何使用 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">``idstaff元素的属性。

延伸阅读

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。参考文献

如何在 Java 中读取 XML 文件—(JDOM 解析器)

原文:http://web.archive.org/web/20230101150211/https://mkyong.com/java/how-to-read-xml-file-in-java-jdom-example/

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。参考文献

如何在 Java 中读取 XML 文件(SAX 解析器)

原文:http://web.archive.org/web/20230101150211/https://mkyong.com/java/how-to-read-xml-file-in-java-sax-parser/

本教程将向您展示如何使用 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 文件,并依次调用以下事件或方法:

  1. startDocument()
  2. startElement()<name>
  3. characters()mkyong
  4. endElement()</name>
  5. 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,并覆盖startElementendElementcharacters方法来打印所有的 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。参考文献

如何在 Java 中读取 XML 文件(StAX 解析器)

原文:http://web.archive.org/web/20230101150211/https://mkyong.com/java/how-to-read-xml-file-in-java-stax-parser/

本教程展示了如何使用 Streaming API for XML (StAX) 解析器来读取或解析 XML 文档。

目录


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 包含两个主要接口XMLStreamReaderXMLStreamWriterXMLStreamReader.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 包含两个主要接口XMLEventReaderXMLEventWriter,我们使用 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/

8。参考文献

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值