Java——XML

九、XML

1、XML简介

XML是可扩展标记语言(Extensible Markup Language)的缩写,它是一种数据表示格式,可以描述十分复杂的数据结构常用于传输和存储数据。

一个XML文档大概是长这样:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE note SYSTEM "book.dtd">
<book id="1">
    <name>Java核心技术</name>
    <author>Cay S. Horstmann</author>
    <isbn lang="CN">1234567</isbn>
    <tags>
        <tag>Java</tag>
        <tag>Network</tag>
    </tags>
    <pubDate/>
</book>

xml默认使用UTF-8编码;

xml内容经常通过网络作为消息传说。

1.1 XML的结构

xml有着固定的结构,第一行一定是 <?xml version="1.0" ?> ,可以加上可选的编码。

<!DOCTYPE note SYSTEM "book.dtd"> 声明的是文档定义类型(DTD: Document Type Definition),是可选的。

下面的才是xml的文档内容。需要注意的是一个xml文档有且仅有一个根元素。

当内容中出现了特殊符号时,需要转义,因为xml文档中已经使用 <>'' 等做标识符。

字符表示
<&lt;
>&gt;
&&amp;
"&quot;
&apos;

例如内容为 Java<tm> 时应该写成:

<name>Java&lt;tm&gt;</name>

2、解析XML

XML是一种树形结构的文档,它有着两种标准的解析API:

  • DOM:一次性读取XML,并在内存中表示为树形结构;
  • SAX:以流的形式读取XML,使用事件回调。

以下面的xml为例(book.xml):

<?xml version="1.0" encoding="UTF-8" ?>
<book id="1" category="computer">
    <name>Java核心技术</name>
    <author>Cay S. Horstmann</author>
    <isbn lang="CN">1234567</isbn>
    <tags>
        <tag>Java</tag>
        <tag>Network</tag>
    </tags>
    <pubDate/>
</book>

2.1 使用DOM

book.xml 解析为DOM结构如下:

在这里插入图片描述

Java提供了DOM API来解析xml,使用了下面的对象来表示xml的内容:

  • Document:代表整个xml文档;

  • Element:代表一个xml元素;

    xml元素指的是从 开始标签结束标签 的部分。一个元素可以包括:

    • 其他元素
    • 文本
    • 属性(下面的Attribute)
  • Attribute:代表一个元素的某个属性。

在这里插入图片描述

使用DOM API解析XML文档的代码如下:

public class XMLUtil {

    public static void main(String args[]) throws IOException, SAXException, ParserConfigurationException {
        parseXML();
    }

    public static void parseXML() throws ParserConfigurationException, IOException, SAXException {
        InputStream input = XMLUtil.class.getResourceAsStream("/book.xml");
        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        DocumentBuilder db = dbf.newDocumentBuilder();
        Document doc = db.parse(input);
        printNode(doc, 0);
    }

  	//遍历以读取指定元素的值:
    static void printNode(Node n, int indent) {
        for (int i = 0; i < indent; i++) {
            System.out.print(' ');
        }
        switch (n.getNodeType()) {
            case Node.DOCUMENT_NODE:
                System.out.println("Document: " + n.getNodeName());
                break;
            case Node.ELEMENT_NODE:
                System.out.println("Element: " + n.getNodeName());
                break;
            case Node.TEXT_NODE:
                System.out.println("Text: " + n.getNodeName() + " = " + n.getNodeValue());
                break;
            case Node.ATTRIBUTE_NODE:
                System.out.println("Attr: " + n.getNodeName() + " = " + n.getNodeValue());
                break;
            case Node.CDATA_SECTION_NODE:
                System.out.println("CDATA: " + n.getNodeName() + " = " + n.getNodeValue());
                break;
            case Node.COMMENT_NODE:
                System.out.println("Comment: " + n.getNodeName() + " = " + n.getNodeValue());
                break;
            default:
                System.out.println("NodeType: " + n.getNodeType() + ", NodeName: " + n.getNodeName());
        }
        for (Node child = n.getFirstChild(); child != null; child = child.getNextSibling()) {
            printNode(child, indent + 1);
        }
    }

}

其中的 DocumentBuilder.parse() 用于解析一个xml,它可以接受InputStream、File、URL,会返回一个Document对象,这个对象代表了整个xml文档的属性结构

输出的机构如下:

