Java 核心技术卷 II(第 8 版) – 读书笔记 – 第 2 章

Java知己
本章主要介绍 Java 与 XML。

1、XML 非常适合表示复杂的、结构化数据。

2、XML 与 HTML 有所差异,XML 更严格,如:区分大小写、必须结束标签、属性必须有值。

3、XML 以文档头开始,如:

<?xml version="1.0" encoding="utf-8"?>

4、然后包含若干元素和子元素。

<configuration>
  <title></title>
  ......
</configuration>

5、如果能使用元素,就尽量不要使用属性,如:

<font>
    <family>宋体</family>
    <size>14</size>
</font>

要好于:

<font size="14" family="宋体">
</font>

毕竟属性在解析时候非常麻烦。

关注公众号:「Java知己」,发送「Group」,加入Java知己微信群。与10万程序员一起进步。每天更新Java知识哦,期待你的到来!

6、除了元素、文本之外,还有一些特殊的片段:
(1) 字符引用,如 &# 十进制,&#X 十六进制
(2) 实体引用,如 & lt; > & " '
(3)CDATA 引用部分,在这之中可以自由的使用 & 等上述特殊符号:

<![CDATA[ < &> 都可用 ]]>,但是这部分内不能包含]]>,要特别注意。

(4) 处理命令,<?xml 等
(5) 注释:<!– 注释内容 –>

7、解析 XML 有两种基本方式:
(1) 树状:将 XML 完全转化成树状结构,又叫 DOM(Document Object Model)
(2) 流解析:读入 XML 文档时生成对应的事件,流装,有的叫做 SAX(Simple API for XML)。。。好冷啊。。。

8、DOM 解析器的接口居然已经被 W3C 标准化了。。太神奇了,看来 XML 应用真广泛。。我第一次听说具体某个接口实现被标准化的。。。JDK 自带的 org.w3c.dom 就是这种标准化的结果(SUN 提供实现)。而我们使用的其他第三方,也基本遵循这样的接口标准。

9、使用 Java 自带的传统 Parser(DOM 解析器)解析文档:

//构造DocumentBuilderFactory实例
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
//构造DocumentBuilder实例
DocumentBuilder builder = factory.newDocumentBuilder();
//用DocumentBuilder解析文档
Document doc = builder.parse("./test.xml");

10、Document 由若干个 Element(实际实现了 Node) 组成,父 doc.getElement() 将返回 root 结点。
获取孩子:getChildNodes(),但它会包含文字、结点等,如要用 instanceof Element 进行判断。
获取文字:getData() 获取文本。
获取属性:getAttributes()

下面的例子演示了如何遍历孩子 Node,判断类型(Element/Text/Attr) ,并打印特有的属性:

import javax.xml.parsers.*;
import org.w3c.dom.*;
import org.xml.sax.*;
import java.io.*;

public class XMLTest {
    public static void main(String [] args) throws
        Exception {
            DocumentBuilderFactory factory =
                DocumentBuilderFactory.newInstance();
            DocumentBuilder builder =
                factory.newDocumentBuilder();
            Document doc = builder.parse("./sitemap.xml");

            //Get root element
            Element root = doc.getDocumentElement();
            //Get all children
            NodeList nodes =  root.getChildNodes();
            for(int i = 0; i < nodes.getLength(); i++) {
                Node node = nodes.item(i);
                if(node instanceof Element) {
                    System.out.println("Element "+node.getNodeName());
                    NamedNodeMap attr_map = node.getAttributes();
                    for(int j=0;j<attr_map.getLength();j++) {
                        Node node_attr = attr_map.item(j);
                        if(node_attr instanceof Attr) {
                            System.out.println(((Attr)node_attr).getName() + " " + ((Attr)node_attr).getValue());
                        }
                    }
                } else if (node instanceof Text) {
                    System.out.println("Text "+node.getNodeName());
                    System.out.println(((Text)node).getWholeText().trim());
                    System.out.println();
                } else {
                    System.out.println(node.getClass());
                }
            }
        }
}

11、由于 XML 是自解释结构,非常灵活。我们在解析、使用一个 XML 文档之前,并不知道它们是否是按照我们预想的结构组织的。于是,DTD 或者 Schema 通过预先定义的文档结构方式,来验证一个 xml 是否符合自己预期的格式

