arm-linux

http://armboard.taobao.com/

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::
 

                                          OXmlEd1.11使用指南

 

                                                                                         沈东良

                                                                                                               2007-8-22

   

简介

诞生

OXmlEd项目是一个“对象--XML映射”(Object-Xml Mapping)的类库。它的目的是帮助开发者方便、快速的从XML文件构建出Java对象,从Java对象生成出相应的XML文件。

OXmlEd是一个开源的项目,诞生于200789,由沈东良(英文名Edward Shen,网名良少)创建,项目地址:https://sourceforge.net/projects/oxmled/

在我的Blog上也会有相应的介绍文章。http://blog.csdn.net/shendl/

 

概况

OXmlEd项目最新的版本是1.11版本。一共经历了3个版本。1.0版本,实现了编程方式的“对象—XML映射”。1.1版本实现了标注(Annotation)方式的“对象—XML映射”。最新的1.11版本是对1.1版本的完善的BUG修复。

目前,还没有经受全面的测试,可能会有一些问题,欢迎用户到项目网站或者我的BlogEmail上报告BUG

通过OXmlEd项目的开发,我深深体会到做开源软件的不易!平时工作就很忙,周末又有私事,因此留给开发开源软件的时间就更加少了。

但是,既然我创建了OXmlEd项目,就必须尽快把它做完,不能半途而废。因此,开发OXmlEd项目是在死亡进度的恶劣条件下开发的。测试驱动开发,一些设计、重构等等,能省则省,只为了在及其有限的时间内开发出尽可能多的功能。因此,项目的质量有所下降。等功能开发完毕之后,如果有时间,我将好好重构代码。

   

使用OXmlEd进行“对象—XML映射”

使用标注把你的Java对象映射为XML文件

使用OXmlEd库,你只需要使用2个标注(XMLNodeXMLAttribute)就可以方便地把任意java类表示成XML文件,也可以从生成的XML文件重新得到Java对象。

Test源文件夹下的net.sf.oxmled.mapping.sample包下的Test类和Person类是2个测试类。

参考它们,你可以为你自己的java类添加标注,实现与XML文件的映射。

 

Test示例

/**

 * 1,根节点,应该指定为root

 * 2,如果编码是默认的utf-8,那么属性值可以是中文,但属性的名字,节点的名字等如果是中文,就会是乱码。

 * 因此,设为gbk

 *    只有根节点需要在类定义上标注XMLNode,如果是作为子节点,则不需要在类上进行标注。 可以在属性上添加标注!

 *   

 *    注意:在基类上声明属性是无用的!  以为无法反射得到那些私有字段!

 */

@XMLNode(root=true,encoding="gbk")

public class Test {

       /**

        * 属性。

        * 指定了从xml文件的属性值转到Java类的字段的值的方法。 通过这个方法,可以根据XML文件的相应属性的String值,得到java对象中

        * 该属性的实际的int值。

        */

       @XMLAttribute(castMethod=IConvertCastUtil.castInt)

       private int a=1;

       /**

        * 上面的castMethod=IConvertCastUtil.castInt 并没有实际指定使用这个方法。而是用了一个专门的工具类的方法。

        *   查找的顺序是,那个工具类的方法,如果没有,就找本地的方法。

        *   如果上面的Annotation改为这样:

        *  @XMLAttribute(castMethod="castInt")

        *  就会使用这个方法进行转换。

        * @param value

        * @return

        */

       private int castInt(String value){

              return Integer.parseInt(value);

             

       }

       /**

        * 指定这个字段是需要映射为XML文件的数据项

        */

       @XMLAttribute()

       private String b="BBB";

       @XMLAttribute(castMethod="castBoolean")

       private boolean bool=false;

       /**

        *

        * @param value

        * @return

        */

       private boolean castBoolean(String value){

             

              return Boolean.parseBoolean(value);

       }

      

       @XMLAttribute(castMethod="castDouble")

       private double dd=245.32d;

       /**

        *

        * @param value

        * @return

        */

       private double castDouble(String value){

              return Double.parseDouble(value);

       }

      

       /**

        * 这个属性,保存在xml文件中时,使用“我们”作为属性的名字。

        */

       @XMLAttribute(name="我们",castMethod="castInt")

       private int aa=1;

       /**

        * 这个属性,保存在xml文件中时,使用"www"作为属性的名字。

        */

       @XMLAttribute(name="www")

       private String bb="BBB";

       @XMLAttribute(value="true",castMethod="castBoolean")

       private boolean boolbbb=false;

       /**

        * 这个属性,保存在xml文件中时,使用"dddda"作为属性的名字。使用固定的"111.32d"作为属性的值

        */

       @XMLAttribute(name="dddda",value="111.32d",castMethod="castDouble")

       private double ddd=245.32d;

 

      

       @XMLAttribute

       private String str="String";

       /**

        * 子节点  可以是一个单独的类,也可以是一个数组,一个Collection接口的实现类!

        *   

        */

       @XMLNode()

       private TestB testB=new TestB();

      

       @XMLNode()

       private TestB[] child=new TestB[]{new TestB(),new TestB(),null,new TestB(),null,new TestB()};

       /**

        * 子节点

        */

       @XMLNode()

       private AttributeList childList=new AttributeList();

      

 

       /**

        *

        */

       public Test() {

              /*

               *

               */

              this.testB.setSsss("I love U!");

              this.childList.add("sssssssss");

              this.childList.add(new TestB());

       }

 

       /**

        * @param args

        * @throws Exception

        * @throws Exception

        */

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

              /*

               *

               */

              //IXmlUtil util=new XmlUtil();

              //util.loadXmlFile(ClassLoaderUtil.getResource("net.sf.oxmled.mapping.sample.Test"));

             

              Test test=new Test();

              System.out.println(test.a);

              //这是执行Object-XML Mapping的助手类

              IOXmlMapping oXmlMapping=new OXmlMapping();

              try {

                     //java对象生成为一个xml文件。 这里会放在程序的根路径下。名字是该对象的类名。

                     //你也可以指定生成的位置和文件名。

                    oXmlMapping.save(test);

                    /**

                     * load方法,根据xml文件,生成Java对象!

                     */

                     Test test2 =(Test) oXmlMapping.load("net.sf.oxmled.mapping.sample.Test");

                     System.out.println(test2.a);

                     System.out.println(test2.aa);

                    

              } catch (IllegalArgumentException e) {

                     /*

                     *

                     */

                     e.printStackTrace();

              } catch (MalformedURLException e) {

                     /*

                     *

                     */

                     e.printStackTrace();

              } catch (URISyntaxException e) {

                     /*

                     *

                     */

                     e.printStackTrace();

              } catch (IllegalAccessException e) {

                     /*

                     *

                     */

                     e.printStackTrace();

              } catch (IOException e) {

                     /*

                     *

                     */

                     e.printStackTrace();

              } catch (Exception e) {

                     /* TODO 自动生成 catch

                      *

                      */

                     e.printStackTrace();

              }

 

       }

 

}

 

 

TestB类的源代码:

public class TestB {

       @XMLAttribute()

       private String ssss="as";

 

       public String getSsss() {

              return ssss;

       }

 

       public void setSsss(String ssss) {

              this.ssss = ssss;

       }

       public TestB() {

              /*

               *

               */

       }

}

Person示例

@XMLNode(root=true)

public class Person extends AbstractPerson implements java.io.Serializable {

       // Fields

    @XMLAttribute(castMethod=IConvertCastUtil.castWrappedLong)

       private Long id;

    @XMLAttribute

       private String number;

    @XMLAttribute

       private String name;

       private Byte type;

       /**

        * 因为这个子节点是集合,而其声明的类型是一个接口。所以,需要我们提供 具体的 集合类。java.util.HashSet

        * 注意:  你必须提供有无参构造器的类,否则将无法通过反射创建对象!

        */

