-
每个人都知道什么是XML,也知道它的格式.如果深入点理解如何使用XML,可能就不是每个人都知道的了. XML是一种自描述性文档,它的作用是内容的承载,和展示没有任何关系.所以,如何将XML里的数据以合理的方式取出展示,是XML编程的主要部分. 这篇文章从广度上来描述XML的一切特性.
XML有一大堆的官方文档和Spec文档以及教程.但是它们都太专业,文字太官方,又难懂,文字多,例子少,篇幅分散且跨度大. 于是需要一篇小文章,以通俗的话语以概括的角度来阐述XML领域的技术.再给几个小的example. 这就是我写这篇文章的原因.写它也是为了自我学习总结.
本文所用的代码结构如下图:
首先确定这篇文章使用的XML例子,后面所有的代码都基于此例.
01.
<?
xml
version
=
"1.0"
encoding
=
"UTF-8"
?>
02.
<?
xml-stylesheet
type
=
"test/xsl"
href
=
"bookStore.xsl"
?>
03.
<!DOCTYPE bookStore PUBLIC "bookStore.dtd" "bookStore.dtd">
04.
<
bookStore
name
=
"java"
xmlns
=
"http://joey.org/bookStore"
xmlns:audlt
=
"http://japan.org/book/audlt"
xmlns:xsi
=
"http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation
=
"bookStore.xsd"
>
05.
<
keeper
>
06.
<
name
>Joey</
name
>
07.
</
keeper
>
08.
<
books
>
09.
<
book
id
=
"1"
>
10.
<
title
>XML</
title
>
11.
<
author
>Steve</
author
>
12.
</
book
>
13.
<
book
id
=
"2"
>
14.
<
title
>JAXP</
title
>
15.
<
author
>Bill</
author
>
16.
</
book
>
17.
<
book
id
=
"3"
audlt:color
=
"yellow"
>
18.
<
audlt:age
> >18 </
audlt:age
>
19.
<
title
>Love</
title
>
20.
<
author
>teacher</
author
>
21.
</
book
>
22.
</
books
>
23.
</
bookStore
>
XML的作用
- 一种文档格式.只是内容的载体.
- 常用来做数据存储,数据传输或者配置描述.
- 它不负责展示.至于里面的内容如何使用,由XML程序来控制.
XML的格式
- 首先第一行为XML的声明:
<?xml version="1.0" encoding="uft-8">
2.紧跟着可能会有DTD校验方法.
<!DOCTYPE root-element SYSTEM "filename">
3.如果XML想依托工具自动展现,需要XML展现方法. CSS或者XSLT.
4.Element所构成的树形结构.
5.Element上的namespace.
6.除了用DTD验证方法,也可以Element上使用XSD来校验XML的合法性.<note xmlns="http://www.w3schools.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.w3schools.com note.xsd">
...
</note>XML字符编码
XML存储时所使用的字符编码. 这个编码告诉解析程序应该使用什么编码格式来对XML解码. 为了国际通用,使用UTF-8吧. 对于纯英文,UTF8只需要一个字节来表示一个英文字符. XML的size也不会太大.
XML命名空间
命名空间语法包括声明部分 默认命名xmlns="<URL>"或者指定命名xmlns:prefix="http://<namespace specification URL>" 和 使用部分<prefix:tag>或者<tag prefix:attr="">.
命名空间解决了两个问题.
- 相同名称的标签表示不同的意义,它们各自存在与自己的命名空间中.比如<table>即可以表示表格,也可以表示桌子. 给他们一个命名空间. <n1:table>为表单,<n2:table>为桌子.
- 对既有的元素进行属性扩展或者元素扩展. 比如本文例子中的<book>多了audlt的属性和子元素.它是对原来元素的扩展.
在Java或者JavaScript中是使用namespace的, 注意以下几点:
- DOM中存在两个方法getElementsByTagName()和getElementsByTagNameNS(). 第一个方法需要使用qualified name作为参数,而第二个方法需要使用namespace和localname作为参数. 如下
1.
document.getElementsByTagNameNS("http://japan.org/book/audlt", "age");
2.
document.getElementsByTagName("audlt:age");
- 如果XML里面使用了namespace, 那么XSLT和XPATH也必须使用同等的namespace,否则xpath将搜索不到你想查找的元素,在java的Xpath中,需要设置NamespaceContext. 请看DOM实例和我写的XSL文件.
XML语法验证
验证XML合法性靠的是DTD或者XSD.这是XML的两个规范. XSD比DTD要新,所以也先进.
DTD
本文中的XML里面声明了DTD的引用,XML parser就会自动加载DTD来验证XML. 这需要给parser设定两个前提.一是开启了验证模式,而是明白DTD的加载位置. XML parser可以是JS,java或者browser. 加载位置可以使用PUBLIC ID或者SYSTEM ID来判断.请看下面的声明:
<!DOCTYPE bookStore SYSTEM "bookStore.dtd">
上面的声明没有PUBLIC ID, 只有SYSTEM ID, SYSTEM ID=XML当前路径+"/bookStore.dtd". 可见system id是一个相对与XML的路径.
声明PUBLIC ID:
<!DOCTYPE bookStore PUBLIC "bookStore.dtd" "bookStore.dtd">
PUBLIC ID也为"bookStore.dtd". 这时候,Parser会自动根据这两个ID去尝试加载DTD文件,如果加载不到,则抛出exception. JAVA中,我们可以通过实现EntityResolver接口的方法来自定义DTD的所在位置. 详情请看JAVA部分.
本文用的DTD是:
01.
<!ELEMENT bookStore (keeper, books)>
02.
<!ATTLIST bookStore name CDATA #REQUIRED>
03.
<!ELEMENT keeper (name)>
04.
<!ELEMENT name (#PCDATA)>
05.
<!ELEMENT books (book)>
06.
<!ELEMENT book (title, author)>
07.
<!ATTLIST book id ID #REQUIRED>
08.
<!ELEMENT title (#PCDATA)>
09.
<!ELEMENT author (#PCDATA)>
XSD
使用XSD来验证XML只需要一个XSD的定义文件,开启Parser的XSD验证功能. XSD的验证方法在后面的JAVA代码中可以看到. 本文使用的XSD如下:
01.
<?
xml
version
=
"1.0"
encoding
=
"UTF-8"
?>
02.
<
xsd:schema
xmlns:xsd
=
"http://www.w3.org/2001/XMLSchema"
>
03.
<
xsd:element
name
=
"bookStore"
type
=
"bookStoreType"
/>
04.
05.
<
xsd:complexType
name
=
"bookStoreType"
>
06.
<
xsd:sequence
>
07.
<
xsd:element
name
=
"keeper"
type
=
"keeperType"
></
xsd:element
>
08.
<
xsd:element
name
=
"books"
type
=
"booksType"
></
xsd:element
>
09.
</
xsd:sequence
>
10.
<
xsd:attribute
name
=
"name"
type
=
"xsd:string"
></
xsd:attribute
>
11.
</
xsd:complexType
>
12.
13.
<
xsd:complexType
name
=
"keeperType"
>
14.
<
xsd:sequence
>
15.
<
xsd:element
name
=
"name"
type
=
"xsd:string"
></
xsd:element
>
16.
</
xsd:sequence
>
17.
</
xsd:complexType
>
18.
19.
<
xsd:complexType
name
=
"booksType"
>
20.
<
xsd:sequence
>
21.
<
xsd:element
name
=
"book"
type
=
"bookType"
></
xsd:element
>
22.
</
xsd:sequence
>
23.
</
xsd:complexType
>
24.
25.
<
xsd:complexType
name
=
"bookType"
>
26.
<
xsd:sequence
>
27.
<
xsd:element
name
=
"title"
type
=
"xsd:string"
></
xsd:element
>
28.
<
xsd:element
name
=
"author"
type
=
"xsd:string"
></
xsd:element
>
29.
</
xsd:sequence
>
30.
<
xsd:attribute
name
=
"id"
type
=
"xsd:int"
></
xsd:attribute
>
31.
</
xsd:complexType
>
32.
33.
</
xsd:schema
>
XML查询方法(XPath) 略.
XML展示方法(CSS, XSL)
如下面的代码片段所示,XML可以有stylesheet转换成其他格式, 如HTML, TXT等. stylesheet可以是css,也可以是xsl.
<?xml-stylesheet type="test/xsl" href="bookStore.xsl"?>
主流browser都已经支持这种转换格式. 除了自动转换,我们也可以使用代码对转换进行控制.我们可以用java在服务器端进行xslt的转换,也可以使用javascript在前端对xml进行xslt转换. 代码在后面均可找到. 书写xsl的时候,namespace一定要注意. xpath一定要和namespace所对应. 我所使用的XSL为:
01.
<?
xml
version
=
"1.0"
encoding
=
"UTF-8"
?>
02.
<
xsl:stylesheet
version
=
"1.0"
03.
xmlns:xsl
=
"http://www.w3.org/1999/XSL/Transform"
xmlns:b
=
"http://joey.org/bookStore"
04.
xmlns:a
=
"http://japan.org/book/audlt"
>
05.
<
xsl:output
method
=
"html"
version
=
"1.0"
encoding
=
"UTF-8"
indent
=
"yes"
></
xsl:output
>
06.
<
xsl:template
match
=
"/"
>
07.
<
html
>
08.
<
body
>
09.
<
h2
>Book Store<<<
xsl:value-of
select
=
"/b:bookStore/@name"
></
xsl:value-of
>>></
h2
>
10.
<
div
>
11.
There are <
xsl:value-of
select
=
"count(/b:bookStore/b:books/b:book)"
></
xsl:value-of
> books.
12.
</
div
>
13.
<
div
>
14.
Keeper of this store is <
xsl:value-of
select
=
"/b:bookStore/b:keeper/b:name"
></
xsl:value-of
>
15.
</
div
>
16.
<
xsl:for-each
select
=
"/b:bookStore/b:books/b:book"
>
17.
<
div
> Book:
18.
<
span
>title=<
xsl:value-of
select
=
"b:title"
></
xsl:value-of
></
span
>;
19.
<
span
>author=<
xsl:value-of
select
=
"b:author"
></
xsl:value-of
></
span
>
20.
<
xsl:if
test
=
"@a:color"
>
21.
<
span
style
=
"color:yellow"
>H Book, require age<
xsl:value-of
select
=
"a:age"
></
xsl:value-of
></
span
>
22.
</
xsl:if
>
23.
</
div
>
24.
</
xsl:for-each
>
25.
</
body
>
26.
</
html
>
27.
</
xsl:template
>
28.
</
xsl:stylesheet
>
XML与javascript
Javascript对XML的支持在IE和FF+Chrome上是不同的. IE使用的ActiveXObject来生成一个XML的实例.FF与Chrome等其它主流浏览器均遵循w3c规范. 生成的XML document可以使用其DOM方法对dom tree进行操作. 也可以借助框架dojo,jquery等简化操作.
下面这个例子是使用JS对XML进行XSLT转化,从而生成HTML.
01.
function createXMLDoc(xmlStr) {
02.
var xmlDoc;
03.
if
(window.DOMParser) {
04.
// FF Chrome
05.
var parser=
new
DOMParser();
06.
xmlDoc=parser.parseFromString(xmlStr,
"text/xml"
);
07.
}
else
if
(window.ActiveXObject){
08.
// Internet Explorer
09.
xmlDoc=
new
ActiveXObject(
"Microsoft.XMLDOM"
);
10.
xmlDoc.async=
"false"
;
11.
xmlDoc.loadXML(xmlStr);
12.
}
13.
return
xmlDoc;
14.
}
15.
16.
function transform(xmlDoc, xslDoc) {
17.
if
(window.XSLTProcessor) {
18.
// chrome FF
19.
var xslp =
new
XSLTProcessor();
20.
xslp.importStylesheet(xslDoc);
21.
return
xslp.transformToFragment(xmlDoc,document);
22.
}
else
if
(window.ActiveXObject){
23.
// IE
24.
return
xmlDoc.transformNode(xslDoc);
25.
}
26.
}
27.
28.
var xmlStr =
29.
[
'<bookStore name="java" xmlns="http://joey.org/bookStore" xmlns:audlt="http://japan.org/book/audlt" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="bookStore.xsd">'
,
30.
'<keeper><name>Joey</name></keeper>'
,
31.
'<books>'
,
32.
'<book id="1"> <title>XML</title><author>Steve</author></book>'
,
33.
'<book id="2"><title>JAXP</title> <author>Bill</author></book>'
,
34.
'<book id="3" audlt:color="yellow"><audlt:age> >18 </audlt:age> <title>Love</title><author>teacher</author></book>'
,
35.
'</books></bookStore>'
].join(
''
);
36.
37.
var xslStr =
38.
[
'<?xml version="1.0" encoding="UTF-8"?>'
,
39.
'<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:b="http://joey.org/bookStore" xmlns:a="http://japan.org/book/audlt">'
,
40.
'<xsl:output method="html" version="1.0" encoding="UTF-8" indent="yes" />'
,
41.
'<xsl:template match="/">'
,
42.
'<html>'
,
43.
'<body>'
,
44.
'<h2>Book Store<<<xsl:value-of select="/b:bookStore/@name"/>>></h2>'
,
45.
'<div>There are <xsl:value-of select="count(/b:bookStore/b:books/b:book)"/> books.</div>'
,
46.
'<div>Keeper of this store is <xsl:value-of select="/b:bookStore/b:keeper/b:name"/></div>'
,
47.
'<xsl:for-each select="/b:bookStore/b:books/b:book">'
,
48.
'<div>Book: '
,
49.
'<span>title=<xsl:value-of select="b:title"/></span>;<span>author=<xsl:value-of select="b:author"/></span>'
,
50.
'<xsl:if test="@a:color">'
,
51.
'<span color="yellow">H Book, require age<xsl:value-of select="a:age"/></span>'
,
52.
'</xsl:if>'
,
53.
'</div>'
,
54.
'</xsl:for-each>'
,
55.
'</body>'
,
56.
'</html>'
,
57.
'</xsl:template>'
,
58.
'</xsl:stylesheet>'
].join(
''
);
59.
60.
var xmlDoc = createXMLDoc(xmlStr);
61.
var xslDoc = createXMLDoc(xslStr);
62.
var dom = transform(xmlDoc, xslDoc);
63.
console.log(dom.childNodes[
0
].outerHTML);
XML与java
Java对XML的支持被称为JAXP(Java API for XML Processing). JAXP被当做标准,放入了J2SE1.4.从此以后,JRE自带XML的处理类库. 当然,JAXP允许使用第三方的XML Parser,不同的parser有着不同的优缺点,用户可以自己选择. 但所有的Parser均必须实现JAXP所约定的Interface. 掌握JAXP,需要知道以下内容. 这些都会在后面进行描述.
- JAXP的parser以及如何使用第三方parser.
- XML的解析方法SAX,DOM以及STAX.
- XML的写出方法STAX和XSLT.
- 使用XPath搜索DOM.
- JAXP使用XSLT转换XML.
- DOM与JDOM,DOM4J的区别.
- JAXP验证XML.
- JAXP支持namespace
J2SE的JAXP提供了5个包,用于支持XML.
- javax.xml.parsers - 为各种第三方parser提供了接口.
- org.w3c.dom - 提供了DOM类
- org.xml.sax - 提供了SAX类
- javax.xml.transform - 提供了XSLT的API.
- javax.xml.stream - 提供了STAX的API. STAX比SAX简单,比DOM快.
- javax.xml.xpath - 使用xpath对DOM进行字段查询.
每个接口与类的使用方法就不使用文字描述了,后面会用代码和注释的方式一一介绍JAXP的类库. 在描述SAX,StAX,DOM等方法之前,有必要做一个highlevel的比较. 每一个解析方法的优缺点是什么?改如何选择它们.
首先,XML解析器存在SAX, StAX和DOM, 而XML文件生成方法又有StAX和DOM. XPath是一个查询DOM的工具. XSLT是转换XML格式的工具. 如下图所示:
XML的解析从数据结构上来讲,分两大类: Streaming和Tree. Streaming又分为SAX和StAX. Tree就是DOM. SAX和StAX均是顺序解析XML,并生成读取事件.我们可以通过监听事件来得到我们想要的内容. DOM是一次性的以tree结构形式载入内存.
Streaming VS DOM
- DOM需要内存.对于大文档或者多文档,DOM性能差.还有,在android手机上就少用DOM这种占内存的东东吧.
- Streaming是实时性的,它没有上下文. 如果一个XML的element需要上下文才能理解,使用DOM会方便.
- 如果XML来自网络,我们对其结构并不明朗,使用Streaming比较好. DOM适合对XML的结构非常清楚.比如web.xml的结构就是一个人人皆知的结构.
- 需要对XML进行增删改查.则使用DOM.
Streaming又包含SAX和StAX, SAX是推(push)解析方法,而StAX是拉(pull)解析方法. 后面有SAX和StAX的实例.
Pull VS Push
- Pull可以让我们的代码掌握主动权,在合适的时候去调用解析器继续工作. Push是被动的听从解析器只会.解析器会不停的读,并把事件push到handler中.
- Pull的代码简单,小.Lib也小.
- Pull可以一个线程同时解析多个文档. 因为主动权在我们.
- StAX可以将一个普通的数据流伪造成一个个XML的读取事件,从而在构造成一个XML.好似DB中的View.
SAX | StAX | DOM | |
API Type | Push, Streaming | Pull, Streaming | Tree, In momery |
Support XPath? | No | No | Yes |
Read XML | Yes | Yes | Yes |
Write XML | No | Yes | Yes |
CRUD | No | No | Yes |
Parsing Validation (DTD, XSD) | Yes | Optional (JDK embedded | Yes |
javax.xml.validation包提供了跟XML解析独立与解析过程的验证方法. 性能比不过Parsing Validation. Parsing validation指的是在解析过程中进行验证.
SAX实例
SAXParser是调用XMLReader的, 如果使用SAXParser,则需要传参DefaultHandler. DefaultHandler实现了上图的4个Handler接口. 你也可以直接使用XMLReader,然后调用它的parser方法.只是在parser前,需set每个Handler. SAXParser是Event-Driven设计模式, 随着读取XML的字节,随着传递event给handler来处理.
读的工作其实是有XMLReader来做的,所有的events也是XMLReader产生的.所以,将一个非XML格式的文件模拟成一个XML,只需要复写XMLReader,读取非XML文件时,发出假的Event,这样handler将会把这个文件当做一个XML来处理. 这种机制会在XSLT中用到.
关于模拟XML
SAX可以将一个非XML格式文件的读取模拟成一个XML的文件的读取.通过构造XML的读取Event. 只是SAX需要复写XMLReader.
ContentHandler
用于处理XML的各种数据类型的读取事件.这里面的事件有
- setDocumentLocator. 读取<?xml ...?>
- startDocument and endDocument. XML的最外层tag的开始与结束.
- startPrefixMapping and endPrefixMapping. 命名空间影响范围的进入与退出.
- startElement and endElement. 每个Element的开始与结束.
- characters. 读取Element的text node value.
实现方式可以参考org.xml.sax.helpers.DefaultHandler.
ErrorHandler
用于处理XML解析阶段所发生的警告和错误.里面有三个方法,warning(), error()和fatalError(). waring和error用于处理XML的validation(DTD或XSD)错误.这种错误并不影响XML的解析,你可以把这种错误产生的exception压下来,而不向上抛.这样XML的解析不会被终断. fatalError是XML结构错误,这种错误无法被压制,即使我的handler不抛,Parser会向外抛exception.
DTDHandler
DTD定义中存在ENTITY和NOTATION.这都属于用户自定义属性. XML Parser无法理解用户自定义的ENTITY或者NOTATION, 于是它把这方面的验证工作交给了DTDHandler. DTDHandler里面只有2个方法:notationDecl和unparsedEntityDecl. 我们实现这两个方法来验证我们的NOTATION部分是否正确.
EntityResolver
在XML的验证段落里面提到过DTD的定位. EntityResolver可以帮助我们做这件事情. EntityResolver里面只有一个方法,叫做ResolveEntity(publicId, systemId). 每当Parser需要使用external文件的时候,就会调用这个方法. 我们可以在这个方法里面做一些预处理. 代码如下:
01.
public
class
MyEntityResolver
implements
EntityResolver {
02.
03.
@Override
04.
public
InputSource resolveEntity(String publicId, String systemId)
05.
throws
SAXException, IOException {
06.
if
(
"bookStore.dtd"
.equals(publicId)) {
07.
InputStream in =
this
.getClass().getResourceAsStream(
"/jaxp/resources/bookStore.dtd"
);
08.
InputSource is =
new
InputSource(in);
09.
return
is;
10.
}
11.
return
null
;
12.
}
13.
}
SAX Parser的使用
请注意里面是如何开启validation模式的. XSD有两种开启方法.
01.
public
class
MySAX {
02.
private
SAXParser parser;
03.
04.
public
static
void
main(String[] args)
throws
Exception {
05.
new
MySAX();
06.
}
07.
08.
public
MySAX()
throws
ParserConfigurationException, SAXException, IOException {
09.
// Use "javax.xml.parsers.SAXParserFactory" system property to specify a Parser.
10.
// java -Djavax.xml.parsers.SAXParserFactory=yourFactoryHere [...]
11.
// If property is not specified, use J2SE default Parser.
12.
// The default Parser is "com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl".
13.
SAXParserFactory spf = SAXParserFactory.newInstance();
14.
spf.setNamespaceAware(
true
);
15.
16.
// Use XSD defined by JAXP 1.3, JAVA1.5
17.
//SchemaFactory sf = SchemaFactory.newInstance("http://www.w3.org/2001/XMLSchema");
18.
//spf.setSchema(sf.newSchema(this.getClass().getResource("/jaxp/resources/bookStore.xsd")));
19.
// or Use old way defined by JAXP 1.2
20.
// parser.setProperty("http://java.sun.com/xml/jaxp/properties/schemaLanguage","http://www.w3.org/2001/XMLSchema");
21.
// parser.setProperty("http://java.sun.com/xml/jaxp/properties/schemaSource", new File("schema.xsd"));
22.
<P>
23.
// XSD disabled, use DTD. <SPAN style="LINE-HEIGHT: 1.5; FONT-SIZE: 9pt"> spf.setValidating(true);</SPAN><SPAN style="LINE-HEIGHT: 1.5; FONT-SIZE: 9pt"> </SPAN><SPAN style="LINE-HEIGHT: 1.5; FONT-SIZE: 9pt"> this.parser = spf.newSAXParser();</SPAN>
24.
</P>
25.
// You can directly use SAXParser to parse XML. Or use XMLReader.
26.
// SAXParser warps and use XMLReader internally.
27.
// I will use XMLReader here.
28.
//this.parser.parse(InputStrean, DefaultHandler);
29.
XMLReader reader =
this
.parser.getXMLReader();
30.
reader.setContentHandler(
new
MyContentHandler());
31.
reader.setDTDHandler(
new
MyDTDHandler());
32.
reader.setErrorHandler(
new
MyErrorHandler());
33.
reader.setEntityResolver(
new
MyEntityResolver());
34.
35.
InputStream in =
this
.getClass().getResourceAsStream(
"/jaxp/resources/bookStore.xml"
);
36.
InputSource is =
new
InputSource(in);
37.
is.setEncoding(
"UTF-8"
);
38.
reader.parse(is);
39.
}
40.
}
DOM实例 + XPath
借用oracle的图片来说明DOM解析的架构.
JAVA对XML的解析标准存在DOM, JDOM, DOM4J. 有人认为JDOM和DOM4J都是DOM的另一种实现方法,这是错误的.
- DOM是XML的数据模型标准,它跨越java,javascript等一切语言和平台.
- JDOM和DOM4J是专门针对java的模型.它简化了DOM,更加容易使用. 比如DOM中可以包含混合元素,即<a>text<b>text</b>test</a>. JDOM和DOM4J只允许<a>text</a>. 此外,DOM的数据访问模型也非常的复杂. 如果你的XML结构简单,可以使用JDOM和DOM4J. DOM4J的性能最好.
这篇文章只讲一下DOM. DOM的code和SAX的code相似的地方有:
- 开启DTD或者XSD validation的方法.
- 都用到ErrorHandler处理parser error和EntityResolver处理external引用.
- 使用SAXException.但这都不意味着DomBuilder内部使用了SAXParser.
得到DOM数据模型以后,可以使用DOM的遍历方法来寻找元素,也可以使用XPATH来查找指定元素,XPath的重点注意事项是NamespaceContext. 接下来是DOM的code实例.
01.
public
class
MyDOM {
02.
03.
public
static
void
main(String[] args)
throws
Exception {
04.
new
MyDOM();
05.
}
06.
07.
public
MyDOM()
throws
Exception {
08.
// Use "javax.xml.parsers.DocumentBuilderFactory" system property to specify a Parser.
09.
// java -Djavax.xml.parsers.DocumentBuilderFactory=yourFactoryHere [...]
10.
// If property is not specified, use J2SE default Parser.
11.
// The default Parser is "com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl".
12.
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
13.
14.
dbf.setIgnoringComments(
false
);
15.
dbf.setNamespaceAware(
true
);
16.
dbf.setIgnoringElementContentWhitespace(
true
);
17.
18.
// Use XSD defined by JAXP 1.3, JAVA1.5
19.
// SchemaFactory sf = SchemaFactory.newInstance("http://www.w3.org/2001/XMLSchema");
20.
// dbf.setSchema(sf.newSchema(this.getClass().getResource("/jaxp/resources/bookStore.xsd")));
21.
// or Use old way defined by JAXP 1.2
22.
// dbf.setAttribute("http://java.sun.com/xml/jaxp/properties/schemaLanguage","http://www.w3.org/2001/XMLSchema");
23.
// dbf.setAttribute("http://java.sun.com/xml/jaxp/properties/schemaSource", new File("schema.xsd"));
24.
// dbf.setSchema(schema);
25.
26.
// XSD disabled, use DTD.
27.
dbf.setValidating(
true
);
28.
29.
DocumentBuilder db = dbf.newDocumentBuilder();
30.
db.setErrorHandler(
new
MyErrorHandler());
31.
db.setEntityResolver(
new
MyEntityResolver());
32.
33.
Document document = db.parse(
this
.getClass().getResourceAsStream(
"/jaxp/resources/bookStore.xml"
));
34.
35.
// Operate on Document according to DOM module.
36.
NodeList list = document.getElementsByTagNameNS(
"http://joey.org/bookStore"
,
"book"
);
37.
System.out.println(list.item(
2
).getAttributes().item(
0
).getLocalName());
38.
// Node that if you don't specify name space, you need to use Qualified Name.
39.
System.out.println(document.getElementsByTagName(
"audlt:age"
).item(
0
).getTextContent());
40.
41.
// Use xpath to query xml
42.
XPathFactory xpf = XPathFactory.newInstance();
43.
XPath xp = xpf.newXPath();
44.
// Need to set a namespace context.
45.
NamespaceContext nc =
new
NamespaceContext() {
46.
47.
@Override
48.
public
String getNamespaceURI(String prefix) {
49.
if
(prefix.equals(
"b"
))
return
"http://joey.org/bookStore"
;
50.
if
(prefix.equals(
"a"
))
return
"http://japan.org/book/audlt"
;
51.
return
null
;
52.
}
53.
54.
@Override
55.
public
String getPrefix(String namespaceURI) {
56.
if
(namespaceURI.equals(
"http://joey.org/bookStore"
))
return
"b"
;
57.
if
(namespaceURI.equals(
"http://japan.org/book/audlt"
))
return
"a"
;
58.
return
null
;
59.
}
60.
61.
@Override
62.
public
Iterator getPrefixes(String namespaceURI) {
63.
return
null
;
64.
}
65.
66.
};
67.
xp.setNamespaceContext(nc);
68.
System.out.println(xp.evaluate(
"/b:bookStore/@name"
, document));
69.
System.out.println(xp.evaluate(
"/b:bookStore/b:books/b:book[@id=3]/@a:color"
, document));
70.
}
71.
}
StAX实例
StAX和SAX比较,代码简单,且可以写XML. 但StAX规范对于解析时的validation不是强制的.所以,JDK自带StAX解析器就不支持Parsing Validation.
StAX存在两种API, Cursor API(XMLStreamReader, XMLStreamWriter)和Iterator API(XMLEventReader, XMLEventWriter). Cursor API就是一个像游标一样的读或者写API. 我们得不停的调用XML writer和XML reader来读写XML每一个字段,这是的代码逻辑层和XML解析层交叉在一起,很混乱. Iterator API将逻辑层和XML解析层分离,对Event进行封装,所有的数据都封装在Event中,逻辑层和解析层靠Event实体来打交道,实现了松耦合. 这是我的理解:
- Cursor API比Iterator API更底层.
- Iterator API对Event封装的比较好,隔离了逻辑层和XML解析层.实现了松耦合.逻辑层只需要focus在event数据本身上.
- Iterator API更简单.推荐使用.
- 使用Iterator API很容易实现将普通文本格式的内容伪装转化成一个XML格式的文件.
下面代码分别用Cursor API和Iterator API对XML解析,然后再重新生成写到JAVA Console.
001.
public
class
MyStAX {
002.
003.
public
static
void
main(String[] args)
throws
Exception {
004.
coursorAPIReadWrite();
005.
eventAPIReadWrite();
006.
}
007.
008.
// use cursor API to read and write XML.
009.
public
static
void
coursorAPIReadWrite()
throws
Exception {
010.
XMLInputFactory xif = XMLInputFactory.newInstance();
011.
// Set properties for validation, namespace...
012.
// But, JDK embeded StAX parser does not support validation.
013.
//xif.setProperty(XMLInputFactory.IS_VALIDATING, true);
014.
xif.setProperty(XMLInputFactory.IS_NAMESPACE_AWARE,
true
);
015.
016.
// Handle the external Entity.
017.
xif.setXMLResolver(
new
XMLResolver() {
018.
public
Object resolveEntity(String publicID, String systemID,
019.
String baseURI, String namespace)
throws
XMLStreamException {
020.
if
(publicID.equals(
"bookStore.dtd"
)) {
021.
return
Class.
class
.getResourceAsStream(
"/jaxp/resources/bookStore.dtd"
);
022.
}
023.
return
null
;
024.
}
025.
});
026.
027.
XMLOutputFactory xof = XMLOutputFactory.newInstance();
028.
// Set namespace repairable. Sometimes it will bring you bug. Use it carefully.
029.
// xof.setProperty(XMLOutputFactory.IS_REPAIRING_NAMESPACES, true);
030.
031.
InputStream sourceIn = Class.
class
.getResourceAsStream(
"/jaxp/resources/bookStore.xml"
);
032.
OutputStream targetOut = System.out;
//new FileOutputStream(new File("target.xml"));
033.
034.
XMLStreamReader reader = xif.createXMLStreamReader(sourceIn);
035.
XMLStreamWriter writer = xof.createXMLStreamWriter(targetOut, reader.getEncoding());
036.
writer.writeStartDocument(reader.getEncoding(), reader.getVersion());
037.
038.
while
(reader.hasNext()) {
039.
int
event = reader.next();
040.
switch
(event) {
041.
case
XMLStreamConstants.DTD:
042.
out(reader.getText());
043.
writer.writeCharacters(
"\n"
);
044.
writer.writeDTD(reader.getText());
045.
writer.writeCharacters(
"\n"
);
046.
break
;
047.
case
XMLStreamConstants.PROCESSING_INSTRUCTION:
048.
out(reader.getPITarget());
049.
writer.writeCharacters(
"\n"
);
050.
writer.writeProcessingInstruction(reader.getPITarget(), reader.getPIData());
051.
break
;
052.
case
XMLStreamConstants.START_ELEMENT:
053.
out(reader.getName());
054.
NamespaceContext nc = reader.getNamespaceContext();
055.
writer.setNamespaceContext(reader.getNamespaceContext());
056.
writer.setDefaultNamespace(nc.getNamespaceURI(
""
));
057.
writer.writeStartElement(reader.getPrefix(), reader.getLocalName(), reader.getNamespaceURI());
058.
059.
for
(
int
i=
0
; i<reader.getAttributeCount(); i++) {
060.
QName qname = reader.getAttributeName(i);
061.
String name=qname.getLocalPart();
062.
if
(qname.getPrefix()!=
null
&& !qname.getPrefix().equals(
""
)) {
063.
//name = qname.getPrefix()+":"+name;
064.
}
065.
writer.writeAttribute(name, reader.getAttributeValue(i));
066.
}
067.
for
(
int
i=
0
; i<reader.getNamespaceCount(); i++) {
068.
writer.writeNamespace(reader.getNamespacePrefix(i), reader.getNamespaceURI(i));
069.
}
070.
break
;
071.
case
XMLStreamConstants.ATTRIBUTE:
072.
out(reader.getText());
073.
break
;
074.
case
XMLStreamConstants.SPACE:
075.
out(
"SPACE"
);
076.
writer.writeCharacters(
"\n"
);
077.
break
;
078.
case
XMLStreamConstants.CHARACTERS:
079.
out(reader.getText());
080.
writer.writeCharacters(reader.getText());
081.
break
;
082.
case
XMLStreamConstants.END_ELEMENT:
083.
out(reader.getName());
084.
writer.writeEndElement();
085.
break
;
086.
case
XMLStreamConstants.END_DOCUMENT:
087.
writer.writeEndDocument();
088.
break
;
089.
default
:
090.
out(
"other"
);
091.
break
;
092.
093.
}
094.
}
095.
writer.close();
096.
reader.close();
097.
098.
}
099.
100.
public
static
void
eventAPIReadWrite()
throws
Exception {
101.
XMLInputFactory xif = XMLInputFactory.newInstance();
102.
xif.setProperty(XMLInputFactory.IS_NAMESPACE_AWARE,
true
);
103.
// Handle the external Entity.
104.
xif.setXMLResolver(
new
XMLResolver() {
105.
public
Object resolveEntity(String publicID, String systemID,
106.
String baseURI, String namespace)
throws
XMLStreamException {
107.
if
(publicID.equals(
"bookStore.dtd"
)) {
108.
return
Class.
class
.getResourceAsStream(
"/jaxp/resources/bookStore.dtd"
);
109.
}
110.
return
null
;
111.
}
112.
});
113.
XMLOutputFactory xof = XMLOutputFactory.newInstance();
114.
115.
InputStream sourceIn = Class.
class
.getResourceAsStream(
"/jaxp/resources/bookStore.xml"
);
116.
OutputStream targetOut = System.out;
117.
XMLEventReader reader = xif.createXMLEventReader(sourceIn);
118.
XMLEventWriter writer = xof.createXMLEventWriter(targetOut);
119.
120.
while
(reader.hasNext()) {
121.
XMLEvent event = reader.nextEvent();
122.
out(event.getEventType());
123.
writer.add(event);
124.
}
125.
reader.close();
126.
writer.close();
127.
}
128.
129.
public
static
void
out(Object o) {
130.
System.out.println(o);
131.
}
132.
133.
}
XSLT实例
上面了解了SAX,DOM和STAX,它们均为XML解析方法. 其中SAX只适合解析读取. DOM则是XML内存中的数据展现. STAX可以解析,也可以写出到文件系统.
如果将DOM从内存输出XML文件. 如果需要将一个XML文件转换成一个HTML或任意其他格式文件,则需要JAXP的XSLT特性. 这里的转换包括:
- 两个结构不同的DOM相互转换. DOMSouce -----> DOMResult
- DOM输出到XML. DOMSource -----> StreamResult
- DOM转化成另一种格式文件,比如HTML. DOMSource ---(XSL)--->StreamResult.
- XML文件转换成另一种格式文件. SAXSource|StreamSource ---(XSL)---->StreamResult
- XML文件到DOM. SAXSource|StreamSouce ------> DOMResult
- DOM到另一个SAX事件 DOMSource------>SAXResult
XSLT的下面包含了4个包:
- javax.xml.transform - 定义了Transformer类,调用Transformer的transform(source, result)方法,可以进行XML的转换.
- javax.xml.transform.sax - 里面定义了SAXSource和SAXResult.
- javax.xml.transfrom.dom - 定义了DOMSource和DOMResult.
- javax.xml.transform.stream - 定义了StreamSource和StreamResult.
- javax.xml.transform.stax - 定义了StAXSource和StAXResult.(java1.6)
从上面可以看出,JAXP可以进行4*4=16种转换方式.(sax, sax), (sax, dom), (sax, stream)...
再高级一点,利用SAXSouce----->DOMResult的转化功能, 和SAX模拟XML读取功能, XSLT可以将一个非XML格式的文件,转换成一个DOM. 下面的代码将包含此例. 代码中还包含另外一个例子,就是把XML按照XSL的格式转换成HTML.
注意, XSLT处理DTD有技巧:
在xml2html的转换中, 使用StreamSource在代码的书写上是最简单的, 但为什么使用了SAXSource? 那是因为要转换的XML中引用了DTD, StreamSource无法处理外部引用, 会导致Transformer抛TransformerException. 失败的异常内容为DTD文件找不到. 那么,在这种情况下,我们只能使用SAXSource,并给它赋予一个可以解析外部DTD引用的XMLReader. 终于成功了.
001.
public
class
MyXSLT {
002.
TransformerFactory tff;
003.
004.
public
static
void
main(String[] args)
throws
Exception {
005.
MyXSLT xslt =
new
MyXSLT();
006.
xslt.xml2html();
007.
xslt.str2xml();
008.
}
009.
010.
public
MyXSLT() {
011.
tff = TransformerFactory.newInstance();
012.
}
013.
014.
public
void
xml2html()
throws
Exception {
015.
Transformer tr = tff.newTransformer(
new
SAXSource(
new
InputSource(
this
.getClass().getResourceAsStream(
"/jaxp/resources/bookStore.xsl"
))));
016.
017.
SAXParserFactory spf = SAXParserFactory.newInstance();
018.
SAXParser parser = spf.newSAXParser();
019.
parser.getXMLReader().setEntityResolver(
new
EntityResolver() {
020.
@Override
021.
public
InputSource resolveEntity(String publicId, String systemId)
022.
throws
SAXException, IOException {
023.
if
(
"bookStore.dtd"
.equals(publicId)) {
024.
InputStream in =
this
.getClass().getResourceAsStream(
"/jaxp/resources/bookStore.dtd"
);
025.
InputSource is =
new
InputSource(in);
026.
return
is;
027.
}
028.
return
null
;
029.
}
030.
});
031.
Source source =
new
SAXSource(parser.getXMLReader(),
new
InputSource(
this
.getClass().getResourceAsStream(
"/jaxp/resources/bookStore.xml"
)));
032.
Result target =
new
StreamResult(System.out);
033.
tr.transform(source, target);
034.
}
035.
036.
// "[joey,bill,cat]" will be transformed to
037.
// <test><name>joey</name><name>bill</name><name>cat</name></test>
038.
public
void
str2xml()
throws
Exception {
039.
final
String[] names =
new
String[]{
"joey"
,
"bill"
,
"cat"
};
040.
Transformer tr = tff.newTransformer();
041.
042.
Source source =
new
SAXSource(
new
XMLReader() {
043.
private
ContentHandler handler;
044.
045.
@Override
046.
public
void
parse(InputSource input)
throws
IOException,
047.
SAXException {
048.
handler.startDocument();
049.
handler.startElement(
""
,
"test"
,
"test"
,
null
);
050.
for
(
int
i=
0
; i<names.length; i++) {
051.
handler.startElement(
""
,
"name"
,
"name"
,
null
);
052.
handler.characters(names[i].toCharArray(),
0
, names[i].length());
053.
handler.endElement(
""
,
"name"
,
"name"
);
054.
}
055.
handler.endElement(
""
,
"test"
,
"test"
);
056.
handler.endDocument();
057.
}
058.
059.
@Override
060.
public
void
parse(String systemId)
throws
IOException, SAXException {
061.
}
062.
063.
@Override
064.
public
boolean
getFeature(String name)
065.
throws
SAXNotRecognizedException, SAXNotSupportedException {
066.
return
false
;
067.
}
068.
069.
@Override
070.
public
void
setFeature(String name,
boolean
value)
071.
throws
SAXNotRecognizedException, SAXNotSupportedException {
072.
}
073.
074.
@Override
075.
public
Object getProperty(String name)
076.
throws
SAXNotRecognizedException, SAXNotSupportedException {
077.
return
null
;
078.
}
079.
080.
@Override
081.
public
void
setProperty(String name, Object value)
082.
throws
SAXNotRecognizedException, SAXNotSupportedException {
083.
}
084.
085.
@Override
086.
public
void
setEntityResolver(EntityResolver resolver) {
087.
}
088.
089.
@Override
090.
public
EntityResolver getEntityResolver() {
091.
return
null
;
092.
}
093.
094.
@Override
095.
public
void
setDTDHandler(DTDHandler handler) {
096.
}
097.
098.
@Override
099.
public
DTDHandler getDTDHandler() {
100.
return
null
;
101.
}
102.
103.
@Override
104.
public
void
setContentHandler(ContentHandler handler) {
105.
this
.handler = handler;
106.
}
107.
108.
@Override
109.
public
ContentHandler getContentHandler() {
110.
return
handler;
111.
}
112.
113.
@Override
114.
public
void
setErrorHandler(ErrorHandler handler) {
115.
}
116.
117.
@Override
118.
public
ErrorHandler getErrorHandler() {
119.
return
null
;
120.
}
121.
},
new
InputSource());
122.
123.
Result target =
new
StreamResult(System.out);
124.
tr.transform(source, target);
125.
}
126.
127.
}