学习XML对任何类型的应用开发都很有帮助 比如可以在应用中有些经常要改变的配置或者像网站的后台管理DIY部分都可能会用到一些配置的修改 XML这种数据表现形式 是非常好的解决办法 另外对于学习AJAX也有很大的好处 因为DOM是一个已经标准化了的解析办法 在javascript中也会同样要处理文档结构 只是语法不同 只用再掌握JavaScript的DOM API 就可以很轻松的达到同样的效果
产生: 为了解决更清晰的表达层次结构和重复元素 使读写变得更简单 代替properties的一种方案
tips :
1 XML是大小写敏感的
2 XML的结束标签不能省略
3 XML中 只有一个标签而没有相对应的结束标签的元素必须以 "/"结尾
4 属性值必须用引号括起来
5 所有属性值必须有属性
6 最好值包括文本或者只包括子元素 不要两者兼有 避免出现:
<
t1
>
text1
<
t2
>
text2
</
t2
>
</
t1
>
7 一个重要的规则: 属性只是用来修饰值
解析XML (以SUN的api为基础)
要构造一个DocumentBuilder对象 可以从DocumentBuilderFactory中得到
方法
DocumentBuilderFactory factory
=
DocumentBuilderFactory.newInstance();
DocumentBuilder builder
=
factory.newDocumentBuilder();
然后就能获得一个Document对象
Document是XML的树型结构的具体形式的表现 由实现Node接口及多个子接口的对象构成
得到一个Document
File f
=
new
File(
"
bin/hibernate.cfg.xml
"
);
Document doc
=
builder.parse(f);
一些其他的构造方法(DocumentBuilder)
API中看出可以用URL和IO中的类来构造Document
重要接口
Node 是整个对象模型的主要数据类型 表示该文档树的单个节点 实现Node的接口有 Document Text Element Entity 等
tip : 并不是所有的Node都有子节点 如text 不同的接口会有更加合理和简单的获取和设置的相关方法
Document是用来表示整个XML或者Html之类的文档 是文档的根 提供数据的访问和构造以及修改方法
另:document也是实现Node的接口
NodeList接口提供对节点的有序集合的抽象,没有定义或约束如何实现此集合。DOM 中的 NodeList
对象是活动的。
NodeList
中的项可以通过从 0 开始的整数索引进行访问。
Text 表示Element或者Attr的文本内容
Element
接口表示 HTML 或 XML 文档中的一个元素。元素可能有与它们相关的属性;由于 Element
接口继承自 Node
,所以可以使用一般 Node
接口属性 attributes
来获得元素所有属性的集合。Element
接口上有通过名称获得 Attr
对象或通过名称获得属性值的方法。在 XML 中(其中的属性值可能包含实体引用),应该获得 Attr
对象来检查表示属性值的可能相当复杂的子树。另一方面,在 HTML 中(其中的所有属性都有简单的字符串值),可以使用直接访问属性值的方法,这既安全又便捷。
NamedNodeMap 实现 NamedNodeMap
接口的对象用于表示可以通过名称访问的节点的集合。注意,NamedNodeMap
不从 NodeList
继承;不以任何特定的顺序维护 NamedNodeMaps
。在实现 NamedNodeMap
的对象中包含的对象还可以通过顺序索引进行访问,但只允许方便地枚举 NamedNodeMap
的内容,并不意味着 DOM 指定这些节点的顺序。
例子:
解析一个xml文档来 基本接口及其方法的应用
我就把我的hibernate.cfg.xml拿来玩 当然Hibernate本身是用Dom4J的
<?
xml version='1.0' encoding='UTF-8'
?>
<!
DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd"
>

<!--
Generated by MyEclipse Hibernate Tools.
-->
<
hibernate-configuration
>
<
session-factory
>
<
property
name
="connection.driver_class"
>
com.mysql.jdbc.Driver
</
property
>
<
property
name
="connection.url"
>
jdbc:mysql://localhost:3306/sshtest
</
property
>
<
property
name
="connection.username"
>
root
</
property
>
<
property
name
="connection.password"
>
00000000
</
property
>
<!--
JDBC connection pool
-->
<
property
name
="hibernate.connection.provider_class"
>
org.hibernate.connection.C3P0ConnectionProvider
</
property
>
<
property
name
="hibernate.c3p0.max_size"
>
20
</
property
>
<
property
name
="hibernate.c3p0.min_size"
>
5
</
property
>
<
property
name
="hibernate.c3p0.timeout"
>
120
</
property
>
<
property
name
="hibernate.c3p0.max_statements"
>
100
</
property
>
<
property
name
="hibernate.c3p0.idle_test_period"
>
120
</
property
>
<
property
name
="hibernate.c3p0.acquire_increment"
>
2
</
property
>
<
property
name
="hibernate.transaction.factory_class"
>
org.hibernate.transaction.JDBCTransactionFactory
</
property
>
<!--
SQL dialect
-->
<
property
name
="dialect"
>
org.hibernate.dialect.MySQLDialect
</
property
>
<
property
name
="current_session_context_class"
>
thread
</
property
>
<
property
name
="cache.provider_class"
>
org.hibernate.cache.NoCacheProvider
</
property
>
<
property
name
="show_sql"
>
true
</
property
>
<!--
<property name="hbm2ddl.auto">create</property>
-->
<
mapping
resource
="com/ergal/hibernate/pojo/User.hbm.xml"
/>
<
mapping
resource
="com/ergal/hibernate/pojo/Artist.hbm.xml"
/>
<
mapping
resource
="com/ergal/hibernate/pojo/Category.hbm.xml"
/>
<
mapping
resource
="com/ergal/hibernate/pojo/FileType.hbm.xml"
/>
<
mapping
resource
="com/ergal/hibernate/pojo/MyFile.hbm.xml"
/>
<
mapping
resource
="com/ergal/hibernate/pojo/MyLocation.hbm.xml"
/>
<
mapping
resource
="com/ergal/hibernate/pojo/MyPackage.hbm.xml"
/>
<
mapping
resource
="com/ergal/hibernate/pojo/MyPic.hbm.xml"
/>

</
session-factory
>
</
hibernate-configuration
>
下面写一个类Test.java
package
com.ergal;

import
java.io.File;
import
java.io.IOException;
import
javax.xml.parsers.DocumentBuilder;
import
javax.xml.parsers.DocumentBuilderFactory;
import
javax.xml.parsers.ParserConfigurationException;
import
org.w3c.dom.Document;
import
org.w3c.dom.Element;
import
org.w3c.dom.Node;
import
org.w3c.dom.NodeList;
import
org.w3c.dom.Text;
import
org.xml.sax.SAXException;


public
class
Test

...
{
public static void main(String[] args) throws IOException

...{
try

...{
DocumentBuilderFactory factory=DocumentBuilderFactory.newInstance();
DocumentBuilder builder=factory.newDocumentBuilder();
File f=new File("bin/hibernate.cfg.xml");
//得到文档
Document doc=builder.parse(f);
//得到根元素
Element root=doc.getDocumentElement();
}catch(ParserConfigurationException e)

...{
e.printStackTrace();

}catch (SAXException e) ...{
e.printStackTrace();
}
}
}
这里面的root就是获得的根元素 来看看它的名字是什么 Node里有getNodeName的方法
Element里有 getTagName的方法 在这里都可以用 在后面添加以下代码:
//
获得根元素的名字
String rootElementName
=
root.getNodeName();
//
String rootElementName=root.getTagName();
System.out.println(rootElementName);
Console 里就会打印
hibernate-configuration
现在 要获得hibernate-configuration下的所有子节点 (事实上只有一个) 就要用到 NodeList这个接口和
Element的getChildNodes()这个方法
添加以下代码
//
得到子节点的集合
NodeList childrenNodes
=
root.getChildNodes();
//
得到字节点的个数
int
nodeSize
=
childrenNodes.getLength();
System.out.println(nodeSize);
//
在for中得到各个子节点
for
(
int
i
=
0
; i
<
nodeSize; i
++
)

...
{
Node child=childrenNodes.item(i);
System.out.println(child.getNodeName()+" "+child.getNodeValue());
}
在Console 打印的结果是
3
#text