12、XML Schema 比 DTD 的功能更为强大,较新的 xml 规范一般都采用 XML Schema。

13、通常,把 DTD 定义单独做为. dtd 文件,放在别的网站 / URL 上,然后从 xml 中引用:

<?xml version="1.0" ?>
<!DOCTYPE configuration SYSTEM "http://www.xxx.com/config.dtd"

14、关于 DTD 规则不在描述了,如果确定一个 xml 是符合你指定的 dtd,那么访问 xml 就可以很简单:

直接 if(elem.getTag().equals(“name”)){….}

这样的处理就可以了。

设置解析器:

setEntityResolver(EntityResolver er)

一般需要再设置错误处理回调:

setErrorHandler(ErrorHandler eh)

15、如果需要只访问 XML 中的某一部分,那么用 SAX 有点大炮打蚊子了。。我们可以用 XPath。例如下面的 xml 代码:

<configuration>
    <database>
        <user>user</user>
        <pass>123456</pass>
    </database>
</configuration>

我们其实可以直接访问:/configuration/database/user 来获取用户名:

而传统 SAX 要获得 N 个 Element 再判断神马的。

其他一些 XPath 语法:

/gridbag/row -> 可能是获得一组结点
/gridbag/row[1] -> 选定第一个 row 元素 (下标从 1 开始)
@表示属性:
/gridbag/row[1]/cell[1]/@anchor

16、JDK5 之后支持 XPath 了:javax.xml.xpath.XPathFactory

下面的例子演示了用 XPath 分别读取结点、数组、属性等。

evaluate 的最后一个常量见 javax.xml.xpath.XPathConstants。

如果是要一堆数组,直接 NODESET,然后就被映射为 NodeList 了,麻烦写 for 就没写。

import javax.xml.parsers.*;
import javax.xml.xpath.*;
import org.w3c.dom.*;
import org.xml.sax.*;
import java.io.*;

public class TestXPath {
    public static void main(String [] args) throws Exception {
        //doc
        DocumentBuilderFactory factory =
            DocumentBuilderFactory.newInstance();
        DocumentBuilder builder =
            factory.newDocumentBuilder();
        Document doc = builder.parse("./sitemap.xml");
        //Xpath
        XPathFactory xpath_factory = XPathFactory.newInstance();
        XPath path = xpath_factory.newXPath();

        //XPath visit as Text
        String text = path.evaluate("/urlset/url[1]/loc", doc);
        System.out.println(text);

        //XPath visit as Node
        Node node = (Node)path.evaluate("/urlset/url[1]", doc, XPathConstants.NODE);
        System.out.println(node.getNodeName());

        //XPath visit as Number(as double)
        Double num = (Double)path.evaluate("/urlset/url[1]/priority", doc, XPathConstants.NUMBER);
        System.out.println(num);

    }
}

17、Java 自带的 xml 解析器也支持命名空间,形如:

<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
......
</xsd:schema>

对应的 API 中可以获得 LocalName 和 URL 空间:

Node.getLocalName()

Node.getNamespaceURI()

也可以获取工厂是否支持 URL 空间

DocumentBuilderFactory.isNamespaceAware()/SetNamespaceAware()

18、传统的 DOM 解析器试图将 XML 文件映射成树状结构。这对大的、复杂的 XML 是致命的。比较先进的方法是流解析器 (streaming parser),通过回调函数进行处理。

19、我们前面使用的都是传统 Parser(默认的 DocumentFactory),Java 还提供 SAX,用于流状解析 XML 文件,它更适用于大文件。

一个 SAXParser 将在解析的过程中回调 ContentHandler,后者需要自己实现,主要方法有:

startElement 和 endElement:在遇到起始、终止标签时使用。
characters:每当遇到字符数据时调用
startDocument 和 endDocument 分别在文档开始和结束时各调用一次。

一般,我们可以直接使用 DefaultHandler,它对上述方法都实现了空方法。我们需要在哪些地方做处理,直接覆盖对应的方法就可以了。

一个用 SAXParser 和 startElement 来打印所有结点的 href 属性的例子如下:

其实 SAXParser 真的很强大的……

import org.xml.sax.helpers.*;
import javax.xml.parsers.*;
import org.xml.sax.*;

