智远 的个人资料Diwan's Space照片日志列表 工具 帮助
没有相册。
此共享空间没有音乐列表。

Diwan's Space

实现理想
5月22日

[转]Jakarta-Common-BeanUtils使用笔记

 

Jakarta-Common-BeanUtils使用笔记

                                      

Beanutils用了魔术般的反射技术,实现了很多夸张有用的功能,都是C/C++时代不敢想的。无论谁的项目,始终一天都会用得上它。我算是后知后觉了,第一回看到它的时候居然错过。

1.属性的动态getter、setter

在这框架满天飞的年代,不能事事都保证执行getter,setter函数了,有时候属性是要根据名字动态取得的,就像这样:  
BeanUtils.getProperty(myBean,"code");
而Common BeanUtils的更强功能在于可以直接访问内嵌对象的属性,只要使用点号分隔。
BeanUtils.getProperty(orderBean, "address.city");
相比之下其他类库的BeanUtils通常都很简单,不能访问内嵌的对象,所以有时要用Commons BeanUtils来替换它们。

BeanUtils还支持List和Map类型的属性,如下面的语法即可取得Order的顾客列表中第一个顾客的名字
BeanUtils.getProperty(orderBean, "customers[1].name");
其中BeanUtils会使用ConvertUtils类把字符串转为Bean属性的真正类型,方便从HttpServletRequest等对象中提取bean,或者把bean输出到页面。
而PropertyUtils就会原色的保留Bean原来的类型。

2.BeanCompartor 动态排序

还是通过反射,动态设定Bean按照哪个属性来排序,而不再需要在实现bean的Compare接口进行复杂的条件判断。
List peoples = ...; // Person对象的列表
Collections.sort(peoples, new BeanComparator("age"));

如果要支持多个属性的复合排序,如"Order By lastName,firstName"

ArrayList sortFields = new ArrayList();
sortFields.add(new BeanComparator("lastName"));
sortFields.add(new BeanComparator("firstName"));
ComparatorChain multiSort = new ComparatorChain(sortFields);
Collections.sort(rows,multiSort);

其中ComparatorChain属于jakata commons-collections包。
如果age属性不是普通类型,构造函数需要再传入一个comparator对象为age变量排序。
另外, BeanCompartor本身的ComparebleComparator, 遇到属性为null就会抛出异常, 也不能设定升序还是降序。这个时候又要借助commons-collections包的ComparatorUtils.

   Comparator mycmp = ComparableComparator.getInstance();
   mycmp = ComparatorUtils.nullLowComparator(mycmp);  //允许null
   mycmp = ComparatorUtils.reversedComparator(mycmp); //逆序
   Comparator cmp = new BeanComparator(sortColumn, mycmp);

3.Converter 把Request或ResultSet中的字符串绑定到对象的属性

   经常要从request,resultSet等对象取出值来赋入bean中,如果不用MVC框架的绑定功能的话,下面的代码谁都写腻了。

   String a = request.getParameter("a");
bean.setA(a);
String b = ....
bean.setB(b);
......

不妨写一个Binder自动绑定所有属性:

    MyBean bean = ...;
HashMap map = new HashMap();
Enumeration names = request.getParameterNames();
while (names.hasMoreElements())
{
String name = (String) names.nextElement();
map.put(name, request.getParameterValues(name));
}
BeanUtils.populate(bean, map);

    其中BeanUtils的populate方法或者getProperty,setProperty方法其实都会调用convert进行转换。
     但Converter只支持一些基本的类型,甚至连java.util.Date类型也不支持。而且它比较笨的一个地方是当遇到不认识的类型时,居然会抛出异常来。 对于Date类型,我参考它的sqldate类型实现了一个Converter,而且添加了一个设置日期格式的函数。
要把这个Converter注册,需要如下语句:

    ConvertUtilsBean convertUtils = new ConvertUtilsBean();
   DateConverter dateConverter = new DateConverter();
   convertUtils.register(dateConverter,Date.class);



//因为要注册converter,所以不能再使用BeanUtils的静态方法了,必须创建BeanUtilsBean实例
BeanUtilsBean beanUtils = new BeanUtilsBean(convertUtils,new PropertyUtilsBean());
beanUtils.setProperty(bean, name, value);

4 其他功能

4.1 ConstructorUtils,动态创建对象
     public static Object invokeConstructor(Class klass, Object arg)
4.2 MethodUtils,动态调用方法
    MethodUtils.invokeMethod(bean, methodName, parameter);


4.3 PropertyUtils,当属性为Collection,Map时的动态读取:
Collection: 提供index

   BeanUtils.getIndexedProperty(orderBean,"items",1);
或者
  BeanUtils.getIndexedProperty(orderBean,"items[1]");

Map: 提供Key Value
  BeanUtils.getMappedProperty(orderBean, "items","111");//key-value goods_no=111
或者
  BeanUtils.getMappedProperty(orderBean, "items(111)")

4.4 PropertyUtils,直接获取属性的Class类型
     public static Class getPropertyType(Object bean, String name)
4.5 动态Bean 用DynaBean减除不必要的VO和FormBean 
5月6日

javadoc的使用(例子)

测试类:
package test;
import java.io.IOException;
/**
 * 这是一个用于测试JAVADOC的类
 * @author Diwan
 * @version 0.1
 *
 */
public class JavaDocTest {
 
 /**这是一个静态变量*/
 public static final String PATH = "c:/abc.txt";
 
 /**
  * 这是说明一.
  * 这是说明二
  * @param arg0 参数一
  * @param arg1 参数二
  * @return boolean 成功返回true,失败返回false
  * @exception IOException 当找不到文件时抛出此异常
  */
 public boolean method1(boolean arg0,String arg1) throws IOException{
  return true;
 }
}
 
导出后:

test
Class JavaDocTest

java.lang.Object
  |_test.JavaDocTest

public class JavaDocTest
extends java.lang.Object

这是一个用于测试JAVADOC的类

Version:
0.1
Author:
Diwan

Field Summary
static java.lang.String PATH
          这是一个静态变量
 
Constructor Summary
JavaDocTest()
           
 
Method Summary
 boolean method1(boolean arg0, java.lang.String arg1)
          这是说明一.
 
Methods inherited from class java.lang.Object
equals, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
 

Field Detail

PATH

public static final java.lang.String PATH
这是一个静态变量

See Also:
Constant Field Values

Constructor Detail

JavaDocTest

public JavaDocTest()
Method Detail

method1

public boolean method1(boolean arg0,
                       java.lang.String arg1)
                throws java.io.IOException
这是说明一. 这是说明二

Parameters:
arg0 - 参数一
arg1 - 参数二
Returns:
boolean 成功返回true,失败返回false
Throws:
java.io.IOException - 当找不到文件时抛出此异常

以上是通过Eclipse的Export功能直接导出的。在导出向导中可以设置各种参数,例如是否按包导出等。

5月3日

[转]javadoc的使用

 

目录

  前言
  一. Java 文档和 javadoc
  二. 文档注释的格式
    1. 文档注释的格式化
    2. 文档注释的三部分
  三. 使用 javadoc 标记
    1. @see 的使用
    2. 使用 @author、@version 说明类
    3. 使用 @param、@return 和 @exception 说明方法
  四. javadoc 命令


前言

  Java 的语法与 C++ 及为相似,那么,你知道 Java 的注释有几种吗?是两种?

  // 注释一行
  /* ...... */ 注释若干行

  不完全对,除了以上两种之外,还有第三种,文档注释:

  /** ...... */ 注释若干行,并写入 javadoc 文档

  通常这种注释的多行写法如下:

  /**
   * .........
   * .........
   */

  暂停,暂停!这第三种注释有什么用?javadoc 又是什么东西?

  好,那就让我告诉你——


