一种表示结构化信息的标准方法,以使计算机能够方便地使用此类信息,并且人们可以非常方便地编写和理解这些信息。XML 是 eXtensible Markup Language(可扩展标记语言)的缩写。www.w3.org/XML/ 上提供了 XML 标准。XML 提供了一种简便的标准方法对数据进行分类,以使其更易于读取、访问以及处理。XML 使用类似于 HTML 的树结构和标签结构.
zhangsan zhangsan@hotmail.com 2013-1-1
处理XML文档的程序应该容易编写Java与xml是一种完美的组合,Java平台是一种跨平台的编程环境 ,XML是一种跨平台的数据格式 与其他语言相比,Java平台提供了更好的XML支持
在XML中,要求可供选择的特性数量保持绝对的少,更理想一点一个也没有
XML文档应当是可读性强和条理清晰的
XML的设计应当是正规并且简洁的
XML文档应该容易创建
在XML的结构中应该简洁、精练
在Internet上XML应该是直接可以使用的
XML应该支持各种各样的应用
存储数据:最基本的办法是采用io流读取XML与Access,Oracle和SQL Server等数据库不同,数据库提供了更强有力的数据存储和分析能力,例如:数据索引、排序、查找、相关一致性等,XML仅仅是展示数据。事实上XML与其他数据表现形式最大的不同是:他极其简单。
配置文件(用得最多)
支持设计与特定领域相关的标记不需要借助于其它的工具就可以理解XML中数据的含义,是因为XML中对数据进行了自描述,这保证了只要XML文件本身不被破坏就可以理解所包含数据的含义。同时,XML有很好的规格文档,保证了XML的语义不被误解。
自描述数据
无效文档:
没有遵守 XML 规范定义的语法规则。如果开发人员已经在 DTD 或模式中定义了文档能够包含什么,而某个文档没有遵守在其 DTD 或模式中定义的规则
有效文档:既遵守 XML 语法规则也遵守在其 DTD 或模式中定义的规则。
格式良好(well-formed)的文档:遵守 XML 语法,但没有 DTD 或模式。
<代表小于符号
> 代表大于符号
" 代表一个双引号
' 代表一个单引号(或撇号)
& 代表一个“与”符号&。
为了避免 XML 解析错误,我们应该使用 <来替代 <,使用 & 替代 &。但是假设你需要在 XML 文档里写一段内容,里面包含了很多 <或者 &,要将所有 <或者 & 转换成实体引用是很讨厌的事情。
这时候,你可以使用 CDATA 区 (CDATA section)。在 CDATA 区里,你可以不必使用实体引用,因为 XML 解析器不会解析 CDATA 区内的内容。
CDATA 区 (CDATA section) 以 结束。 示例如下:
This is a html page
I like
]]>
注意:在 CDATA 区内,不能出现 ]]>
如果文档是一个"有效的XML文档",那么文档一定要有相应DTD文件,并且严格遵守DTD文件制定的规范。DTD为英文Document Type Definition,中文意思为“文档类型定义”,用于定义这个文档的具体类型,不通场合的xml类型不一样,具体用那一种,要通过DTD定义,所以使用时要指明相应的DTD文件。
DTD的作用:
一方面它帮助你编写合法的代码 ,另一方面它让浏览器正确地显示器代码。
DTD文件的声明语句紧跟在XML声明语句后面,格式如下:
其中:
"!DOCTYPE"是指你要定义一个DOCTYPE; "type-of-doc"是文档类型的名称,由你自己定义; "SYSTEM/PUBLIC"这两个参数只用其一。SYSTEM是指文档使用的私有DTD文件的网址,而PUBLIC则指文档调用一个公用的DTD文件的网址。 "dtd-name" 就是DTD文件的网址和名称。所有DTD文件的后缀名为".dtd"。
例如用上面的例子,可写成这样:
上面的声明表示:
该xml文件版本是1.0,引用了其他文件,文档字符编码是UTF-8DTD文档类型名称是filelist,使用的是私有DTD文件的网址,DTD文件为filelist.dtd
2、区分大小写(case-sensitive)
在XML文档中,大小写是有区别的。
和
是不同的标识。注意在写元素时,前后标识大小写要保持一样。例如:
ajie ,写成ajie 是错误的。
3、属性值
在XML中则规定,所有属性值必须加引号(可以是单引号,也可以是双引号),否则将被视为错误。
属性名的第一个字母必须以字母或下划线开头
在同一个开始标记里,一个特殊的属性名只能出现一次
4、所有的标识必须有相应的结束标识
在XML中规定,所有标识必须成对出现,有一个开始标识,就必须有一个结束标识。
5、所有的空标识也必须被关闭
空标识就是标识对之间没有内容的标识。在XML中,规定所有的标识必须有结束标识,针对这样的空标识,XML中处理的方法是在原标识最后加“/”。例如:
应写为
;
层次清晰,便于存取其中的节点。
缺点:
DOM 构建整个文档驻留内存的树。如果文档很大,就会要求有极大的内存。
DOM 创建表示原始文档中每个东西的对象,包括元素、文本、属性和空格。如果您只需关注原始文档的一小部分,那么创建那些永远不被使用的对象是极其浪费的。
DOM 解析器必须在您的代码取得控制权之前读取整个文档。对于非常大的文档,这会引起显著的延迟。
DOM 解析器把 XML 文档转化为一个包含其内容的树,并可以对树进行遍历。然后利用navigation APIs访问所需的树节点来完成任务。可以很容易的添加和修改树中的元素。然而由于使用 DOM 解析器的时候需要处理整个 XML 文档,所以对性能和内存的要求比较高,尤其是遇到很大的 XML 文件的时候。由于它的遍历能力,DOM 解析器常用于 XML 文档需要频繁的改变的服务中。
2、SAX
解析器采用了基于事件的模型,侧重过程
优点:
快速、适合大文件。
缺点:
编程较难,仅用于串行存取,由于应用程序不以任何方式存储数据,所以,使用 SAX 时,不可能对数据进行更改。
SAX(Simple API for XML) 不是某个官方标准,但它是 XML 社区事实上的标准,几乎所有的 XML 解析器都支持它。SAX 解析器采用了基于事件的模型,它在解析 XML 文档的时候可以触发一系列的事件,当发现给定的tag(标签)的时候,它可以激活一个回调方法,告诉该方法制定的标签已经找到。
由于应用程序简单地检查经过其的数据,所以不需要将数据存储在内存里。当遇到大文档时,这是一个突出的优势。SAX 对内存的要求通常会比较低,因为它让开发人员自己来决定所要处理的tag。特别是当开发人员只需要处理文档中所包含的部分数据时,SAX 这种扩展能力得到了更好的体现。但用 SAX 解析器的时候编码工作会比较困难,而且很难同时访问同一个文档中的多处不同数据。
选择 DOM 还是 SAX,这取决于几个因素:
应用程序的目的:如果必须对数据进行更改,并且作为 XML 将它输出,则在大多数情况下,使用 DOM。
数据的数量:对于大文件,SAX 是更好的选择。
将如何使用数据:如果实际上只使用一小部分数据,则使用 SAX 将数据抽取到应用程序中,这种方法更好些。
需要速度:通常,SAX 实现比 DOM 实现快。
3、JDOM
注意JDOM的J是Java的意思,决不是DOM扩展,虽然名字差不多,但两者平行的关系。与DOM主要有两方面不同:
首先,JDOM仅使用具体类而不使用接口。这在某些方面简化了API,但是也限制了灵活性。
第二,API大量使用了Collections类,简化了那些已经熟悉这些类的Java开发者的使用。是一个开源项目,它基于树型结构,利用纯JAVA的技术对XML文档实现解析、生成、序列化以及多种操作。JDOM的目的是成为Java特定文档模型,它简化与XML的交互并且比使用DOM实现更快 JDOM 自身不包含解析器。它通常使用 SAX2 解析器来解析和验证输入 XML 文档(尽管它还可以将以前构造的 DOM 表示作为输入)。
4、DOM4J
具有性能优异、功能强大和使用便捷的特点,同时它也是一个开放源代码的软件。如今你可以看到越来越多的Java软件都在使用DOM4J来读写XML,特别值得一提的是连Sun的JAXM也在用DOM4J。 目前许多开源项目中大量采用DOM4J,例如Hibernate也用DOM4J来读取XML配置文件。
dom4j是一个Java的XML API,类似于jdom,用来读写XML文件的。Dom4j是一个易用的、开源的库,用于XML,XPath和XSLT。它应用于Java平台,采用了Java集合框架并完全支持DOM,SAX和JAXP。
2、 DOM4J的API文档
从网上可以下载DOM4J的API帮助文档、jar包(如dom4j-1.6.1.jar)、开源代码。其帮助文档首页如下:
Home是首页,FAQ是常见问题,Quick start是快速入门,主要说明dom4j的基本使用方法,并列举了一写简单的例子。包括如何得到一个xml文档对应的Document对象,(在Java编程环境中模拟的一个文档),通过迭代器(Iterator)遍历xml树,快速循环,创建XML文档,把一个document写到文件中等。Javadoc(1.6.1)就是对应的dom4j对应的类似Java JDK帮助文档一样文档。里面特别常用的接口以斜体方式显示。如Node、Document、Elemet等。
3、 dom4j中常用的接口概述
它的主要接口都在org.dom4j这个包里定义:
Attribute 接口定义了XML的属性
Branch接口为能够包含子节点的节点如XML元素(Element)和文档(Docuemnts)定义了一个公共的行为,
CDATA接口定义了XML CDATA 区域 ,次区域的大于小于等符号不会被当做标签的符号来读取。
CharacterData接口是一个标识借口,标识基于字符的节点。如CDATA,Comment, Text.
Comment接口定义了XML注释的行为
Document 定义了XML文档
DocumentType接口定义XML DOCTYPE声明
Element Element定义XML 元素
ElementHandler接口定义了 Element 对象的处理器
ElementPath 被 ElementHandler 使用,用于取得当前正在处理的路径层次信息
Entity接口定义xml实体:XML entity
Node接口为所有的dom4j中XML节点定义了多态行为
NodeFilter接口定义了在dom4j节点中产生的一个滤镜或谓词的行为(predicate)
ProcessingInstruction接口定义 XML 处理指令.
Text接口定义XML 文本节点.
Visitor接口用于实现Visitor模式.
XPath接口在分析一个字符串后会提供一个XPath 表达式
常用接口的继承关系:
java.lang.Cloneable
Node
DocumentType
Attribute
ProcessingInstruction
Entity
Branch
Document
Element
CharacterData
CDATA
Text
Comment
可见,上面关系中,大多都是Node的子节点,这些节点都来自java.org.dom4包中。
4、 Dom4j常用接口、类说明
1) Node接口
asXML():隶属于Node类,用于将XML转换为String
public Node selectSingleNode(String xpathExpression)可以通过xpath路径返回一个节点。若xpathExpression为"/users/user[id='108']",则得到的是user节点,它有一个子元素id,标签id的值为108.若xpathExpression为"/users/user[@firstname='liu']",则会得到一个user节点,它有个属性是firstname,属性值为liu。
public List selectNodes(String xpathExpression)可以返回所有xpath路径指定的节点
2) SAXReader接口
此接口通过SAX解析事件创建一个DOM4J的SAXReader对象树。常用其无参构造方法SAXReader()。
public Document read(InputStream in)此方法可以读取一个输入流in,返回一个Java环境下文档Document类型的对象。SAXReader的read方法是重载的,可以从InputStream, File, Url等多种不同的源来读取。得到的Document对象就带表了整个XML。读取的字符编码是按照XML文件头定义的编码来转换。如果遇到乱码问题,注意要把各处的编码名称保持一致即可。
3) Document接口
此接口是Node的子接口,用于定义一个XML文档,没有自己的构造方法,有两种方法得到Document对象:一是从SAXReader的read方法读流得到,二是由DocumentHelper类的静态方法createDocument得到。
public Element getRootElement() 此方法可以得到这个Document的根元素,也就是树的根。
public Element addElement(String name)此方法是从Document的父类Node那里继承而来的, 可以给Document添加一个名为name元素,并返回这个元素。
public void add(Element element)此方法也是从Node那里继承而来,可以添加一个元素到该document的分支上。如果传进来的元素已经有了父节点,就会抛出非法异常。
4) Element接口
此接口是Node、Branch的子接口,用于定义一个XML元素,也就是树的节点,一个元素可以设置命名空间,属性,子节点和文本内容。
public Element element(String name) 此方法可以得到该元素下面名字为name的子元素
public List elements() 此方法可以得到一个存储该元素下面所有元素的List表。
public String getText() 此方法可以返回该元素的文本内容,如果内容为空则返回一个空字符串而不是null。
public String getTextTrim() 返回该元素所含有的text内容,其中连续的空格被转化为单个空格,该方法不会返回null
public String attributeValue(String name) 此方法可以得到该元素名为name的属性的值,如果xml文档中没有指明该属性,就返回其默认值。
public String attributeValue(String name,String defaultValue)该方法返回该元素名为name属性的值,如果xml文档中没有指明,就返回传给此方法的dafaultValue,这可能并不是该属性的默认值。
public Element addAttribute(String name,String value)此方法可以给该元素名为name的属性设置值,并覆盖该属性先前的值。返回该元素。
部分其他方法简介
MethodComment
getQName()元素的QName对象
getNamespace()元素所属的Namespace对象
getNamespacePrefix()元素所属的Namespace对象的prefix
getNamespaceURI()元素所属的Namespace对象的URI
getName()获取该元素的local name
getQualifiedName()元素的qualified name
attributeIterator()元素属性的iterator,其中每个元素都是Attribute对象
attributeValue()元素的某个指定属性所含的值
elementIterator()元素的子元素的迭代器Iterator,其中每个元素都是Element对象
element()元素的某个指定(qualified name或者local name)的子元素
elementText()元素的某个指定(qualified name或者local name)的子元素中的text信息
getParent元素的父元素
getPath()元素的XPath表达式,其中父元素的qualified name和子元素的qualified name之间使用"/"分隔
isTextOnly()是否该元素只含有text或是空元素
detach() 移除自己
isRootElement()是否该元素是XML树的根节点
selectNodes(String xpathExpression)通过XPATH获取节点。
selectSingleNode(String xpathExpression)通过XPATH获取一个节点。
getDocument()作为一个Document返回
5) OutputFormat类
问题:把做好的xml文档直接用流输出,输出的方式默认为紧凑的,才用UTF-8编码,遇到中文字符就会显示为乱码。如何用很清晰的缩进的格式显示呢。
说明:此类位于java.org.dom4j.io包中,用于格式化XML文档的输出。
此类的使用方式是将此类的对象作为参数传递给XMLWriter类的构造方法,从而构建一个XML输出流类,调用其write方法进行输出。
public static OutputFormat createPrettyPrint()此静态方法不是OutputFormat的构造方法,但该方法可以创建一个设定好XML输出格式的OutputFormat对象。此默认格式就是,元素标签之间换行,并且子元素与父元素间产生2个空格的缩进,且换行、缩进中空的部分都是空白。
public void setIndent(String indent) 该方法可以设置该类对象的XML文档输出时,子元素与父元素间缩进填充的字符串indent。字符串indent将会显示在XML文档子元素与父元素间缩进的位置。
public void setEncoding(String encoding)此方法可以设定输出的XML文档的编码方式,如果是GBK就可正确显示文档的中文信息。
6) XMLWriter类
此类是dom4j.io中的类,可以得到一个dom4j树,把他按xml文件格式的流。其有参构造方法可以接受一个设定好xml文档格式的OutputFormat,并按照这个OutputFormat给定格式输出xml文档。写入文件后,要调用此类的flush方法刷新缓冲区,在关闭流。
public XMLWriter(OutputStream out, OutputFormat format)
给构造方法,就收一个输出流对象和一个OutputForemat对象。out可以指定XMLWriter输出XML文档的地方,format指定输出格式。
write方法
此方法进行了多次重载,可以写Document、Element、String等等。
7) DocumentHelper
createDocument():创建一个Document对象
parseText(String text):解析给定Xml的文本,生成Document对象
5、 用DOM4解析技术操作xml文档
1) 几种存储方式:
普通txt文件------存取不便
数据库 -----------没必要
技术 -------------结构清晰,层次明了
Xml与数据库中表的关系:
根元素-----表名
子元素-----记录
子元素中的子元素---------字段
2) 生成xml文档:
得到文件输出流:新建File对象,创建FileWriter输出流
设置生成文档格式:创建OutputFromat对象设置
创建输出流:创建XMLWriter,构造方法得到输出流、设定的格式
写文件到磁盘:把已有的Document对象通过XMLWriter的write方法写入磁盘
刷新、关闭流
3) 获取xml文档:
找到文件:通过磁盘xml文件,new一个File对象
打开文件:通过得到的文件对象创建一个InputStream
把InputStream变成树:创建一个SAXReader对象,通过其read方法读文件对象或输入流对象得到一个Document对象
4) 查询xml文档的叶子节点
找树根:Document的getRootElement方法可以得到SAXReader创建的树的类型为Element的根节点。
找树枝:Element接口中的elements()可以找到该元素下的所有节点
找树叶:用element方法可以找到指定名称的子节点
关闭流
Xml path简称xpath
问题:查找过程浪费时间在一些中间节点,如果中间节点过多则性能下降。
定义:由xml中的节点或者属性构成的序列。
XPath是用来搜索和查询XML文档,以得到与给定标准相匹配的节点列表的语言。它已经被W3C标准化了。一个XPath语言的表达式可详细说明要匹配的位置和模式。将布尔运算符、字符串运算符和算术运算符应用到XPath语言的表达式中,能够建立极其复杂的查询.
作用:根据条件(某个节点或者属性)快速定位某个节点。
关系:
Users----user-----id
/users/user[id='108']
借助子节点的值来定位父节点
条件是id的值
定位的节点是user
/users/user[@firstname='liu']
借助节点属性值来定位所在的节点。
XPath语言的特性
能找到当前节点的所有子节点
能找到具有特定标志的当前上下文节点的所有祖先节点
能找到具有特定标志的当前节点的最后一个子元素
根据一个给定属性的元素的当前上下文节点找到第n个元素
能找到具有
或 标志的第一个子元素 能找到不具有某个属性的元素的所有子节点
能找到所有数字元素子节点的总和
能找到所有子节点的数量
待解析的xml:
119
刘晓东
computer
34567
1997-08-01
USA
m
121
晓东
computer
34567
1997-08-01
USA
m
120
刘晓东
computer
34567
1997-08-01
USA
m
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.InputStream;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.SAXReader;
import org.dom4j.io.XMLWriter;
/**对Document的一些操作都放在此类管理
* 1.通过xml文件得到Document对象
* 2.对已有的Document对象,把他按照我们的格式写到磁盘中
*/
public class DocumentManager {
private static String fileName = "src/Users.xml";
public static Document getDocument(){
return getDocument(fileName);
}
public static Document getDocument(String fileName){
try {
File file = new File(fileName);
InputStream is = new FileInputStream(file);
SAXReader reader = new SAXReader();
Document document = reader.read(is);
return document;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public static void writeDocument(Document document){
writeDocument(document,fileName);
}
public static void writeDocument(Document document,String filename){
//1.set the format
OutputFormat format = OutputFormat.createPrettyPrint();
//2.set the indent to 4 white spaces.
format.setIndent(" ");
try {
XMLWriter writer = new XMLWriter(new FileOutputStream(filename),format);
writer.write(document);
writer.flush();
writer.close();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static void main(String[] args) {
getDocument();
}
}
import java.sql.Date;
/**创建一个程序中实体对应xml中的信息,更加人性化,用户也更容易
* 理解 当然这就需要xml中的东西与实体间进行转换,包含对象、操作等
* 1.创建一个用户类User,对应xml中的Student,用户的属性对应xml中Student所有子元素
* 2.实体的各属性都要有set、get方法,以便设置、获取属性值
* 3.尤其是对xml的文档的元素进行排序,如果用xml里面的元素排,在排的时候要把元素从他们的父节点上移除
* 添加的时候还要设置被被添加的子元素的父节点为null,很是麻烦,用把Element转换成为User对象,排好了序
* 再写到原来的xml文档,这样就简单很多。
* 4.重写toString方法便于查看属性
*/
public class User {
private Integer id = null;
private String username = null;
private String password = null;
private Date birth = null;
private String address = null;
private String gender = null;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public Date getBirth() {
return birth;
}
public void setBirth(Date birth) {
this.birth = birth;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
@Override
public String toString(){
return "id=" + id + "\tusername=" + username + "\tpassword=" + password
+ "\tgender="+gender+"\taddress"+address+"\tbirth="+birth;
}
}
import java.util.List;
/**创建一个接口,让人一步了然的看清有什么方法,做了写什么事,
* 还可以实现接口
*/
public interface IUserDAO {
public ListgetAllUser();
public User getUserByUsername(String username);
public ListgetUserListByUsername(String username);
public ListgetUserListByKeyword1(String keyword);
public ListgetUserListByKeyword2(String keyword);
public boolean addUser(User user);
public boolean addUserWithUniquePK(User user)throws UserExistsException;
public boolean addUserWithAutoPK(User user);
public boolean deleteUserByPK(int id);
public boolean update(User user);
public void sortUserList(ListuserList);
public void sortUserList(ListuserList,boolean ascendable);
public void testXPath1();
public User getUserById(int id);
public void sortById1();
public void sortById2();
public void sortById3();
public void sortById4();
}
import java.sql.Date;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;
public class UserDAO implements IUserDAO {
/**
* 把一个Element对象转换成一个User对象
* 1.得到Element各子元素存储的信息
* 2.把这些String类型的信息转换成相应的类型后赋值给User对象的属性
* 3.返回User对象
*/
private User getObjectFromElement(Element userElement) {
String idStr = userElement.elementTextTrim("id");
String username = userElement.elementTextTrim("username");
String birthStr = userElement.elementTextTrim("birth");
String gender = userElement.elementTextTrim("gender");
String password = userElement.elementTextTrim("password");
String address = userElement.elementTextTrim("address");
Integer id = Integer.parseInt(idStr);
// convert a string in the format "yyyy-mm-dd" into a Date type.
Date birth = Date.valueOf(birthStr);
User user = new User();
user.setId(id);
user.setUsername(username);
user.setPassword(password);
user.setBirth(birth);
user.setAddress(address);
user.setGender(gender);
return user;
}
// 通过指定的用户名得到xml中第一个与指定用户名相同的用户对应的User对象
public User getUserByUsername(String username) {
try {
Document document = DocumentManager.getDocument();
Element usersElement = document.getRootElement();
ListuserElementList = usersElement.elements();
for (Element userElement : userElementList) {
Element usernameElement = userElement.element("username");
String _username = usernameElement.getText();
if (username.equals(_username) == false) {
continue;
}
return this.getObjectFromElement(userElement);
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public ListgetAllUser() {
return null;
}
/**
* 通过指定的字符串查找到xml文件中用户名中包含此字符串的所有User对象
* 1.得到xml对应的Docuement对象,寻找到xml中所有的Student元素
* 2.创建一个存储User对象的List列表
* 3.让Student的子元素也就是叶子节点存储的信息与我们想找的username比较
* 4.把匹配的转换成为User对象后添加到List中
* 5.返回这个List
*/
@Override
public ListgetUserListByUsername(String username) {
Document document = DocumentManager.getDocument();
Element usersElement = document.getRootElement();
ListuserElementList = usersElement.elements();
ListuserList = new ArrayList ();
// 创建一个ArrayList,这样的数组列表有序、可以重复、长度可变,
// 用于存储我们找到的User对象
for (Element userElement : userElementList) {
String _username = userElement.elementTextTrim("username");
if (_username.contains(username)) {
User user = this.getObjectFromElement(userElement);
userList.add(user);
}
}
return userList;
}
/**
* 查询所有包含我们指定字符串内容信息的用户,并转换成User对象返回
* 1.创建一个存储User对象的ArrayList
* 2.找到存有所有用户元素列表,循环找到各个user节点
* 2.找到其字节点叶子节点,看他们的内容是否包含我们要查询的信息keywword
* 3.如果遇到任何一个叶子结点包含,就不再找这个user的后面元素了
* 4.满足条件的user节点转换成user对象,添加到List中
* 5.返回List
*/
public ListgetUserListByKeyword1(String keyword) {
Document document = DocumentManager.getDocument();
Element usersElement = document.getRootElement();
ListuserElementList = usersElement.elements();
ListuserList = new ArrayList ();
for (Element userElement : userElementList) {
String _username = userElement.elementTextTrim("username");
if (_username.contains(keyword)) {
User user = this.getObjectFromElement(userElement);
userList.add(user);
continue;
// 如果这叶子节点包含我们想找的信息,就添加到List中,并终止本次循环
// 不用在查这个user元素后面的子元素是否包含keyword
}
String password = userElement.elementTextTrim("password");
if (password.contains(keyword)) {
User user = this.getObjectFromElement(userElement);
userList.add(user);
continue;
}
String birth = userElement.elementTextTrim("birth");
if (birth.contains(keyword)) {
User user = this.getObjectFromElement(userElement);
userList.add(user);
continue;
}
String address = userElement.elementTextTrim("address");
if (address.contains(keyword)) {
User user = this.getObjectFromElement(userElement);
userList.add(user);
continue;
}
String gender = userElement.elementTextTrim("gender");
if (gender.contains(keyword)) {
User user = this.getObjectFromElement(userElement);
userList.add(user);
continue;
}
}
return userList;
}
/**
* 与上面的getUserListByKeyword2功能一样
*/
public ListgetUserListByKeyword2(String keyword) {
Document document = DocumentManager.getDocument();
Element usersElement = document.getRootElement();
ListuserElementList = usersElement.elements();
ListuserList = new ArrayList ();
for (Element userElement : userElementList) {
ListuserSubelementList = userElement.elements();
// traverse every sub-element of the user element.
for (Element userSubelement : userSubelementList) {
String elementValue = userSubelement.getTextTrim();
if (elementValue.contains(keyword)) {
// if the sub-element is located successfully,other
// sub-element is not read any longer.
User user = this.getObjectFromElement(userElement);
userList.add(user);
break;
// 如果找到了user的某个子元素包含keyword,就跳出访问其
// 子元素的这个循环,接着外层查找下个user的子节点是否包含
}
}
}
return userList;
}
/**
* 把一个User对象user,把它转换成为Element类型的user节点
* 1.创建名为user的Element对象
* 2.通过Element的addElement方法给user添加子元素
* 3.设置子元素中的内容为User对象对应属性的内容
* 4.返回一个Element元素
*/
private Element getElementFromObject(User user) {
Element userElement = DocumentHelper.createElement("user");
// dom4j中的大多对象都不是构造方法生成的,而是用DcumentHelper来creat的
// 包括文档、元素、属性、文本等都可以依次来生成
userElement.addElement("id").setText(user.getId() + "");
userElement.addElement("username").setText(user.getUsername());
userElement.addElement("password").setText(user.getPassword());
userElement.addElement("birth").setText(user.getBirth() + "");
userElement.addElement("address").setText(user.getAddress());
userElement.addElement("gender").setText(user.getGender());
return userElement;
}
/**
* 添加User对象的用户user到XML文档中
* 1.把User对象user转换成为Element类型的元素
* 2.得到指定xml文档的Document对象,找到要添加元素的节点
* 3.通过addElement方法把要添加的元素填进去
* 4.更改计算user节点元素个数的count属性,每添加一个就加1
* 5.把我们修改后的Document对象写到磁盘
*/
@Override
public boolean addUser(User user) {
Element userElement = this.getElementFromObject(user);
Document document = DocumentManager.getDocument();
Element usersElement = document.getRootElement();
usersElement.add(userElement);
this.setCountAttribute(usersElement, true);
DocumentManager.writeDocument(document);
return false;
}
/**
* 设置usersElement的计user数的属性count
* 1.通过attributeValue方法得到该元素的指定属性的值
* 2.转换成为整数类型
* 3.increment为false表示是增加元素,count就增加,反之减少
* 4.把count转换成字符串后通过addAtributte方法把设定好的属性添加到原节点中
*/
private void setCountAttribute(Element usersElement, boolean increment) {String countStr = usersElement.attributeValue("count");int count = 0;try {count = Integer.parseInt(countStr);} catch (NumberFormatException e) {
// TODO Auto-generated
catch blocke.printStackTrace();}if (increment) {count++;} else {count--;}
//addAttribute方法可以向元素中添加属性
usersElement.addAttribute("count", count + "");
}
/**
* 添加id唯一的用户,一旦id相同就抛出异常。
* 1.得到元素id
* 2.坚持要插入元素的id是否已经存在
* 3.如果不存在就添加元素,如果存在就抛出异常
* 这写操作是在转换成Element前做的,只是对User对象,这样更简便 *
*/
@Overridepublic
boolean addUserWithUniquePK(User user) throws UserExistsException {
Document document = DocumentManager.getDocument();
if (this.findUserElementById(document, user.getId()) != null) {
throw new UserExistsException("The user DOES EXIST!");
}
this.addUser(user);
return false;
}
@Overridepublic
boolean addUserWithAutoPK(User user) {
// PK是primary key的缩写,主键的意思,这里就是id号
// 自动设置用户id,找出现有id号最大的,让新加入的用户id号比最大的大一就可以了
Document document = DocumentManager.getDocument();
Element usersElement = document.getRootElement();
ListuserElementList = usersElement.elements();
int maxId = 0;
for (Element userElement : userElementList) {
String idStr = userElement.elementTextTrim("id");
int id = 0;
try {
id = Integer.parseInt(idStr);
} catch (NumberFormatException e) {
e.printStackTrace();
}
if (maxIdmaxId = id;
}
}
maxId++;
user.setId(maxId);
this.addUser(user);
return false;
}
/** 通过id找到一个Document对象中的元素 */
private Element findUserElementById(Document document, int id) {
Element usersElement = document.getRootElement();
ListuserElementList = usersElement.elements();
for (Element userElement : userElementList) {
// Step 2:
String idStr = userElement.elementTextTrim("id");
if (idStr.equals(id + "")) {
return userElement;
}
}
return null;
}
/**
* 删除指定id的节点
* 1.通过id找到要删除的节点
* 2.找到要删除节点的父节点,可以用父接口中的getParent找到,在这里就是根节点
* 3.判断要删除的节点是否为空
* 4.通过父节点,调用remove方法删除要删除的节点
* 5.如果删除成功,就让计算user节点个数的减少
* 6.把修改过的Document对象写到原xml文件中 *
*/
public boolean deleteUserByPK(int id) {
Document document = DocumentManager.getDocument();
Element userElement = this.findUserElementById(document, id);
if (userElement != null) {
Element usersElement = document.getRootElement();
if (usersElement.remove(userElement)) {
// 执行remove方法的同时也做了判断条件,移除后才能设置计数的属性
// alter the count attribute if succeeded in removing the
element.this.setCountAttribute(usersElement, false);
}
// Step 4:re-write the document into the
disc.DocumentManager.writeDocument(document);
}
return false;
}
/**
* 用户传入一个User对象,更新xml文档中对应元素的信息
* 1.得到要更新的xml文档的document
* 2.由于id是唯一的,通过User对象中的id属性值,找到xml文档中对应id的元素
* 3.得到这个元素的父节点,可以用getParent方法,这里是根节点
* 4.通过父节点调用remove方法移除原来的节点
* 5.把User对象转换成Element对象,添加到document中
* 6.写更新后的document到xml文档
*/
@Overridepublic
boolean update(User user) {
Document document = DocumentManager.getDocument();
Element userElement = this.findUserElementById(document, user.getId());
Element usersElement = document.getRootElement();
if (usersElement.remove(userElement)) {
userElement = this.getElementFromObject(user);
usersElement.add(userElement);
}
DocumentManager.writeDocument(document);
return false;
}
/**
* 对一个存储User对象的UserList中的元素排序
* 1.构建比较器对象,指定排序标准
* 2.用Conections的sort方法排序
*/
@Overridepublic
void sortUserList(ListuserList, boolean ascendable) {
// Step 1:make a sort
standard.ComparatoruserComparator = new UserComparator(
ascendable);
// Step 2:relate the sort standard to the sorted objects
// 调用Collections的静态方法sort根据排序标准排序
Collections.sort(userList, userComparator);
}
@Overridepublic
void sortUserList(ListuserList) {
this.sortUserList(userList, true);
// 默认排序顺序是升序
}
/**
* 通过xpath来快速查找
* 1.设置xpath路径,是字符串的形式,XPath技术可以通过子元素名、自己属性名来获得节点,
* 并可以进而找到其父节点、子节点、元素等
* 2.可以把xpath字符串路径传递给selectSingleNode、selectNodes方法,这两个方法来自Node,
* 来得到Node实例调用这两个方法的对象是说指XPath路径的父节点
*/
@Overridepublic
void testXPath1() {
// xpath可以根据条件(某个节点或者属性)快速定位某个节点。
// locate user node whose sub-element id value is 121
String xpath = "/users/user[id='121']";
// 通过id值查找节点
userDocument document = DocumentManager.getDocument();
Node userElement = document.selectSingleNode(xpath);
// selectSingleNode方法来自Node,可以通过xpath字符串路径得到一个Node实例
// 此方法在此处返回的实例是Element对象,拥有子节点id为121的user节点
System.out.println(this.getObjectFromElement((Element) userElement));
System.out.println("username="
+ ((Element) userElement).elementText("username"));
String xpath = "/users/user[id='121']/username";
Document document = DocumentManager.getDocument();
Node userElement = document.selectSingleNode(xpath);
// 此时selectSingleNode得到的是一个user节点的子元素username,
// 该user节点有一个元素id,他的值为121
System.out.println("username=" + ((Element) userElement).getTextTrim());
String xpath = "/users/user[@firstname='liu']/birth";
// 通过属性访问,这里的selectSingleNode方法得到的是user的一个子元素birth,
// 这个user有个属性叫firstname,属性值为liu
Element birthElement = (Element) DocumentManager.getDocument()
.getRootElement().selectSingleNode(xpath);
}
/**
* 通过id得到用户User对象
* 1.通过指定指定id建立可变的XPath字符串路径
* 2.通过XPath路径得到xml文档中对应的元素
* 3.把找到的元素转换成为User对象类型返回
*/
@Overridepublic
User getUserById(int id) {
String xpath = "/users/user[id='" + id + "']";
System.out.println("xpath=" + xpath);
Element userElement = (Element) DocumentManager.getDocument()
.selectSingleNode(xpath);
return this.getObjectFromElement(userElement);
}
/**
* 让xml文档按照id进行排序 * 保留document,建新的根节点users
* 1.得到原xml文档的document
* 2.创建一个List
* 3.取原来xml文档的所有等待排序的user元素存在List中
* 4.把所有要排序的user元素从原根节点中移除来,这样他们才“自由”了,才可以进行排序,否则会抛出异常
* 5.对List中所有元素排序 *
* 6.设置List中所有元素的父节点为null
* 7.新建一个Element对象users
* 8.把排好序的List中的元素依次添加到users下面,添加一个节点为子元素时,必须确认其父元素为空
* 9.在document下面移除原来的根节点,设置users为根节点
* 10.写document到xml文档
*/
public void sortById3() {
ListuserElementList = rootElement.elements("user");
// 把原来的所有用户存入List中
Element newRootElement = DocumentHelper.createElement("users");
// System.out.println("newRootElement父节点为"+newRootElement.getParent());
// 重新创建一个users的节点
for (Element userElement : userElementList) {
rootElement.remove(userElement);
}
// 把原来根节点的所有内容从原来的根节点移除出去,这样这些节点才是自由的,才可以排序?
List l = rootElement.elements();
System.out
.println("rootElement.elements() 有 " + rootElement.elements());
Comparator userElementComparator = new UserElementComparator();
Collections.sort(userElementList, userElementComparator);
// sort方法根据Comparator中的compare方法的结果进行排序,Comparator是个比较器。
// 此实现将指定列表转储到一个数组中,并对数组进行排序,在重置数组中相应位置每个元素的列表上进行迭代。
for (Element userElement : userElementList) {
System.out.println(userElement.elementTextTrim("id"));
}
for (Element userElement : userElementList) {
// System.out.println("userElement父节点是:"+userElement.getParent());
userElement.setParent(null);
// System.out.println("userElement父节点变成了:"+userElement.getParent());
// System.out.println();
// 设置userElement的父节点为空newRootElement.add(userElement);
// 在新的根节点上添加元素}newRootElement.setAttributeValue("count",
// userElementList.size() + "");
// 为新的根节点添加属性document.setRootElement(newRootElement);
// setRootElement可以设置document的根节点DocumentManager.writeDocument(document);}}
}
}
}