public class SAXTest {
    public static void main(String [] args) throws Exception {
        //SAXParser
        SAXParserFactory sax_fac = SAXParserFactory.newInstance();
        SAXParser sax_parser = sax_fac.newSAXParser();

        //Handler
        DefaultHandler handler = new DefaultHandler()
        {
            public void startElement(String uri, String localName, String qName, Attributes attributes) {
                for(int i = 0; i < attributes.getLength(); i++) {
                    String name = attributes.getLocalName(i);
                    if(name.equals("href")) {
                        String value = attributes.getValue(i);
                        System.out.println(value);
                    }
                }
            }
        };  

        //SAXParser
        sax_parser.parse("./sitemap.xml", handler);
    }
}

20、如果说 SAX 的事件处理是 Push 的(有事件来的时候自动回调预先定义的函数)。那么 JDK6 新提供的 StAX 就是 Pull 的。你需要自己去轮询事件:

StAX 的名字是:XMLStreamReader

话说我没感觉比 SAX 简单。。。

import org.xml.sax.helpers.*;
import javax.xml.parsers.*;
import javax.xml.stream.*;
import org.xml.sax.*;
import java.io.*;

public class StAXTest {
    public static void main(String [] args) throws Exception {
        //StAXParser
        XMLInputFactory stax_fac = XMLInputFactory.newInstance();
        XMLStreamReader parser = stax_fac.createXMLStreamReader(new FileReader("./sitemap.xml"));

        //Use reader to pull all event
        while(parser.hasNext()) {
            int event = parser.next();
            if(event==XMLStreamConstants.START_ELEMENT) {
                int attr_cnt = parser.getAttributeCount();
                if(attr_cnt!=0) {
                    for(int i=0;i<attr_cnt; i++) {
                        String attr_name = parser.getAttributeLocalName(i);
                        if(attr_name.equals("href")) {
                            System.out.println(parser.getAttributeValue(i));
                        }
                    }
                }
            }
        }   

    }
}

21、 前面一直在研究如何解析 (读取 XML),现在开始研究如何生成 XML(生成 XML)。

22、使用 DOM 树那个传统接口,可以一步一步地构造 DOM 树。但是奇怪的是…… 不支持输出到流。。。

//创建子结点
Document doc = builder.newDocument();
Element rootElement = doc.createElement(“root”);
doc.appendChild(rootElement);

//添加Text
Text textNode = doc.createTextNode(“I’m text string.”);
rootElement.appendChild(textNode);

//设置属性
rootElement.setAttribute(name, value);

23、上面的方式需要用很恶心的方式才能输出出来。。。所以此时可以考虑切换到其他开源 XML 解析了。

24、如果一定要用 JDK 原生的搞定,可以用 StAX,用 XMLStreamWriter.writeXXX 函数:

writeStartDocument()
writeEndDocument()
writeStartElement()
writeEndElement()
writeAttribute()
writeCharacters()

下面的,生成 XML 文档:

import org.xml.sax.helpers.*;
import javax.xml.parsers.*;
import javax.xml.stream.*;
import org.xml.sax.*;
import java.io.*;

public class WriteXMLTest {
    public static void main(String [] args) throws Exception {
        //StAXParser
        XMLOutputFactory stax_fac = XMLOutputFactory.newInstance();
        XMLStreamWriter writer = stax_fac.createXMLStreamWriter(new FileWriter("./temp.xml"));

        //Make document
        writer.writeStartDocument();
        //make root node
        writer.writeStartElement("root");
        //make node1 node and attribute
        writer.writeStartElement("node1");
        writer.writeAttribute("attr", "attr_value");
        writer.writeEndElement();
        writer.writeEndElement();
        writer.writeEndDocument();
        //Flush to disk
        writer.close();
    }   
}

25、XSL 转换 (XSLT):可以将 XML 转化成其他 HTML、文本等。需要提供 XSLT 样式表。

26、Java 中用 TransformFactory+StreamSource 可以完成 XSLT 转化。这货已经非常不主流了。。所以不写代码了。

本章完毕。

您可能也喜欢如下文章:

Java 核心技术卷 II(第 8 版) – 读书笔记 – 第 1 章(上)

Java 核心技术卷 II(第 8 版) – 读书笔记 – 第 1 章(下)

反射是框架设计的灵魂

关注公众号:「Java知己」,发送「1024」,免费领取 30 本经典编程书籍。​与 10 万程序员一起进步。每天更新Java知识哦,期待你的到来!

Java知己

image

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值