一. Java 文档和 javadoc

  Java 程序员都应该知道使用 JDK 开发,最好的帮助信息就来自 SUN 发布的 Java 文档。它分包、分类详细的提供了各方法、属性的帮助信息,具有详细的类树信息、索引信息等,并提供了许多相关类之间的关系,如继承、实现接口、引用等。

  Java 文档全是由一些 html 文件组织起来的,在 SUM 的站点上可以下载它们的压缩包。但是你肯定想不到,这些文档我们可以自己生成。——就此打住,再吊一次胃口。

  安装了 JDK 之后,安装目录下有一个 src.jar 文件或者 src.zip 文件,它们都是以 ZIP 格式压缩的,可以使用 WinZip 解压。解压之后,我们就可以看到分目录放的全是 .java 文件。是了,这些就是 Java 运行类的源码了,非常完整,连注释都写得一清二楚……不过,怎么看这些注释都有点似曾相识的感觉?

  这就不奇怪了,我们的迷底也快要揭开了。如果你仔细对比一下 .java 源文件中的文档注释 (/** ... */) 和 Java 文档的内容,你会发现它们就是一样的。Java 文档只是还在格式和排版上下了些功夫。再仔细一点,你会发现 .java 源文件中的注释还带有 HTML 标识,如
等,在 Java 文档中,该出现这些标识的地方,已经按标识的的定义进行了排版。

  终于真像大白了,原来 Java 文档是来自这些注释。难怪这些注释叫做文档注释呢!不过,是什么工具把这些注释变成文档的呢?

  是该请出 javadoc 的时候了。在 JDK 的 bin 目录下你可以找到 javadoc,如果是 Windows 下的 JDK,它的文件名为 javadoc.exe。使用 javdoc 编译 .java 源文件时,它会读出 .java 源文件中的文档注释,并按照一定的规则与 Java 源程序一起进行编译,生成文档。

  介绍 javadoc 的编译命令之前,还是先了解一下文档注释的格式吧。不过为了能够编译下面提到的若干例子,这里先介绍一条 javadoc 命令:

  javadoc -d 文档存放目录 -author -version 源文件名.java

  这条命令编译一个名为 “源文件名.java”的 java 源文件,并将生成的文档存放在“文档存放目录”指定的目录下,生成的文档中 index.html 就是文档的首页。-author 和 -version 两个选项可以省略。


二. 文档注释的格式

  文档注释可以用于对类、属性、方法等进行说明。写文档注释时除了需要使用 /** .... */ 限定之外,还需要注意注释内部的一些细节问题。

  1. 文档和文档注释的格式化

  生成的文档是 HTML 格式,而这些 HTML 格式的标识符并不是 javadoc 加的,而是我们在写注释的时候写上去的。比如,需要换行时,不是敲入一个回车符,而是写入
,如果要分段,就应该在段前写入

  因此,格式化文档,就是在文档注释中添加相应的 HTML 标识。

  文档注释的正文并不是直接复制到输出文件 (文档的 HTML 文件),而是读取每一行后,删掉前导的 * 号及 * 号以前的空格,再输入到文档的。如

  /**
* This is first line.

***** This is second line.

This is third line.
*/

  编译输出后的 HTML 源码则是

  This is first line.

This is second line.

This is third line.

  前导的 * 号允许连续使用多个,其效果和使用一个 * 号一样,但多个 * 号前不能有其它字符分隔,否则分隔符及后面的 * 号都将作为文档的内容。* 号在这里是作为左边界使用,如上例的第一行和第二行;如果没有前导的 * 号,则边界从第一个有效字符开始,而不包括前面的空格,如上例第三行。

  还有一点需要说明,文档注释只说明紧接其后的类、属性或者方法。如下例:

 
/** comment for class */
public class Test {

    /** comment for a attribute */
    int number;

    /** comment for a method */
    public void myMethod() { ...... }

    ......
}

  上例中的三处注释就是分别对类、属性和方法的文档注释。它们生成的文档分别是说明紧接其后的类、属性、方法的。“紧接”二字尤其重要,如果忽略了这一点,就很可能造成生成的文档错误。如

 
import java.lang.*;

/** commnet for class */

public class Test { ...... }

// 此例为正确的例子

  这个文档注释将生成正确的文档。但只需要改变其中两行的位置,变成下例,就会出错:

 
/** commnet for class */

import java.lang.*;

public class Test { ...... }

// 此例为错误的例子

  这个例子只把上例的 import 语句和文档注释部分交换了位置,结果却大不相同——生成的文档中根本就找不到上述注释的内容了。原因何在?

  “/** commnet for class */”是对 class Test 的说明,把它放在“public class Test { ...... }”之前时,其后紧接着 class Test,符合规则,所以生成的文档正确。但是把它和“import java.lang.*;”调换了位置后,其后紧接的就是不 class Test 了,而是一个 import 语句。由于文档注释只能说明类、属性和方法,import 语句不在此列,所以这个文档注释就被当作错误说明省略掉了。

  2. 文档注释的三部分

  根据在文档中显示的效果,文档注释分为三部分。先举例如下,以便说明。

 
/**
 * show 方法的简述.
 * 

show 方法的详细说明第一行
* show 方法的详细说明第二行 * @param b true 表示显示,false 表示隐藏 * @return 没有返回值 */

public void show(boolean b) { frame.show(b); }

  第一部分是简述。文档中,对于属性和方法都是先有一个列表,然后才在后面一个一个的详细的说明。列表中属性名或者方法名后面那段说明就是简述。如下图中被红框框选的部分:

      简述部分写在一段文档注释的最前面,第一个点号 (.) 之前 (包括点号)。换句话说,就是用第一个点号分隔文档注释,之前是简述,之后是第二部分和第三部分。如上例中的 “* show 方法的简述.”。

  有时,即使正确地以一个点号作为分隔,javadoc 仍然会出错,把点号后面的部分也做为了第一部分。为了解决这个问题,我们可以使用一个

标志将第二分部分开为下一段,如上例的“*

show 方法的详细说明第一行 ....”。除此之外,我们也可以使用
来分隔。

  第二部分是详细说明部分。该部分对属性或者方法进行详细的说明,在格式上没有什么特殊的要求,可以包含若干个点号。它在文档中的位置如下图所示:

这部分文档在上例中相应的代码是:

  * show 方法的简述.
  *

show 方法的详细说明第一行

  * show 方法的详细说明第二行

  发现什么了?对了,简述也在其中。这一点要记住了,不要画蛇添足——在详细说明部分中再写一次简述哦!

  第三部分是特殊说明部分。这部分包括版本说明、参数说明、返回值说明等。它在文档中的位置:

 第三部分在上例中相应的代码是

  * @param b true 表示显示,false 表示隐藏

  * @return 没有返回值

  除了 @param 和 @return 之外,还有其它的一些特殊标记,分别用于对类、属性和方法的说明……不要推我,我马上就说。

三. 使用 javadoc 标记

  javadoc 标记是插入文档注释中的特殊标记,它们用于标识代码中的特殊引用。javadoc 标记由“@”及其后所跟的标记类型和专用注释引用组成。记住了,三个部分——@、标记类型、专用注释引用。不过我宁愿把它分成两部分:@ 和标记类型、专用注释引用。虽然 @ 和 标记类型之间有时可以用空格符分隔,但是我宁愿始终将它们紧挨着写,以减少出错机会。

  javadoc 标记有如下一些:

标记 用于 作用
@author 对类的说明 标明开发该类模块的作者
@version 对类的说明 标明该类模块的版本
@see 对类、属性、方法的说明 参考转向,也就是相关主题
@param 对方法的说明 对方法中某参数的说明
@return 对方法的说明 对方法返回值的说明
@exception 对方法的说明 对方法可能抛出的异常进行说明

  下面详细说明各标记。

1. @see 的使用

  @see 的句法有三种:

  @see 类名
  @see #方法名或属性名
  @see 类名#方法名或属性名

  类名,可以根据需要只写出类名 (如 String) 或者写出类全名 (如 java.lang.String)。那么什么时候只需要写出类名,什么时候需要写出类全名呢?

  如果 java 源文件中的 import 语句包含了的类,可以只写出类名,如果没有包含,则需要写出类全名。java.lang 也已经默认被包含了。这和 javac 编译 java 源文件时的规定一样,所以可以简单的用 javac 编译来判断,源程序中 javac 能找到的类,javadoc 也一定能找到;javac 找不到的类,javadoc 也找不到,这就需要使用类全名了。

  方法名或者属性名,如果是属性名,则只需要写出属性名即可;如果是方法名,则需要写出方法名以及参数类型,没有参数的方法,需要写出一对括号。如

成员类型 成员名称及参数 @see 句法
属性 number @see number
属性 count @see count
方法 count() @see count()
方法 show(boolean b) @see show(boolean)
方法 main(String[] args) @see main(String[])

  有时也可以偷懒:假如上例中,没有 count 这一属性,那么参考方法 count() 就可以简写成 @see count。不过,为了安全起见,还是写全 @see count() 比较好。

  @see 的第二个句法和第三个句法都是转向方法或者属性的参考,它们有什么区别呢?

  第二个句法中没有指出类名,则默认为当前类。所以它定义的参考,都转向本类中的属性或者方法。而第三个句法中指出了类名,则还可以转向其它类的属性或者方法。

  关于 @see 标记,我们举个例说明。由于 @see 在对类说明、对属性说明、对方法说明时用法都一样,所以这里只以对类说明为例。

 
/**
 * @see String
 * @see java.lang.StringBuffer
 * @see #str
 * @see #str()
 * @see #main(String[])
 * @see Object#toString()
 */
public class TestJavaDoc {

}

  生成的文档的相关部分如下图:

  String 和 StringBuffer 都是在 java.lang 包中,由于这个包是默认导入了的,所以这两个类可以直接写类名,也可以写类全名。str、str() 为同名属性和方法,所以方法名需要用 () 区分。main 是带参数的方法,所以在 () 中指明了参数类型。toString() 虽然在本类中也有 (从 Object 继承的),但我们是想参考 Object 类的 toString() 方法,所以使用了 Object#toString()。

  奇怪的是,为什么其中只有 str、str() 和 main(String[]) 变成了链接呢?那是因为编译时没有把 java.lang 包或者 Stirng、StringBuffer、Object 三个类的源文件一起加入编译,所以,生成的文档没有关于那三个类的信息,也就不可以建立链接了。后面讲解 javadoc 编译命令的时候还会详细说明。

  上例中如果去把类中的 str 属性去掉,那么生成的文档又会有什么变化呢?你会发现,原来是 str, str(),而现在变成了 str(), str(),因为 str 属性已经没有了,所以 str 也表示方法 str()。

  2. 使用 @author、@version 说明类

  这两个标记分别用于指明类的作者和版本。缺省情况下 javadoc 将其忽略,但命令行开关 -author 和 -version 可以修改这个功能,使其包含的信息被输出。这两个标记的句法如下:

  @author 作者名
  @version 版本号

  其中,@author 可以多次使用,以指明多个作者,生成的文档中每个作者之间使用逗号 (,) 隔开。@version 也可以使用多次,只有第一次有效,生成的文档中只会显示第一次使用 @version 指明的版本号。如下例

 
/**
 * @author Fancy
 * @author Bird
 * @version Version 1.00
 * @version Version 2.00
 */
public class TestJavaDoc {

}

  生成文档的相关部分如图:

  从生成文档的图示中可以看出,两个 @author 语句都被编译,在文档中生成了作者列表。而两个 @version 语句中只有第一句被编译了,只生成了一个版本号。

  从图上看,作者列表是以逗号分隔的,如果我想分行显示怎么办?另外,如果我想显示两个以上的版本号又该怎么办?

  ——我们可以将上述两条 @author 语句合为一句,把两个 @version 语句也合为一句:

  @author Fancy
Bird
  @version Version 1.00
Version 2.00

  结果如图:

  我们这样做即达到了目的,又没有破坏规则。@author 之后的作者名和 @version 之后的版本号都可以是用户自己定义的任何 HTML 格式,所以我们可以使用
标记将其分行显示。同时,在一个 @version 中指明两个用
分隔的版本号,也没有破坏只显示第一个 @version 内容的规则。

  3. 使用 @param、@return 和 @exception 说明方法

  这三个标记都是只用于方法的。@param 描述方法的参数,@return 描述方法的返回值,@exception 描述方法可能抛出的异常。它们的句法如下:

  @param 参数名 参数说明
  @return 返回值说明
  @exception 异常类名 说明

  每一个 @param 只能描述方法的一个参数,所以,如果方法需要多个参数,就需要多次使用 @param 来描述。

  一个方法中只能用一个 @return,如果文档说明中列了多个 @return,则 javadoc 编译时会发出警告,且只有第一个 @return 在生成的文档中有效。

  方法可能抛出的异常应当用 @exception 描述。由于一个方法可能抛出多个异常,所以可以有多个 @exception。每个 @exception 后面应有简述的异常类名,说明中应指出抛出异常的原因。需要注意的是,异常类名应该根据源文件的 import 语句确定是写出类名还是类全名。   示例如下:

 
public class TestJavaDoc {

    /**
     * @param n a switch
     * @param b excrescent parameter
     * @return true or false
     * @return excrescent return
     * @exception java.lang.Exception throw when switch is 1
     * @exception NullPointerException throw when parameter n is null
     */
    public boolean fun(Integer n) throws Exception {
        switch (n.intValue()) {
        case 0:
            break;
        case 1:
            throw new Exception("Test Only");
        default:
            return false;
        }
        return true;
    }

}

  使用 javadoc 编译生成的文档相关部分如下图:

  可以看到,上例中 @param b excrescent parameter 一句是多余的,因为参数只是一个 n,并没有一个 b但是 javadoc 编译时并没有检查。因此,写文档注释时一定要正确匹配参数表与方法中正式参数表的项目。如果方法参数表中的参数是 a,文档中却给出对参数 x 的解释,或者再多出一个参数 i,就会让人摸不着头脑了。@exceptin 也是一样。

  上例程序中并没有抛出一个 NullPointerException,但是文档注释中为什么要写上这么一句呢,难道又是为了演示?这不是为了演示描述多余的异常也能通过编译,而是为了说明写异常说明时应考运行时 (RunTime) 异常的可能性。上例程序中,如果参数 n 是给的一个空值 (null),那么程序会在运行的时候抛出一个 NullPointerException,因此,在文档注释中添加了对 NullPointerException 的说明。

  上例中的 @return 语句有两个,但是根据规则,同一个方法中,只有第一个 @return 有效,其余的会被 javadoc 忽略。所以生成的文档中没有出现第二个 @return 的描述。

  讲到这里,该怎么写文档注释你应该已经清楚了,下面就开始讲解 javadoc 的常用命令。


四. javadoc 命令

  运行 javadoc -help 可以看到 javadoc 的用法,这里列举常用参数如下:

用法:
  javadoc [options] [packagenames] [sourcefiles]

选项:

  -public 仅显示 public 类和成员
  -protected 显示 protected/public 类和成员 (缺省)
  -package 显示 package/protected/public 类和成员
  -private 显示所有类和成员
  -d 输出文件的目标目录
  -version 包含 @version 段
  -author 包含 @author 段
  -splitindex 将索引分为每个字母对应一个文件
  -windowtitle 文档的浏览器窗口标题

  javadoc 编译文档时可以给定包列表,也可以给出源程序文件列表。例如在 CLASSPATH 下有两个包若干类如下:

  fancy.Editor
  fancy.Test
  fancy.editor.ECommand
  fancy.editor.EDocument
  fancy.editor.EView

  这里有两个包 (fancy 和 fancy.editor) 和 5 个类。那么编译时 (Windows 环境) 可以使用如下 javadoc 命令:

  javadoc fancy\Test.java fancy\Editor.java fancy\editor\ECommand.java fancy\editor\EDocument.java fancy\editor\EView.java

  这是给出 java 源文件作为编译参数的方法,注意命令中指出的是文件路径,应该根据实际情况改变。也可以是给出包名作为编译参数,如:

  javadoc fancy fancy.editor

  用浏览器打开生成文档的 index.html 文件即可发现两种方式编译结果的不同,如下图:

  用第二条命令生成的文档被框架分成了三部分:包列表、类列表和类说明。在包列表中选择了某个包之后,类列表中就会列出该包中的所有类;在类列表中选择了某个类之后,类说明部分就会显示出该类的详细文档。而用第一条命令生成的文档只有两部分,类列表和类说明,没有包列表。这就是两种方式生成文档的最大区别了。

  下面再来细说选项。

  -public、-protected、-package、-private 四个选项,只需要任选其一即可。它们指定的显示类成员的程度。它们显示的成员多少是一个包含的关系,如下表:

-private (显示所有类和成员)
-package (显示 package/protected/public 类和成员)
-protected (显示 protected/public 类和成员)
-public (仅显示 public 类和成员)

  -d 选项允许你定义输出目录。如果不用 -d 定义输出目录,生成的文档文件会放在当前目录下。-d 选项的用法是

  -d 目录名

  目录名为必填项,也就是说,如果你使用了 -d 参数,就一定要为它指定一个目录。这个目录必须已经存在了,如果还不存在,请在运行 javadoc 之前创建该目录。

  -version 和 -author 用于控制生成文档时是否生成 @version 和 @author 指定的内容。不加这两个参数的情况下,生成的文档中不包含版本和作者信息。

  -splitindex 选项将索引分为每个字母对应一个文件。默认情况下,索引文件只有一个,且该文件中包含所有索引内容。当然生成文档内容不多的时候,这样做非常合适,但是,如果文档内容非常多的时候,这个索引文件将包含非常多的内容,显得过于庞大。使用 -splitindex 会把索引文件按各索引项的第一个字母进行分类,每个字母对应一个文件。这样,就减轻了一个索引文件的负担。

  -windowtitle 选项为文档指定一个标题,该标题会显示在窗口的标题栏上。如果不指定该标题,而默认的文档标题为“生成的文档(无标题)”。该选项的用法是:

  -windowtitle 标题

  标题是一串没有包含空格的文本,因为空格符是用于分隔各参数的,所以不能包含空格。同 -d 类似,如果指定了 -windowtitle 选项,则必须指定标题文本。

  到此为止,Java 文档和 javadoc 就介绍完了。javadoc 真的能让我们在 Java 注释上做文章——生成开发文档。

4月30日

关于AJAX的responseXML

以前用的时候,通过responseXML取得从服务器返回的内容,总是为空,通过responseText才能取得文本内容,非得通过
var xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
mslCoc.async = "false";
xmlDoc.loadXML(text);
来取得一个Document对象。郁闷。。。。看一些介绍,都是可以通过responseXML直接取得Document对象。
 
刚好今天比较闲,同学又问到这个问题,于是又到网上搜了一通,总算是解决了:
关键在于服务器端要加入这么一句:
String CONTENT_TYPE = "text/xml;charset=UTF-8";
response.setContentType(CONTENT_TYPE);//设置response文件头编码
 
 
============= 一个具体的例子 =================
 
服务器端:
 
public class TagTestAction extends Action {
 public ActionForward execute(ActionMapping arg0, ActionForm arg1, HttpServletRequest request, HttpServletResponse response) throws Exception {

  String CONTENT_TYPE = "text/xml;charset=UTF-8";
  response.setContentType(CONTENT_TYPE);//设置response文件头编码
  try{
   PrintWriter out = response.getWriter(); 
   String str = "";
   str += "<?xml version=\"1.0\" ?>";
   str += "<root>";
   str += "<id>123</id>";   
   str += "<result>success</result>";
   str += "</root>";
   out.print(str);
   out.flush();
      }catch (IOException e) {
    e.printStackTrace();
   }
  return null;
 }
}
 
客户端:
 
<%@ page contentType="text/html; charset=gb2312" language="java" import="java.sql.*" errorPage="" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312" />
<title>无标题文档</title>
<script type="text/javascript" language="javascript">
var http_request;
function createHttpRequest(){
 if (window.XMLHttpRequest) { // Mozilla, Safari,...
    http_request = new XMLHttpRequest();
    }else if (window.ActiveXObject) { // IE
       http_request = new ActiveXObject("Msxml2.XMLHTTP");
    }
}
function dosubmit(){
 
  createHttpRequest();
  var url = "tagTestAction.do?isRound=yes";
  http_request.onreadystatechange = handleCheckResutl;
  http_request.open('POST', url, true);
  http_request.send(null); 
}
function handleCheckResutl(){
 if (http_request.readyState == 4) {
        if (http_request.status == 200) {
   xmlDoc = http_request.responseXML;
   alert(xmlDoc.getElementsByTagName("id")[0].firstChild.nodeValue);
   alert(xmlDoc.xml);
  }
 }     
}
</script>
</head>
<body>
<a href="#" onclick="dosubmit()">AJAX测试</a>
</body>
</html>
4月26日

[转]DOM4J的使用2


发表于 2004年9月27日 20:21

本文主要讨论了用dom4j解析XML的基础问题,包括建立XML文档,添加、修改、删除节点,以及格式化(美化)输出和中文问题。可作为dom4j的入门资料。

转载自:http://jalorsoft.com/holen/