//输出结果
Document: #document
 Element: book
  Text: #text = 
    
  Element: name
   Text: #text = Java核心技术
  Text: #text = //输出这个是因为在xml中,元素 `name` 和元素 `author` 存在换行,解析器把当成了text处理
    
  Element: author
   Text: #text = Cay S. Horstmann
  Text: #text = 
    
  Element: isbn
   Text: #text = 1234567
  Text: #text = 
    
  Element: tags
   Text: #text = 
        
   Element: tag
    Text: #text = Java
   Text: #text = 
        
   Element: tag
    Text: #text = Network
   Text: #text = 
    
  Text: #text = 
    
  Element: pubDate
   Text: #text = 2020-03-13
  Text: #text = 

2.2 使用SAX

使用DOM解析的优点是简单省事,但它的主要缺点是如果文件过大,占用内存太大。

针对内存太大的问题,就有了另外一种解析xml的方法是SAX(Simple API for XML)。它是一种基于流的解析方法,边读取XML边解析,并以事件回调的方法让调用者获取数据。也正是因为一边读取一边解析,所以不论XML文件多大,占用的内存都很小。

SAX解析会触发一系列的事件:

  • startDocument:开始读取XML文档;
  • startElement:读取到了一个元素,例如<book>
  • characters:读取到了字符;
  • endElement:读取到了一个结束的元素,例如</book>
  • endDocument:读取XML文档结束。

如果用SAX API解析XML,其Java代码如下:

public class XMLUtil {

    public static void main(String args[]) throws Exception {
        parseXML2();
    }
  
    public static void parseXML2() throws Exception {
        InputStream input = XMLUtil.class.getResourceAsStream("/book.xml");
        SAXParserFactory spf = SAXParserFactory.newInstance();
        SAXParser saxParser = spf.newSAXParser();
        saxParser.parse(input, new MyHandler());
    }
}
class MyHandler extends DefaultHandler {
    public void startDocument() throws SAXException {
        print("start document");
    }

    public void endDocument() throws SAXException {
        print("end document");
    }

    public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
        print("start element:", localName, qName);
    }

    public void endElement(String uri, String localName, String qName) throws SAXException {
        print("end element:", localName, qName);
    }

    public void characters(char[] ch, int start, int length) throws SAXException {
        print("characters:", new String(ch, start, length));
    }

    public void error(SAXParseException e) throws SAXException {
        print("error:", e);
    }

    void print(Object... objs) {
        for (Object obj : objs) {
            System.out.print(obj);
            System.out.print(" ");
        }
        System.out.println();
    }
}

关于startElement 中的参数是什么意思?

/**
     * Receive notification of the start of an element.
     *
     * <p>By default, do nothing.  Application writers may override this
     * method in a subclass to take specific actions at the start of
     * each element (such as allocating a new tree node or writing
     * output to a file).</p>
     *
     * @param uri The Namespace URI, or the empty string if the
     *        element has no Namespace URI or if Namespace
     *        processing is not being performed.
     * @param localName The local name (without prefix), or the
     *        empty string if Namespace processing is not being
     *        performed.
     * @param qName The qualified name (with prefix), or the
     *        empty string if qualified names are not available.
     * @param attributes The attributes attached to the element.  If
     *        there are no attributes, it shall be an empty
     *        Attributes object.
     * @exception org.xml.sax.SAXException Any SAX exception, possibly
     *            wrapping another exception.
     * @see org.xml.sax.ContentHandler#startElement
     */

2.3 转为JavaBean

DOM和SAX两种解析XML的标准接口,使用起来都不直观。

幸运的,我们可以使用 Jackson 这个开源库把XML转化为JavaBean。

首选需要导入依赖包:

<dependency>
  <groupId>com.fasterxml.jackson.dataformat</groupId>
  <artifactId>jackson-dataformat-yaml</artifactId>
  <version>2.10.3</version>
</dependency>

创建一个JavaBean Book.java

/*Book.java*/
public class Book {
    private long id;
    private String name;
    private String category;
    private String author;
    private String isbn;
    private List<String> tags;
    private String pubDate;
  //省略getter和setter
  //省略toString
}

解析测试:

@Test
public void m2() throws IOException {
  InputStream input = Main.class.getResourceAsStream("/book.xml");
  JacksonXmlModule module = new JacksonXmlModule();
  XmlMapper mapper = new XmlMapper(module);
  Book book = mapper.readValue(input, Book.class);

  System.out.println(book);
}
//输出结果
Book{id=1, name='Java核心技术', category='computer', author='Cay S. Horstmann', isbn='1234567', tags=[Java, Network], pubDate='2020-03-13'}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值