session-factory
null
#text


很明显了 这就可以解释那些Node的子接口分别的作用了 Elemnet 表示元素 这里只有session-factory
另外其他空白区域的就是 Text 它的nodeName 就是#text Hibernate的session-factory 的nodeValue为null
所有的内容都可以找到相应的一种Node 如 Element Text 还有注释Comment 等等
这些在Node的API中有明确的定义 如下:
包括属性 nodeName
、nodeValue
和 attributes
作为一种获取节点信息的机制,无需向下强制转换为特定的派生接口。在没有对特定的 nodeType
(如 Element
的 nodeValue
或 Comment
的 attributes
)的属性的明显映射的情况下,这将返回 null
。注意,特定的接口可能包含其他更方便的机制来获取和设置相关信息。
nodeName
、nodeValue
和 attributes
的值将根据以下节点类型的不同而不同。
Interface | nodeName | nodeValue | attributes |
---|
Attr | 与 Attr.name 相同 | 与 Attr.value 相同 | null |
CDATASection | "#cdata-section" | 与 CharacterData.data 相同,CDATA 节的内容 | null |
Comment | "#comment" | 与 CharacterData.data 相同,该注释的内容 | null |
Document | "#document" | null | null |
DocumentFragment | "#document-fragment" | null | null |
DocumentType | 与 DocumentType.name 相同 | null | null |
Element | 与 Element.tagName 相同 | null | NamedNodeMap |
Entity | entity name | null | null |
EntityReference | 引用的实体名称 | null | null |
Notation | notation name | null | null |
ProcessingInstruction | 与 ProcessingInstruction.target 相同 | 与 ProcessingInstruction.data 相同 | null |
Text | "#text" | 与 CharacterData.data 相同,该文本节点的内容 | null |
在所有的Node里面 现在看来 对我们有用的只有Element 在这里就是session-factory
因此可以加上一个条件
Node child
=
childrenNodes.item(i);
if
(child
instanceof
Element)

...
{
System.out.println(child.getNodeName());
}
也可以把这个Node转换为Element来分析
这样Console得到的结果就是
因为已经知道一共有3个节点 而且在session-factory这个节点前有#text 后面也是#text 中间才是有用的元素
所以这里可以用另外一种方法来得到session-factory这个节点
Node firstNode
=
root.getFirstChild();
Node secondNode
=
firstNode.getNextSibling();
效果是一样的
现在可以来遍历sessionFactory里的元素
再看看hibernate.cfg.xml中 在session-factory 中 有好几种Node 有注释 有空白 有元素嵌套(或者说元素里面还有)元素
现在这么写来遍历sessionFactory里的元素
//
得到session-factory的子节点的集合
NodeList thirdNode
=
secondNode.getChildNodes();
//
得到子节点的数量
int
tNodeLength
=
thirdNode.getLength();
//
循环得到子节点
for
(
int
j
=
0
; j
<
tNodeLength; j
++
)