作者:陈光(holen@263.net

时间:2004-09-11

 

本文主要讨论了用dom4j解析XML的基础问题,包括建立XML文档,添加、修改、删除节点,以及格式化(美化)输出和中文问题。可作为dom4j的入门资料。

 

1 下载与安装

 

dom4j是sourceforge.net上的一个开源项目,主要用于对XML的解析。从2001年7月发布第一版以来,已陆续推出多个版本,目前最高版本为1.5。

dom4j专门针对Java开发,使用起来非常简单、直观,在Java界,dom4j正迅速普及。

 

可以到http://sourceforge.net/projects/dom4j下载其最新版。

 

dom4j1.5的完整版大约13M,是一个名为dom4j-1.5.zip的压缩包,解压后有一个dom4j-1.5.jar文件,这就是应用时需要引入的类包,另外还有一个jaxen-1.1-beta-4.jar文件,一般也需要引入,否则执行时可能抛java.lang.NoClassDefFoundError: org/jaxen/JaxenException异常,其他的包可以选择用之。

 

2 示例XML文档(holen.xml

 

为了述说方便,先看一个XML文档,之后的操作均以此文档为基础。

 


holen.xml
<?xml version="1.0" encoding="UTF-8"?>
<books>
    <!--This is a test for dom4j, holen, 2004.9.11-->
    <book show="yes">
       <title>Dom4j Tutorials</title>
    </book>
    <book show="yes">
       <title>Lucene Studing</title>
    </book>
    <book show="no">
       <title>Lucene in Action</title>
    </book>
    <owner>O'Reilly</owner>
</books>


 

这是一个很简单的XML文档,场景是一个网上书店,有很多书,每本书有两个属性,一个是书名[title],一个为是否展示[show],最后还有一项是这些书的拥有者[owner]信息。

 

3 建立一个XML文档

 


 
    /**
     * 建立一个XML文档,文档名由输入属性决定
     * @param filename 需建立的文件名
     * @return 返回操作结果, 0表失败, 1表成功
     */
    public int createXMLFile(String filename){
       /** 返回操作结果, 0表失败, 1表成功 */
       int returnValue = 0;
       /** 建立document对象 */
       Document document = DocumentHelper.createDocument();
       /** 建立XML文档的根books */
       Element booksElement = document.addElement("books");
       /** 加入一行注释 */
       booksElement.addComment("This is a test for dom4j, holen, 2004.9.11");
       /** 加入第一个book节点 */
       Element bookElement = booksElement.addElement("book");
       /** 加入show属性内容 */
       bookElement.addAttribute("show","yes");
       /** 加入title节点 */
       Element titleElement = bookElement.addElement("title");
       /** 为title设置内容 */
       titleElement.setText("Dom4j Tutorials");
      
       /** 类似的完成后两个book */
       bookElement = booksElement.addElement("book");
       bookElement.addAttribute("show","yes");
       titleElement = bookElement.addElement("title");
       titleElement.setText("Lucene Studing");
       bookElement = booksElement.addElement("book");
       bookElement.addAttribute("show","no");
       titleElement = bookElement.addElement("title");
       titleElement.setText("Lucene in Action");
      
       /** 加入owner节点 */
       Element ownerElement = booksElement.addElement("owner");
       ownerElement.setText("O'Reilly");
      
       try{
           /** 将document中的内容写入文件中 */
           XMLWriter writer = new XMLWriter(new FileWriter(new File(filename)));
           writer.write(document);
           writer.close();
           /** 执行成功,需返回1 */
           returnValue = 1;
       }catch(Exception ex){
           ex.printStackTrace();
       }
             
       return returnValue;
    }


 

说明:

Document document = DocumentHelper.createDocument();

通过这句定义一个XML文档对象。

 

Element booksElement = document.addElement("books");

通过这句定义一个XML元素,这里添加的是根节点。

Element有几个重要的方法:

l         addComment:添加注释

l         addAttribute:添加属性

l         addElement:添加子元素

 

最后通过XMLWriter生成物理文件,默认生成的XML文件排版格式比较乱,可以通过OutputFormat类的createCompactFormat()方法或createPrettyPrint()方法格式化输出,默认采用createCompactFormat()方法,显示比较紧凑,这点将在后面详细谈到。

 

生成后的holen.xml文件内容如下:

 


 
<?xml version="1.0" encoding="UTF-8"?>
<books><!--This is a test for dom4j, holen, 2004.9.11--><book show="yes"><title>Dom4j Tutorials</title></book><book show="yes"><title>Lucene Studing</title></book><book show="no"><title>Lucene in Action</title></book><owner>O'Reilly</owner></books>


 

4 修改XML文档

 

有三项修改任务,依次为:

l         如果book节点中show属性的内容为yes,则修改成no

l         把owner项内容改为Tshinghua,并添加date节点

l         若title内容为Dom4j Tutorials,则删除该节点

 


 
    /**
     * 修改XML文件中内容,并另存为一个新文件
     * 重点掌握dom4j中如何添加节点,修改节点,删除节点
     * @param filename 修改对象文件
     * @param newfilename 修改后另存为该文件
     * @return 返回操作结果, 0表失败, 1表成功
     */
    public int ModiXMLFile(String filename,String newfilename){
       int returnValue = 0;
       try{
           SAXReader saxReader = new SAXReader();
           Document document = saxReader.read(new File(filename));
           /** 修改内容之一: 如果book节点中show属性的内容为yes,则修改成no */
           /** 先用xpath查找对象 */
           List list = document.selectNodes("/books/book/@show" );
           Iterator iter = list.iterator();
           while(iter.hasNext()){
              Attribute attribute = (Attribute)iter.next();
              if(attribute.getValue().equals("yes")){
                  attribute.setValue("no");
              }  
           }
          
           /**
            * 修改内容之二: 把owner项内容改为Tshinghua
            * 并在owner节点中加入date节点,date节点的内容为2004-09-11,还为date节点添加一个属性type
            */
           list = document.selectNodes("/books/owner" );
           iter = list.iterator();
           if(iter.hasNext()){
              Element ownerElement = (Element)iter.next();
              ownerElement.setText("Tshinghua");
              Element dateElement = ownerElement.addElement("date");
              dateElement.setText("2004-09-11");
              dateElement.addAttribute("type","Gregorian calendar");
           }
          
           /** 修改内容之三: 若title内容为Dom4j Tutorials,则删除该节点 */
           list = document.selectNodes("/books/book");
           iter = list.iterator();
           while(iter.hasNext()){
              Element bookElement = (Element)iter.next();
              Iterator iterator = bookElement.elementIterator("title");
              while(iterator.hasNext()){
                  Element titleElement=(Element)iterator.next();
                  if(titleElement.getText().equals("Dom4j Tutorials")){
                     bookElement.remove(titleElement);
                  }
              }
           }         
          
           try{
              /** 将document中的内容写入文件中 */
              XMLWriter writer = new XMLWriter(new FileWriter(new File(newfilename)));
              writer.write(document);
              writer.close();
              /** 执行成功,需返回1 */
              returnValue = 1;
           }catch(Exception ex){
              ex.printStackTrace();
           }
          
       }catch(Exception ex){
           ex.printStackTrace();
       }
       return returnValue;
    }
   


 

说明:

List list = document.selectNodes("/books/book/@show" );

list = document.selectNodes("/books/book");

上述代码通过xpath查找到相应内容。

 

通过setValue()、setText()修改节点内容。

 

通过remove()删除节点或属性。

 

5 格式化输出和指定编码

 

默认的输出方式为紧凑方式,默认编码为UTF-8,但对于我们的应用而言,一般都要用到中文,并且希望显示时按自动缩进的方式的显示,这就需用到OutputFormat类。

 


 
   
    /**
     * 格式化XML文档,并解决中文问题
     * @param filename
     * @return
     */
    public int formatXMLFile(String filename){
       int returnValue = 0;
       try{
           SAXReader saxReader = new SAXReader();
           Document document = saxReader.read(new File(filename));
           XMLWriter writer = null;
           /** 格式化输出,类型IE浏览一样 */
           OutputFormat format = OutputFormat.createPrettyPrint();
           /** 指定XML编码 */
           format.setEncoding("GBK");
           writer= new XMLWriter(new FileWriter(new File(filename)),format);
           writer.write(document);
           writer.close();     
           /** 执行成功,需返回1 */
           returnValue = 1;    
       }catch(Exception ex){
           ex.printStackTrace();
       }
       return returnValue;
    }


 

说明:

 

OutputFormat format = OutputFormat.createPrettyPrint();

这句指定了格式化的方式为缩进式,则非紧凑式。

 

format.setEncoding("GBK");

指定编码为GBK。

 

XMLWriter writer = new XMLWriter(new FileWriter(new File(filename)),format);

这与前面两个方法相比,多加了一个OutputFormat对象,用于指定显示和编码方式。

 

6 完整的类代码

 

前面提出的方法都是零散的,下面给出完整类代码。

 


Dom4jDemo.java
package com.holen.dom4j;
 
import java.io.File;
import java.io.FileWriter;
import java.util.Iterator;
import java.util.List;
 
import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.SAXReader;
import org.dom4j.io.XMLWriter;
 
/**
 * @author Holen Chen
 */
public class Dom4jDemo {
   
    public Dom4jDemo() {
    }
 
    public int createXMLFile(String filename){…}
    public int ModiXMLFile(String filename,String newfilename){…}
    public int formatXMLFile(String filename){…}
 
    public static void main(String[] args) {
       Dom4jDemo temp = new Dom4jDemo();
       System.out.println(temp.createXMLFile("d://holen.xml"));        System.out.println(temp.ModiXMLFile("d://holen.xml","d://holen2.xml"));
       System.out.println(temp.formatXMLFile("d://holen2.xml"));
    }
}


 

说明:

main()方法中依次调用三个方法,第一个方法用于生成holen.xml,第二个方法用于修改holen.xml,并且修改后的内容另存为holen2.xml,第三个方法将holen2.xml格式化缩进式输出,并指定编码方式为GBK。


一个应用Dom4j的例子
 
[ 2005-4-21 ]
 
Created with Colorer-take5 Library. Type 'net.sf.colorer.FileType@777255'
 
  0: /*
  1:  * Created on 2005-4-19
  2:  *
  3:  * Copyright (c) 2005 Julysea
  4:  * Window - Preferences - Java - Code Style - Code Templates
  5:  */
  6:
  7: /*应用此log4j的log4j.properties配置文件
  8:  *
  9:  *#######################################################################     
 10:  *# Categories and levels                                                     
 11:  *#######################################################################     
 12:  *                                                                            
 13:  *log4j.rootCategory=DEBUG, FileApp, ConApp                                   
 14:  *log4j.category.de.jayefem=DEBUG, FileApp, ConApp                            
 15:  *                                                                            
 16:  *#######################################################################     
 17:  *# Appenders                                                                 
 18:  *#######################################################################     
 19:  *                                                                            
 20:  *# ConApp is set to be a ConsoleAppender.                                    
 21:  *log4j.appender.ConApp=org.apache.log4j.ConsoleAppender   
 22:  *log4j.appender.ConApp.Target=System.out
 23:  *log4j.appender.ConApp.layout=org.apache.log4j.PatternLayout
 24:  *log4j.appender.ConApp.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n  
 25:  *                                                                            
 26:  *# FileApp                                                                   
 27:  *log4j.appender.FileApp=org.apache.log4j.RollingFileAppender                  
 28:  *log4j.appender.FileApp.File=./log4e.log
 29:  *log4j.appender.FileApp.MaxFileSize=500KB                                    
 30:  *# Keep one backup file                                                      
 31:  *log4j.appender.FileApp.MaxBackupIndex=1                                     
 32:  *log4j.appender.FileApp.layout=org.apache.log4j.PatternLayout                
 33:  *log4j.appender.FileApp.layout.ConversionPattern=%d [%t] %-5p %c - %m%n 
 34:  */
 35:
 36:
 37: /*应用此XML文件做测试
 38:  *
 39:  *<EW cmd="login" mod="Login" version="6.0">
 40:  *<Source uns="" type="user"/>
 41:  *<Username>zhangzhiyun@hp</Username>
 42:  *<Password>111111</Password>
 43:  *<Version>6.01.06.00</Version>
 44:  *</EW>
 45:  */
 46: package xml;
 47:
 48: import java.io.BufferedReader;
 49: import java.io.BufferedWriter;
 50: import java.io.File;
 51: import java.io.FileReader;
 52: import java.io.FileWriter;
 53: import java.io.IOException;
 54:
 55: import org.apache.log4j.Logger;
 56: import org.dom4j.Attribute;
 57: import org.dom4j.DocumentException;
 58: import org.dom4j.DocumentHelper;
 59: import org.dom4j.Element;
 60:
 61: /**
 62:  * @author julysea
 63:  *
 64:  * 一个用Dom4j解析xml的例子
 65:  *
 66:  */
 67: public class Dom4jTest {
 68:
 69:     private static final Logger logger = Logger.getLogger(Dom4jTest.class);
 70:
 71:     public static void main(String[] args) throws IOException,
 72:             DocumentException {
 73:         BufferedReader reader=new BufferedReader(new FileReader("ew.xml"));
 74:         String tempStr;
 75:         String ewXml="";
 76:         while((tempStr=reader.readLine())!=null) {
 77:             ewXml=ewXml+tempStr;
 78:             logger.debug(tempStr);
 79:         }
 80:         Element root = null;
 81:        
 82:         root = DocumentHelper.parseText(ewXml).getRootElement();
 83:         Attribute rootCmd=root.attribute("cmd");
 84:         Attribute rootVersion=root.attribute("version");
 85:         logger.debug("rootNmae = "+root.getName());
 86:         logger.debug("EW'cmd = "+rootCmd.getValue());
 87:         logger.debug("EW'version = "+rootVersion.getValue());
 88:        
 89:         Element usrName=root.element("Username");
 90:         logger.debug("EW.Username value = "+usrName.getTextTrim());
 91:        
 92:         Element source=root.element("Source");
 93:         Attribute sourceUns=source.attribute("uns");
 94:         logger.debug("EW.Source'uns"+sourceUns.getValue());
 95:         Attribute sourceType=source.attribute("type");
 96:         logger.debug("EW.Source'type = "+sourceType.getValue());
 97:        
 98:        
 99:         //创建一个Xml文件
100:         Element user=DocumentHelper.createElement("User");
101:         user.addAttribute("type", "user");
102:         user.addElement("name").addAttribute("type", "PinYin").setText("Julysea");
103:         user.addElement("age").setText("29");
104:         String oneXml=user.asXML();
105:        
106:         BufferedWriter out=new BufferedWriter(new FileWriter("oneXml.xml"));
107:         out.write(oneXml);
108:         out.close();
109:     }
110: }


Dom4j编码问题彻底解决






这几天开始学习dom4j,在网上找了篇文章就开干了,上手非常的快,但是发现了个问题就是无法以UTF-8保存xml文件,保存后再次读出的时候会报“Invalid byte 2 of 2-byte UTF-8 sequence.”这样一个错误,检查发现由dom4j生成的这个文件,在使用可正确处理XML编码的任何的编辑器中中文成乱码,从记事本查看并不会出现乱码会正确显示中文。让我很是头痛。试着使用GBK、gb2312编码来生成的xml文件却可以正常的被解析。因此怀疑的dom4j没有对utf-8编码进行处理。便开始查看dom4j的原代码。终于发现的问题所在,是自己程序的问题。
   在dom4j的范例和网上流行的《DOM4J 使用简介》这篇教程中新建一个xml文档的代码都类似如下
    public void createXML(String fileName) {
        document.nbspdoc = org.dom4j.document.elper.createdocument.);
        Element root = doc.addElement("book");
        root.addAttribute("name", "我的图书");
 
        Element childTmp;
        childTmp = root.addElement("price");
        childTmp.setText("21.22");
 
        Element writer = root.addElement("author");
        writer.setText("李四");
        writer.addAttribute("ID", "001");
 
        try {
            org.dom4j.io.XMLWriter xmlWriter = new org.dom4j.io.XMLWriter(
                    new FileWriter(fileName));
            xmlWriter.write(doc);
            xmlWriter.close();
        }
        catch (Exception e) {
            System.out.println(e);
        }
    }
   在上面的代码中输出使用的是FileWriter对象进行文件的输出。这就是不能正确进行文件编码的原因所在,java中由Writer类继承下来的子类没有提供编码格式处理,所以dom4j也就无法对输出的文件进行正确的格式处理。这时候所保存的文件会以系统的默认编码对文件进行保存,在中文版的window下java的默认的编码为GBK,也就是所虽然我们标识了要将xml保存为utf-8格式但实际上文件是以GBK格式来保存的,所以这也就是为什么能够我们使用GBK、GB2312编码来生成xml文件能正确的被解析,而以UTF-8格式生成的文件不能被xml解析器所解析的原因。
   好了现在我们找到了原因所在了,我们来找解决办法吧。首先我们看看dom4j是如何实现编码处理的
   public XMLWriter(OutputStream out) throws UnsupportedEncodingException {
        //System.out.println("In OutputStream");
        this.format = DEFAULT_FORMAT;
        this.writer = createWriter(out, format.getEncoding());
        this.autoFlush = true;
       namespaceStack.push(Namespace.NO_NAMESPACE);
    }
 
    public XMLWriter(OutputStream out, OutputFormat format) throws UnsupportedEncodingException {
        //System.out.println("In OutputStream,OutputFormat");
        this.format = format;
        this.writer = createWriter(out, format.getEncoding());
        this.autoFlush = true;
       namespaceStack.push(Namespace.NO_NAMESPACE);
    }
    /**
     * Get an OutputStreamWriter, use preferred encoding.
     */
    protected Writer createWriter(OutputStream outStream, String encoding) throws UnsupportedEncodingException {
        return new BufferedWriter(
            new OutputStreamWriter( outStream, encoding )
        );
    }
   由上面的代码我们可以看出dom4j对编码并没有进行什么很复杂的处理,完全通过java本身的功能来完成。所以我们在使用dom4j的来生成我们的XML文件时不应该直接为在构建XMLWriter时,不应该直接为其赋一个Writer对象,而应该通过一个OutputStream的子类对象来构建。也就是说在我们上面的代码中,不应该用FileWriter对象来构建xml文档,而应该使用FileOutputStream对象来构建所以将代码修改入下:
    public void createXML(String fileName) {
        document.nbspdoc = org.dom4j.document.elper.createdocument.);
        Element root = doc.addElement("book");
        root.addAttribute("name", "我的图书");
 
        Element childTmp;
        childTmp = root.addElement("price");
        childTmp.setText("21.22");
 
        Element writer = root.addElement("author");
        writer.setText("李四");
        writer.addAttribute("ID", "001");
 
        try {
            //注意这里的修改
            org.dom4j.io.XMLWriter xmlWriter = new org.dom4j.io.XMLWriter(
                    new FileOutputStream(fileName));
            xmlWriter.write(doc);
            xmlWriter.close();
        }
        catch (Exception e) {
            System.out.println(e);
        }
    }
  
   至此DOM4J的问题编码问题算是告一段落,希望对此文章对其他朋友有用。


下载dom4j后,在其文档中就用详细的使用说明,我又将其封装了一下:

package org.tju.msnrl.butil;

import java.io.*;
import java.util.*;
import org.dom4j.*;
import org.dom4j.io.XMLWriter;
import org.dom4j.io.SAXReader;

/**
 * Dom4j封装类
 * <p>Title: 天津大学博士后流动站</p>
 * <p>Description: 天津大学人事处制作维护</p>
 * <p>Copyright: Copyright (c) 2005</p>
 * <p>Company: 天津大学软件学院.NET实验室(MSNRL)</p>
 * @author Jonathan Q. Bo
 * @version 1.0
 */

public class BDom4j {
  /**XML文件路径*/
  private String XMLPath = null;
  /**XML文档*/
  private Document document = null;

  public BDom4j() {
  }

  /**
   * 初始化xml文件
   * @param XMLPath 文件路径
   */
  public BDom4j(String XMLPath){
    this.XMLPath = XMLPath;
  }

  /**
   * 打开文档
   */
  public void openXML(){
    try{
      SAXReader reader = new SAXReader();
      this.document = reader.read(this.XMLPath);
      System.out.println("openXML() successful ...");
    }catch(Exception e){
      System.out.println("openXML() Exception:" + e.getMessage());
    }
  }
 
  /**
   * 创建文档
   * @param rootName 根节点名称
   */
  public void createXML(String rootName){
    try{
      this.document = DocumentHelper.createDocument();
      Element root = document.addElement(rootName);
      System.out.println("createXML() successful...");
    }catch(Exception e){
      System.out.println("createXML() Exception:" + e.getMessage());
    }
  }

  /**
   * 添加根节点的child
   * @param nodeName 节点名
   * @param nodeValue 节点值
   */
  public void addNodeFromRoot(String nodeName, String nodeValue){
    Element root = this.document.getRootElement();
    Element level1 = root.addElement(nodeName);
    level1.addText(nodeValue);
  }

  /**
   * 打开文档
   * @param filePath 文档路径
   */
  public void openXML(String filePath){
    try{
      SAXReader saxReader = new SAXReader();
      this.document = saxReader.read(filePath);
      System.out.println("openXML(String filePath) successful ...");
    }catch(Exception e){
      System.out.println("openXML() Exception:" + e.getMessage());
    }
  }

  /**
   * 保存文档
   */
  public void saveXML(){
    try{
      XMLWriter output = new XMLWriter(new FileWriter(new File(this.XMLPath)));
      output.write(document);
      output.close();
      System.out.println("saveXML() successful ...");
    }catch(Exception e1){
      System.out.println("saveXML() Exception:" + e1.getMessage());
    }
  }

  /**
   * 保存文档
   * @param toFilePath 保存路径
   */
  public void saveXML(String toFilePath) {
    try {
      XMLWriter output = new XMLWriter(new FileWriter(new File(toFilePath)));
      output.write(document);
      output.close();
    }
    catch (Exception e1) {
      System.out.println("saveXML() Exception:" + e1.getMessage());
    }
  }


  /**
   * 获得某个节点的值
   * @param nodeName 节点名称
   */
  public String getElementValue(String nodeName){
    try {
      Node node = document.selectSingleNode("//" + nodeName);
      return node.getText();
    }
    catch (Exception e1) {
      System.out.println("getElementValue() Exception:" + e1.getMessage());
      return null;
    }
  }

  /**
   * 获得某个节点的子节点的值
   * @param nodeName
   * @param childNodeName
   * @return
   */
  public String getElementValue(String nodeName, String childNodeName){
    try {
      Node node = this.document.selectSingleNode("//" + nodeName + "/" + childNodeName);
      return node.getText();
    }
    catch (Exception e1) {
      System.out.println("getElementValue() Exception:" + e1.getMessage());
      return null;
    }
  }

  /**
   * 设置一个节点的text
   * @param nodeName 节点名
   * @param nodeValue 节点值
   */
  public void setElementValue(String nodeName, String nodeValue){
    try{
      Node node = this.document.selectSingleNode("//" + nodeName);
      node.setText(nodeValue);
    }catch(Exception e1){
      System.out.println("setElementValue() Exception:" + e1.getMessage());
    }
  }

  /**
   * 设置一个节点值
   * @param nodeName 父节点名
   * @param childNodeName 节点名
   * @param nodeValue 节点值
   */
  public void setElementValue(String nodeName, String childNodeName,
                              String nodeValue) {
    try {
      Node node = this.document.selectSingleNode("//" + nodeName + "/" + childNodeName);
      node.setText(nodeValue);
    }
    catch (Exception e1) {
      System.out.println("setElementValue() Exception:" + e1.getMessage());
    }
  }

}

简单封装后,可以用来读写XML文档,对网站进行配置:如指定页面整体风格的css文件,在context init 时读取存入context中,在页面中通过读取context中的相应属性来确定css文件名,完成一项配置,其它的动态配置都类似;

public class BListener extends HttpServlet implements ServletContextListener, ServletContextAttributeListener, HttpSessionListener, HttpSessionAttributeListener {
  private static String XML_FILE_PATH = "c:/test.xml";

  //Notification that the web application is ready to process requests
  public void contextInitialized(ServletContextEvent sce) {
    BDom4j xmlmng = new BDom4j(XML_FILE_PATH);
    xmlmng.openXML();
    sce.getServletContext().setAttribute("css",xmlmng.getElementValue("style-sheet"));
    System.out.println("### context initialized...");
  }

[转 ]Dom4j的使用1

Dom4j 使用简介

作者:冰云 icecloud(AT)sina.com

时间:2003.12.15

 
版权声明:
本文由冰云完成,首发于CSDN,未经许可,不得使用于任何商业用途。
文中代码部分引用自DOM4J文档。
欢迎转载,但请保持文章及版权声明完整。
如需联络请发邮件:icecloud(AT)sina.com
 

    DOM4J是dom4j.org出品的一个开源XML解析包,它的网站中这样定义:

Dom4j is an easy to use, open source library for working with XML, XPath and XSLT on the Java platform using the Java Collections Framework and with full support for DOM, SAX and JAXP.

Dom4j是一个易用的、开源的库,用于XMLXPathXSLT。它应用于Java平台,采用了Java集合框架并完全支持DOMSAXJAXP

DOM4J使用起来非常简单。只要你了解基本的XML-DOM模型,就能使用。然而他自己带的指南只有短短一页(html),不过说的到挺全。国内的中文资料很少。因而俺写这个短小的教程方便大家使用,这篇文章仅谈及基本的用法,如需深入的使用,请……自己摸索或查找别的资料。

之前看过IBM developer社区的文章(参见附录),提到一些XML解析包的性能比较,其中DOM4J的性能非常出色,在多项测试中名列前茅。(事实上DOM4J的官方文档中也引用了这个比较)所以这次的项目中我采用了DOM4J作为XML解析工具。

在国内比较流行的是使用JDOM作为解析器,两者各擅其长,但DOM4J最大的特色是使用大量的接口,这也是它被认为比JDOM灵活的主要原因。大师不是说过么,“面向接口编程”。目前使用DOM4J的已经越来越多。如果你善于使用JDOM,不妨继续用下去,只看看本篇文章作为了解与比较,如果你正要采用一种解析器,不如就用DOM4J吧。

它的主要接口都在org.dom4j这个包里定义:


Attribute定义了XML的属性
Branch为能够包含子节点的节点如XML元素(Element)和文档(Docuemnts)定义了一个公共的行为,
CDATA 定义了XML CDATA 区域
CharacterData是一个标识借口,标识基于字符的节点。如CDATA,Comment, Text.
Comment 定义了XML注释的行为
定义了XML文档
DocumentType 定义XML DOCTYPE声明
Element定义XML 元素
ElementHandler定义了 Element 对象的处理器
ElementHandler 使用,用于取得当前正在处理的路径层次信息
Entity定义 XML entity
Node为所有的dom4j中XML节点定义了多态行为
NodeFilter 定义了在dom4j节点中产生的一个滤镜或谓词的行为(predicate)
ProcessingInstruction 定义 XML 处理指令.
Text 定义XML 文本节点.
Visitor 用于实现Visitor模式.
XPath 在分析一个字符串后会提供一个XPath 表达式

看名字大致就知道它们的涵义如何了。

要想弄懂这套接口,关键的是要明白接口的继承关系:




一目了然,很多事情都清楚了。大部分都是由Node继承来的。知道这些关系,将来写程序就不会出现ClassCastException了。

下面给出一些例子(部分摘自DOM4J自带的文档),简单说一下如何使用。

1.              读取并解析XML文档:

读写XML文档主要依赖于org.dom4j.io包,其中提供DOMReader和SAXReader两类不同方式,而调用方式是一样的。这就是依靠接口的好处。

 
    // 从文件读取XML,输入文件名,返回XML文档
    public Document read(String fileName) throws MalformedURLException, DocumentException {
       SAXReader reader = new SAXReader();
       Document document = reader.read(new File(fileName));
       return document;
    }
 

其中,reader的read方法是重载的,可以从InputStream, File, Url等多种不同的源来读取。得到的Document对象就带表了整个XML。

根据本人自己的经验,读取的字符编码是按照XML文件头定义的编码来转换。如果遇到乱码问题,注意要把各处的编码名称保持一致即可。

2.    取得Root节点

读取后的第二步,就是得到Root节点。熟悉XML的人都知道,一切XML分析都是从Root元素开始的。

 
   public Element getRootElement(Document doc){
       return doc.getRootElement();
    }
 

3.    遍历XML树

DOM4J提供至少3种遍历节点的方法:

1) 枚举(Iterator)

 
    // 枚举所有子节点
    for ( Iterator i = root.elementIterator(); i.hasNext(); ) {
       Element element = (Element) i.next();
       // do something
    }
    // 枚举名称为foo的节点
    for ( Iterator i = root.elementIterator(foo); i.hasNext();) {
       Element foo = (Element) i.next();
       // do something
    }
    // 枚举属性
    for ( Iterator i = root.attributeIterator(); i.hasNext(); ) {
       Attribute attribute = (Attribute) i.next();
       // do something
    }

2)递归

递归也可以采用Iterator作为枚举手段,但文档中提供了另外的做法

 
    public void treeWalk() {
       treeWalk(getRootElement());
    }
    public void treeWalk(Element element) {
       for (int i = 0, size = element.nodeCount(); i < size; i++)     {
           Node node = element.node(i);
           if (node instanceof Element) {
              treeWalk((Element) node);
           } else { // do something....
           }
       }
}
 

3) Visitor模式

最令人兴奋的是DOM4J对Visitor的支持,这样可以大大缩减代码量,并且清楚易懂。了解设计模式的人都知道,Visitor是GOF设计模式之一。其主要原理就是两种类互相保有对方的引用,并且一种作为Visitor去访问许多Visitable。我们来看DOM4J中的Visitor模式(快速文档中没有提供)

只需要自定一个类实现Visitor接口即可。

 
        public class MyVisitor extends VisitorSupport {
           public void visit(Element element){
               System.out.println(element.getName());
           }
           public void visit(Attribute attr){
               System.out.println(attr.getName());
           }
        }
 
        调用:  root.accept(new MyVisitor())

    Visitor接口提供多种Visit()的重载,根据XML不同的对象,将采用不同的方式来访问。上面是给出的Element和Attribute的简单实现,一般比较常用的就是这两个。VisitorSupport是DOM4J提供的默认适配器,Visitor接口的Default Adapter模式,这个模式给出了各种visit(*)的空实现,以便简化代码。

    注意,这个Visitor是自动遍历所有子节点的。如果是root.accept(MyVisitor),将遍历子节点。我第一次用的时候,认为是需要自己遍历,便在递归中调用Visitor,结果可想而知。

4. XPath支持

    DOM4J对XPath有良好的支持,如访问一个节点,可直接用XPath选择。

 
   public void bar(Document document) {
        List list = document.selectNodes( //foo/bar );
        Node node = document.selectSingleNode(//foo/bar/author);
        String name = node.valueOf( @name );
     }
 

    例如,如果你想查找XHTML文档中所有的超链接,下面的代码可以实现:

 
    public void findLinks(Document document) throws DocumentException {
        List list = document.selectNodes( //a/@href );
        for (Iterator iter = list.iterator(); iter.hasNext(); ) {
            Attribute attribute = (Attribute) iter.next();
            String url = attribute.getValue();
        }
     }
 

5. 字符串与XML的转换

有时候经常要用到字符串转换为XML或反之,

 
    // XML转字符串
  Document document = ...;
    String text = document.asXML();
// 字符串转XML
    String text = <person> <name>James</name> </person>;
    Document document = DocumentHelper.parseText(text);
 

6 用XSLT转换XML

 
   public Document styleDocument(
       Document document,
       String stylesheet
    ) throws Exception {
    // load the transformer using JAXP
    TransformerFactory factory = TransformerFactory.newInstance();
    Transformer transformer = factory.newTransformer(
       new StreamSource( stylesheet )
    );
    // now lets style the given document
    DocumentSource source = new DocumentSource( document );
    DocumentResult result = new DocumentResult();
    transformer.transform( source, result );
    // return the transformed document
    Document transformedDoc = result.getDocument();
    return transformedDoc;
}
 

7. 创建XML

  一般创建XML是写文件前的工作,这就像StringBuffer一样容易。

 
    public Document createDocument() {
       Document document = DocumentHelper.createDocument();
       Element root = document.addElement(root);
       Element author1 =
           root
              .addElement(author)
              .addAttribute(name, James)
              .addAttribute(location, UK)
              .addText(James Strachan);
       Element author2 =
           root
              .addElement(author)
              .addAttribute(name, Bob)
              .addAttribute(location, US)
              .addText(Bob McWhirter);
       return document;
    }
 

8. 文件输出

    一个简单的输出方法是将一个Document或任何的Node通过write方法输出

 
    FileWriter out = new FileWriter( foo.xml );
    document.write(out);
 

  如果你想改变输出的格式,比如美化输出或缩减格式,可以用XMLWriter类

 
    public void write(Document document) throws IOException {
       // 指定文件
       XMLWriter writer = new XMLWriter(
           new FileWriter( output.xml )
       );
       writer.write( document );
       writer.close();
       // 美化格式
       OutputFormat format = OutputFormat.createPrettyPrint();
       writer = new XMLWriter( System.out, format );
       writer.write( document );
       // 缩减格式
       format = OutputFormat.createCompactFormat();
       writer = new XMLWriter( System.out, format );
       writer.write( document );
    }
 

如何,DOM4J够简单吧,当然,还有一些复杂的应用没有提到,如ElementHandler等。如果你动心了,那就一起来用DOM4J.

DOM4J官方网站:(我老连不上)


DOM4J下载(SourceForge),最新版本为1.4

 
 
另:
List list = document.selectNodes("//class");
请问一下//class前面的//是什么意思。
还有List list = document.selectNodes( //a/@href );的@表示什么
 
//指的是根目录!
@指的是a的attribute(href)
3月7日

[转]Hibernate数据更新——Session.update/Session.saveOrUpdate

 
Hibernate数据更新——Session.update/Session.saveOrUpdate

Session.update执行步骤:

  1. 首先根据待更新实体对象的Key,在当前session的内部缓存中进行查找,如果发现,则认为当前实体对象已经处于Persistent状态,返回。从这一点可以看出,对一个Persistent状态的实体对象调用update语句并不会发生任何作用。
  2. 初始化实体对象的状态信息(作为之后脏数据检查的依据),并将其纳入内部缓存。注意这里Session.update方法本身并没有发送Update SQL完成数据更新操作,Update SQL将在之后的Session.flush方法中执行(Transaction.commit在真正提交数据库事务之前会调用Session.flush)。

Session.saveOrUpdate执行步骤:

  1. 首先在Session内部缓存中进行查找,如果发现则直接返回。
  2. 执行实体类对应的Interceptor.isUnsaved方法(如果有的话),判断对象是否为未保存状态。
  3. 根据unsaved-value判断对象是否处于未保存状态。
  4. 如果对象未保存(Transient状态),则调用save方法保存对象。
  5. 如果对象未已保存(Detached状态),则调用update方法将对象与Session重新关联。

可以看到,saveOrUpdate实际上时save和update方法的组合应用。它本身并没有增加新的功能特性。