       @XMLNode(concreteClass="java.util.HashSet")

       private Set licenseTags = new HashSet(0);

……

 

 

使用方法

注意点:   

1,所有标注为XMLNodeXMLAttribute的对象,或者集合中的对象,其类型都必须是有无参构造器的Java类。否则,将无法使用IOXmlMapping接口的load方法从XML文件中恢复Java对象。

2,XMLAttribute 可以标注在一个表示根节点或者子节点的Java类的属性上,不论其访问类型是什么都行。但不能放在基类上。

基类上标注的XMLAttribute是不能访问的。(下一个版本将修复这个问题,所有层次、可访问性的字段都可以加上XMLAttribute标注)

 

3,作为XML文件的根节点的java对象。该类需要使用@XMLNodeClass上进行声明。

如果不是作为XML文件的根节点,则无需在该类的Class上使用@XMLNode

一个字段作为子节点,那么就在该字段上标上@XMLNode

 

 

 

 

 

2个标注:

XMLNode

/**

*   XML类型标注,标注在业务对象上,表示:

 *1name---节点的名字

 *2root---是否根节点

 *3encoding---编码

 *     可继承,运行时,目标是类型,文档可见

 *     

 *       用于2处:

 * 1,根节点,标注在类上。

 * 2,子节点,标注在字段上。

 *

 *    如果标注于基类的字段上,则无法反射!

 *      

 */

@Documented

@Retention(value=RetentionPolicy.RUNTIME)

@Inherited

@Target({ElementType.TYPE,ElementType.FIELD})

public @interface XMLNode {

      

        /**

         * 编码

         * @return

         */

        String encoding() default "utf-8";

        /**

         * 是否根节点 默认不是根节点

         * @return

         */

        boolean root() default false;

        /**

               * 节点的名字

               *    如果为"",使用类名作为节点的名字

               *    或者使用字段名作为节点名字---如果是子节点

               * @return

               */

               String name() default "";

               /**

                * 节点的内容

                *     默认是""字符串。  表示没有

                *     "toString"   表示使用 该对象的toString()方法的返回值作为 内容。

                *    

                * @return

                  但是,我们无法正确的从 节点的 text中生成正确的数据!  因为toString()产生的数据,如何逆向构建对象?没有办法!

               String text() default "";

                */

             

               

               /**

                * 具体的实现类,一般只有 类型 数组或者集合的子节点 才需要设置它。

                *     因为,从xml文件生成Java对象时,需要我们自己创建一个空的数组或者集合。

                *     而类中可能使用了基类或者接口来引述它们!

               * @return

               */

              String concreteClass() default "";

}

 

说明

 

 

 

XMLAttribute

/**

*   用于标注字段为属性

 *   如果标注于基类的字段上,则无法反射!

 *  

 *   如果字段是iterator或者数组,则生成多个xml属性

 */

@Documented

@Retention(value=RetentionPolicy.RUNTIME)

@Target({ElementType.FIELD})

@Inherited

public @interface XMLAttribute {

       /**

        *   属性的名字,默认就取是变量的名字

        * @return

        */

       String name() default "";

       /**

        * 默认就取 变量的值的toString()方法的值。

        * @return

        */

       String value() default "";

       /**

        * 转换 xml文件中的属性的字符串为需要的类型的方法。

        * 转换方法的签名如下:

        * IConvertCastUtil

        *  Type castAAA(String value);

        * @return

        */

       String castMethod() default "";

       /**

        * 目前未启用

        *     这个属性用于指出该字段如何转为xml文件中的String

        *     默认就是该字段的toString方法生成的数据。

        *     某些情况下,可能你需要提供这样的方法。

        * @return    根据Java对象生成的属性的值

        */

       String toStringMethod() default "";

      

       //ConvertCastMethod defaultCast() default ConvertCastMethod.none;

 

}

 

说明

castMethod中,如果指定方法。如果在ConvertCastUtil类中已经有了,可以通过IConvertCastUtil接口的常量指定。