...
{
Node thChildNode=thirdNode.item(j);
if(thChildNode instanceof Element)

...{
System.out.println(thChildNode.getNodeName());
}
}
在Console里的结果就是
property
property
property
property
property
property
property
property
property
property
property
property
property
property
property
property
mapping
mapping
mapping
mapping
mapping
mapping
mapping
mapping
这是把空白和注释都过滤掉了的剩下的元素 其实完全可以再细化一下
因为mapping是只有属性的 而property是既有属性也有子节点的(差点写成了子元素)
另:在不知道属性名的情况下是无法获得属性的 Node里的方法getAttributes()返回的是NameNodeMap
这个和NodeList的区别是 只有在有Name的情况下才能访问 就是此集合是通过名称访问节点的集合
而Element里的方法也都需要知道属性名 难道就只有一直 if else 下去来遍历
虽然hibernate是用的Dom4j 我还是看了一下Hibernate的源代码
在Configuration有如下一个方法
private
void
addProperties(Element parent)
...
{
Iterator iter = parent.elementIterator( "property" );

while ( iter.hasNext() ) ...{
Element node = (Element) iter.next();
String name = node.attributeValue( "name" );
String value = node.getText().trim();
log.debug( name + "=" + value );
properties.setProperty( name, value );

if ( !name.startsWith( "hibernate" ) ) ...{
properties.setProperty( "hibernate." + name, value );
}
}
Environment.verifyProperties( properties );
}
这样把键值都取出来再交给parseSessionFactory()方法去解析
parseSessionFactory()的代码如下
private
void
parseSessionFactory(Element sfNode, String name)
...
{
Iterator elements = sfNode.elementIterator();

while ( elements.hasNext() ) ...{
Element subelement = (Element) elements.next();
String subelementName = subelement.getName();

if ( "mapping".equals( subelementName ) ) ...{
parseMappingElement( subelement, name );
}

else if ( "class-cache".equals( subelementName ) ) ...{
String className = subelement.attributeValue( "class" );
Attribute regionNode = subelement.attribute( "region" );
final String region = ( regionNode == null ) ? className : regionNode.getValue();
boolean includeLazy = !"non-lazy".equals( subelement.attributeValue( "include" ) );
setCacheConcurrencyStrategy( className, subelement.attributeValue( "usage" ), region, includeLazy );
}

else if ( "collection-cache".equals( subelementName ) ) ...{
String role = subelement.attributeValue( "collection" );
Attribute regionNode = subelement.attribute( "region" );
final String region = ( regionNode == null ) ? role : regionNode.getValue();
setCollectionCacheConcurrencyStrategy( role, subelement.attributeValue( "usage" ), region );
}

else if ( "listener".equals( subelementName ) ) ...{
parseListener( subelement );
}

else if ( "event".equals( subelementName ) ) ...{
parseEvent( subelement );
}
}
}
然后在把不同的元素 交给不同的处理器 比如 mapping就用parseMappingElement( subelement, name );
也是用if else的方法来取得值的 看来我的想的也八九不离十
好 回到我的例子中 我就不一一按属性的名来获得值了 就直接获得所有的元素的值 只有属性的mapping就可以很方便的获得属性值
于是修改如下 并且根据Text的getData 方法(确切的说是CharactorData的方法)等 来获取每个元素的属性值和子节点的text
//
得到session-factory的子节点的集合
NodeList thirdNode
=
secondNode.getChildNodes();
//
得到子节点的数量
int
tNodeLength
=
thirdNode.getLength();
//
循环得到子节点
for
(
int
j
=
0
; j
<
tNodeLength; j
++
)

...
{
Node thChildNode=thirdNode.item(j);
//得到有子节点的元素
if(thChildNode instanceof Element && thChildNode.hasChildNodes())

...{
//将节点转换为元素
Element thChildElement=(Element)thChildNode;
//获得text
Text textNode=(Text)thChildElement.getFirstChild();
//得到该元素的值 这里的trim方法可以去掉前后的空白
String text=textNode.getData().trim();
System.out.println(text);
}
//得到没有子节点 只有属性的mapping元素
else if(thChildNode instanceof Element && !thChildNode.hasChildNodes())

...{
//将节点转换为元素
Element thChildElement=(Element)thChildNode;
//获得属性的值
String text=thChildElement.getAttribute("resource");
System.out.println(text);
}
}
这样Console的结果就是
com.mysql.jdbc.Driver
jdbc:mysql://localhost:3306/sshtest
root
00000000
org.hibernate.connection.C3P0ConnectionProvider
20
5
120
100
120
2
org.hibernate.transaction.JDBCTransactionFactory
org.hibernate.dialect.MySQLDialect
thread
org.hibernate.cache.NoCacheProvider
true
com/ergal/hibernate/pojo/User.hbm.xml
com/ergal/hibernate/pojo/Artist.hbm.xml
com/ergal/hibernate/pojo/Category.hbm.xml
com/ergal/hibernate/pojo/FileType.hbm.xml
com/ergal/hibernate/pojo/MyFile.hbm.xml
com/ergal/hibernate/pojo/MyLocation.hbm.xml
com/ergal/hibernate/pojo/MyPackage.hbm.xml
com/ergal/hibernate/pojo/MyPic.hbm.xml
这些是最基本的DOM解析方法