本章主要介绍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>
毕竟属性在解析时候非常麻烦。
6、除了元素、文本之外,还有一些特殊的片段:
(1)字符引用,如&#十进制,&#X十六进制
(2)实体引用,如< > & " '
(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核心技术卷二,读到TransformFactory+StreamSource完成XSLT转化这一部分的时候遇到一个问题:在程序清单2-15 transform/TransformTest.java 的第50行中的handler变量没有值,一直没搞懂为什么。
看到你说这个已经非常不主流了,想问下这里的不主流指的是什么意思。
这篇读书笔记写于2012年,也就是4年前。即使在当年看来,XSLT已经不是主流技术了。因为xml在服务端开发中用的越来越少,而在前端领域,也很少用xslt+xml这种技术作为渲染或者逻辑手段。
此外,就解析xml和xslt本身而言,Java自带的这套性能太差,有很多可以替代的开源库。
所以我写下了,“不是主流”,这样的评价,可能有些主观,但反应了部分事实。嗯,就是这个样子。
您好,最近也在读java核心技术卷二,看到TransformFactory+StreamSource完成XSLT转化这一部分遇到一个问题,就是:程序清单2-15 transform/TransformTest.java 第50行里的 handler 变量没有值,这里一直搞不懂为什么。
然后就是您提到这个现在已经非常不主流了,想请教一下不主流是指什么。