    如果没有现存的方法,可以在java类中自定义转换的方法。castMethod=”方法名字。系统将会自动调用这个转换方法。

 

 

OXmlEd工作原理

上面,你已经看到了OXmlEd库的使用标注实现“对象—XML映射”的方法。是非常简单的。不需要你进行复杂的配置,也不需要你提供XML文件的Schema等复杂的工作。

下面谈谈OXmlEd库的工作原理:

1,OXmlEd库建立在XML操作类库之上。

OXmlEd显然需要操作XML文件。“不重复发明轮子”。因此,我们目前在底层使用了Dom4j这个类库来具体操作XML文件。

OXmlEd贯彻了使用接口“解耦”的思想,完全可以使用其它XML操作类库来替代Dom4j,而不会对OXmlEd库的用户不会造成任何影响。实际上,我曾经用jdom替代过Dom4j

2,OXmlEd库在Dom4J等类库上提供了一个助手类,可以帮助你简化一些XML操作的工作,可以看作是对Dom4J等的增强。它们存放在IXmlUtil接口和XmlUtil类中。

3,OXmlEd库有一个自己的核心的对象INode对象。这个对象可以代表所有XML文件。它是OXmlEd库的核心。

INode接口既不依赖于底层的XML操作库,也不依赖于用户需要实现的“对象—XML映射”业务类。

它的作用是:所有java类和XML文件之间的桥梁。

一个Java类对象只要转换成对等的INode对象,就可以通过OXmlEd库生成XML文件。

一个INode对象只要能够转换成为一个Java类的对象,就可以通过OXmlEd库从一个XML文件直接生成一个Java类的对象。

上图是编程方式实现“对象—XML映射”的OXmlEd库结构图

 

 

上图是标注方式实现“对象—XML映射”的OXmlEd库结构图

 

 

编程方式实现“对象—XML映射”

其实,OXmlEd库,最开始的目的,是使用编程方式实现“对象—XML映射”。

只是在OXmlEd这个开源项目发布1.0版之后,我看了一下其他的“对象—XML映射”的实现方案,发现有一些使用配置数据来实现“对象—XML映射”。

因此,我才开发了OXmlEd库的1.1版本。在OXmlEd库的核心:INodeINodeService层上又开发了一个层次:标注和IOXmlMapping层。

实现了使用标注在Java对象和XML文件之间互相映射的功能。

对于一些常用的“对象—XML映射”需求,使用标注确实要简单、迅速很多。

但是,你也可能遇到一些标注方式无法实现“对象—XML映射”的特殊情况。用户的需求常常都会出你意表,稀奇古怪!

这时,你就可以在INodeINodeService这个层次上使用OXmlEd库,通过简单的编程,实现“对象—XML映射”。

你需要在自己的java类中提供一个方法,根据INode对象创建该java类的实例。

需要在自定义的INode实现类中提供一个方法,根据你的Java类的对象,创建INode对象的一个实例。

有了这2个方法,你就可以实现任意java类和INode对象之间的映射,借助OXmlEd的功能,也就能够实现任意java类和XML文件之间的映射。

 

另外,我推荐你使用ChangeListener事件监听器,动态实现业务对象和INode对象之间的同步。

我提供了2INode接口的实现类,帮助你使用ChangeListener事件监听器。我认为ChangeListener事件监听器是最合适的“观察者模式”的实现!

EventListenerNode类可以被实现ChangeListener接口的对象所监听。

ChangeListenerNode类实现了ChangeListener接口,可以监听实现了IEventListenerRegistry接口的业务对象。

你可以让INode和你的Java类互相监听数据的变化,从而实现INodeJava类之间的时刻同步,也就是实现Java类和XML文件之间的时刻同步。

 

 

使用XML格式作为配置文件

OXmlEd项目还提供了使用XML格式作为配置文件的助手类。

以前,我们一般使用属性文件作为配置文件。OXmlEd提供了一套助手类,可以让你非常方便的使用XML文件来保存你的配置信息。

1,IConfig接口,这是管理XML文件的java助手类的接口。

2,OXmlEdConfig类,这是IConfig接口的一个实现类,可以使用它来管理XML配置文件。

这是使用了OXmlEd库的INodeINodeService接口的方法实现的。

3,XmlUtilConfig类,它也是IConfig接口的一个实现类。它的功能和OXmlEdConfig类相同,只是实现方法不同。

它使用了OXmlEd库的IXmlUtil接口和Dom4jDocument类实现的。也就是说,它实际上是在OXmlEd提供的助手类的帮助下用Dom4j实现的。

之所以同时提供了2个实现类,也是希望这2个类能够作为例子,帮助用户使用OXmlEd库。

 

 

XML配置文件的格式

IConfig能够读取的XML配置文件的格式大致是这个样子的。

我对Scheml没什么研究,也没时间写严格的xml格式。

/**

 * 方便的读取xml格式的配置文件。

 * xml文件的类型应该是类似于这样的:

 * <?xml version="1.0" encoding="gbk"?>

<!-- 配置资料 -->

<configs>

   <maps>

    <!--

    单个值

   <map>

       <key>RMIServerIp</key>

       <value>127.0.0.1</value>

   </map>

   多个值

   <map>

       <key>RMIServerIp</key>

       <values>127.0.0.1</values>

       <values>127.0.0.2</values>

   </map>

   <map>

       <key>RMIServerIp</key>

       <value>127.0.0.1</value>

       <values>127.0.0.1</values>

       <values>127.0.0.2</values>

   </map>

   -->

   </maps>

</configs>

 

下面做一下讲解:

<map>

       <key></key>

       <value></value>

   </map>

用来保存一个名值对。

IConfig. get(String key);方法可以得到String值。

<map>

       <key></key>

       <values></values>

       <values></values>

   </map>

可以用来保存这样的名值对:一个名字,对应多个值。

IConfig. gets(String key)方法可以得到List<String>这样的值。

<map>

       <key>RMIServerIp</key>

       <value>127.0.0.1</value>

       <values>127.0.0.2</values>

       <values>127.0.0.3</values>

   </map>

这样的一个配置数据,可以通过IConfig. get(“RMIServerIp”)得到值:127.0.0.1

IConfig. gets(“RMIServerIp”)得到一个List,包含127.0.0.2127.0.0.3

 

Iconfig接口的put(String key, String value)方法可以新增或修改XML配置文件的内容。修改

<map>

       <key></key>

       <value></value>

</map>

 

Iconfig接口的put(String key, List<String> values)方法可以新增或修改XML配置文件的内容。修改

<map>

       <key></key>

       <values></values>

       <values></values>

   </map>

 

 

未来版本会添加的功能

OXmlEd项目未来版本会增加一些功能,目前摆上日程的有:

1,对节点的text内容的支持。不止在属性中保存数据,也会在节点的内容中保存数据。

2,会重构代码。目前,所有转换代码写在固定的类ConvertCastUtil中。未来会变为可配置的。不管什么类都可以。

 

 

 

 

 

 

 

 

 

 

 

 

IConfig的2个实现类,都使用工厂方法创建对象,通过对象实例能够操作XML配置文件。
getInstance()方法返回单例。操作项目路径下的config/config.xml文件。
getNewInstance()方法返回的是多例。每一次都会读取config/config.xml配置文件。
getInstance(String configFile)和getNewInstance(String configFile)这2个方法,读取的是你需要读取的配置文件。请在参数中指定Java程序路径下的配置文件。
如,OXmlEd项目的使用的是config/OXmlEdConfig.xml配置文件。你也可以指定你自己的xml配置文件的路径!
posted on 2007-08-23 00:05  arm-linux  阅读(223)  评论(0编辑  收藏  举报