`

XML基础 (1)

 
阅读更多

学习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中得到

方法

DocumentBuilderFactoryfactory=DocumentBuilderFactory.newInstance();
DocumentBuilderbuilder
=factory.newDocumentBuilder();

然后就能获得一个Document对象

Document是XML的树型结构的具体形式的表现 由实现Node接口及多个子接口的对象构成

得到一个Document

Filef=newFile("bin/hibernate.cfg.xml");

Documentdoc
=builder.parse(f);

一些其他的构造方法(DocumentBuilder)

abstract Document newDocument()
获取 DOM Document 对象的一个新实例来生成一个 DOM 树。
Document parse(Filef)
将给定文件的内容解析为一个 XML 文档,并且返回一个新的 DOM Document 对象。
abstract Document parse(InputSourceis)
将给定输入源的内容解析为一个 XML 文档,并且返回一个新的 DOM Document 对象。
Document parse(InputStreamis)
将给定 InputStream 的内容解析为一个 XML 文档,并且返回一个新的 DOM Document 对象。
Document parse(InputStreamis, StringsystemId)
将给定 InputStream 的内容解析为一个 XML 文档,并且返回一个新的 DOM Document 对象。
Document parse(Stringuri)
将给定 URI 的内容解析为一个 XML 文档,并且返回一个新的 DOM Document 对象。

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的

<?xmlversion='1.0'encoding='UTF-8'?>
<!DOCTYPEhibernate-configurationPUBLIC
"-//Hibernate/HibernateConfigurationDTD3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd"
>

<!--GeneratedbyMyEclipseHibernateTools.-->
<hibernate-configuration>
<session-factory>
<propertyname="connection.driver_class">com.mysql.jdbc.Driver</property>
<propertyname="connection.url">jdbc:mysql://localhost:3306/sshtest</property>
<propertyname="connection.username">root</property>
<propertyname="connection.password">00000000</property>

<!--JDBCconnectionpool-->
<propertyname="hibernate.connection.provider_class">org.hibernate.connection.C3P0ConnectionProvider</property>
<propertyname="hibernate.c3p0.max_size">20</property>
<propertyname="hibernate.c3p0.min_size">5</property>
<propertyname="hibernate.c3p0.timeout">120</property>
<propertyname="hibernate.c3p0.max_statements">100</property>
<propertyname="hibernate.c3p0.idle_test_period">120</property>
<propertyname="hibernate.c3p0.acquire_increment">2</property>

<propertyname="hibernate.transaction.factory_class">org.hibernate.transaction.JDBCTransactionFactory</property>

<!--SQLdialect-->
<propertyname="dialect">org.hibernate.dialect.MySQLDialect</property>
<propertyname="current_session_context_class">thread</property>
<propertyname="cache.provider_class">org.hibernate.cache.NoCacheProvider</property>
<propertyname="show_sql">true</property>
<!--<propertyname="hbm2ddl.auto">create</property>-->

<mappingresource="com/ergal/hibernate/pojo/User.hbm.xml"/>
<mappingresource="com/ergal/hibernate/pojo/Artist.hbm.xml"/>
<mappingresource="com/ergal/hibernate/pojo/Category.hbm.xml"/>
<mappingresource="com/ergal/hibernate/pojo/FileType.hbm.xml"/>
<mappingresource="com/ergal/hibernate/pojo/MyFile.hbm.xml"/>
<mappingresource="com/ergal/hibernate/pojo/MyLocation.hbm.xml"/>
<mappingresource="com/ergal/hibernate/pojo/MyPackage.hbm.xml"/>
<mappingresource="com/ergal/hibernate/pojo/MyPic.hbm.xml"/>

</session-factory>
</hibernate-configuration>

下面写一个类Test.java

packagecom.ergal;

importjava.io.File;
importjava.io.IOException;
importjavax.xml.parsers.DocumentBuilder;
importjavax.xml.parsers.DocumentBuilderFactory;
importjavax.xml.parsers.ParserConfigurationException;
importorg.w3c.dom.Document;
importorg.w3c.dom.Element;
importorg.w3c.dom.Node;
importorg.w3c.dom.NodeList;
importorg.w3c.dom.Text;
importorg.xml.sax.SAXException;


publicclassTest
...{
publicstaticvoidmain(String[]args)throwsIOException
...{
try
...{
DocumentBuilderFactoryfactory
=DocumentBuilderFactory.newInstance();
DocumentBuilderbuilder
=factory.newDocumentBuilder();

Filef
=newFile("bin/hibernate.cfg.xml");

//得到文档
Documentdoc=builder.parse(f);

//得到根元素
Elementroot=doc.getDocumentElement();


}
catch(ParserConfigurationExceptione)
...{
e.printStackTrace();
}
catch(SAXExceptione)...{
e.printStackTrace();
}

}

}

这里面的root就是获得的根元素 来看看它的名字是什么 Node里有getNodeName的方法

Element里有 getTagName的方法 在这里都可以用 在后面添加以下代码:

//获得根元素的名字
StringrootElementName=root.getNodeName();
//StringrootElementName=root.getTagName();
System.out.println(rootElementName);

Console 里就会打印

hibernate-configuration

现在 要获得hibernate-configuration下的所有子节点 (事实上只有一个) 就要用到 NodeList这个接口和

Element的getChildNodes()这个方法

添加以下代码

//得到子节点的集合
NodeListchildrenNodes=root.getChildNodes();

//得到字节点的个数
intnodeSize=childrenNodes.getLength();

System.out.println(nodeSize);
//在for中得到各个子节点
for(inti=0;i<nodeSize;i++)
...{
Nodechild
=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中有明确的定义 如下:

包括属性 nodeNamenodeValueattributes 作为一种获取节点信息的机制,无需向下强制转换为特定的派生接口。在没有对特定的 nodeType(如 ElementnodeValueCommentattributes)的属性的明显映射的情况下,这将返回 null。注意,特定的接口可能包含其他更方便的机制来获取和设置相关信息。

nodeNamenodeValueattributes 的值将根据以下节点类型的不同而不同。

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

因此可以加上一个条件

Nodechild=childrenNodes.item(i);
if(childinstanceofElement)
...{
System.out.println(child.getNodeName());
}


也可以把这个Node转换为Element来分析

这样Console得到的结果就是

session-factory

因为已经知道一共有3个节点 而且在session-factory这个节点前有#text 后面也是#text 中间才是有用的元素

所以这里可以用另外一种方法来得到session-factory这个节点

NodefirstNode=root.getFirstChild();
NodesecondNode
=firstNode.getNextSibling();

效果是一样的

现在可以来遍历sessionFactory里的元素

再看看hibernate.cfg.xml中 在session-factory 中 有好几种Node 有注释 有空白 有元素嵌套(或者说元素里面还有)元素

现在这么写来遍历sessionFactory里的元素

//得到session-factory的子节点的集合
NodeListthirdNode=secondNode.getChildNodes();
//得到子节点的数量
inttNodeLength=thirdNode.getLength();
//循环得到子节点
for(intj=0;j<tNodeLength;j++)
...{
NodethChildNode
=thirdNode.item(j);
if(thChildNodeinstanceofElement)
...{
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有如下一个方法

privatevoidaddProperties(Elementparent)...{
Iteratoriter
=parent.elementIterator("property");
while(iter.hasNext())...{
Elementnode
=(Element)iter.next();
Stringname
=node.attributeValue("name");
Stringvalue
=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()的代码如下

privatevoidparseSessionFactory(ElementsfNode,Stringname)...{
Iteratorelements
=sfNode.elementIterator();
while(elements.hasNext())...{
Elementsubelement
=(Element)elements.next();
StringsubelementName
=subelement.getName();
if("mapping".equals(subelementName))...{
parseMappingElement(subelement,name);
}

elseif("class-cache".equals(subelementName))...{
StringclassName
=subelement.attributeValue("class");
AttributeregionNode
=subelement.attribute("region");
finalStringregion=(regionNode==null)?className:regionNode.getValue();
booleanincludeLazy=!"non-lazy".equals(subelement.attributeValue("include"));
setCacheConcurrencyStrategy(className,subelement.attributeValue(
"usage"),region,includeLazy);
}

elseif("collection-cache".equals(subelementName))...{
Stringrole
=subelement.attributeValue("collection");
AttributeregionNode
=subelement.attribute("region");
finalStringregion=(regionNode==null)?role:regionNode.getValue();
setCollectionCacheConcurrencyStrategy(role,subelement.attributeValue(
"usage"),region);
}

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

elseif("event".equals(subelementName))...{
parseEvent(subelement);
}

}

}

然后在把不同的元素 交给不同的处理器 比如 mapping就用parseMappingElement( subelement, name );

也是用if else的方法来取得值的 看来我的想的也八九不离十

好 回到我的例子中 我就不一一按属性的名来获得值了 就直接获得所有的元素的值 只有属性的mapping就可以很方便的获得属性值

于是修改如下 并且根据Text的getData 方法(确切的说是CharactorData的方法)等 来获取每个元素的属性值和子节点的text

//得到session-factory的子节点的集合
NodeListthirdNode=secondNode.getChildNodes();
//得到子节点的数量
inttNodeLength=thirdNode.getLength();
//循环得到子节点
for(intj=0;j<tNodeLength;j++)
...{
NodethChildNode
=thirdNode.item(j);
//得到有子节点的元素
if(thChildNodeinstanceofElement&&thChildNode.hasChildNodes())
...{
//将节点转换为元素
ElementthChildElement=(Element)thChildNode;
//获得text
TexttextNode=(Text)thChildElement.getFirstChild();
//得到该元素的值这里的trim方法可以去掉前后的空白
Stringtext=textNode.getData().trim();
System.out.println(text);
}

//得到没有子节点只有属性的mapping元素
elseif(thChildNodeinstanceofElement&&!thChildNode.hasChildNodes())
...{
//将节点转换为元素
ElementthChildElement=(Element)thChildNode;
//获得属性的值
Stringtext=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解析方法

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics