jibx基础简介一
一 JiBX 简介 JiBX 是一个绑定 XML 数据到 Java 对象的框架。JiBX 用一个绑定定义文挡(binding definition document)来定义 XML 数据与 Java 对象转换的规则,这个文挡就是联系 XML 数据与 Java 对象之间的桥梁。 这里有必要先介绍两个数据绑定术语 marshal 和 unmarshal,marshal 是由 Java 对象生成 XML 文挡,unmarshal 是根据 XML 文挡建立 Java 对象。 使用 JiBX 的过程分成两个过程,一个是 binding compiler,另一个是 binding runtime。binding compiler 是一个前期准备过程,包括定义绑定定义文挡,定义与 XML 绑定在一起的 Java 对象,然后编译。在这个过程,JiBX 比其他项目在操作上要简单,不用定义 DTD 和 Schema,缺点是需要自己定义 Java 程序。binding runtime 是使用 binding compiler 编译好的 Java class 处理 XML 数据。 JiBX 也用到了第三方的工具 XPP3 Pull Parser 和 BCEL,在 JiBX 发布的文件中也包含了这两个工具相关的文件。 二 简单范例 按照惯例,还是从最简单的例子说起。 首先从 JiBX 网站下载 JiBX,当前最新版本是 beta 3。解开下载的 zip 文件,里面有一个 lib 目录,包含了 bcel.jar, jibx-bind.jar, jibx-extras.jar, jibx-run.jar, xpp3.jar 五个 jar 文件。bcel.jar, jibx-bind.jar 只有在 binding compiler 的时候才用得到。jibx-extras.jar 是一个可选的工具包,里面有一些测试和验证的工具类。 1.定义一个我们将要处理 XML 文件,文件名为 data.xml,内容如下: <customer> <person> <cust-num>123456789</cust-num> <first-name>John</first-name> <last-name>Smith</last-name> </person> <street>12345 Happy Lane</street> <city>Plunk</city> <state>WA</state> <zip>98059</zip> <phone>888.555.1234</phone> </customer> 这个 XML 文件非常简单,共有十个元素,没有属性。根元素 customer 有 person, street, city, state, zip, phone 六个子元素。其中元素 person 有 cust-num, first-name, last-name 三个子元素。 2.接着定义两个 Java 类 Customer 和 Person,也采用最简单的方式,用对象的域值对应元素,内容如下: public class Customer { public Person person; public String street; public String city; public String state; public Integer zip; public String phone; } public class Person { public int customerNumber; public String firstName; public String lastName; } 这个两个类没有任何方法,够简单吧!或许你已经看出来了,Customer 类的七个 field 对应的是 XML 文件中 customer 元素的七个子元素。Person 类的三个 field 对应的是 person 元素的三个子元素。在 Person 类的 field 的名称并不是和 person 元素的子元素名称完全相等,这是遵守 Java 编程规范 field 命名的需要,虽然不相等,但这不重要,可以在绑定定义文挡中把它们一一对应起来。 3.绑定定义文挡 绑定定义文挡是依据绑定定义规范将 XML 数据和 Java 对象绑定的 XML 文挡。文件名为 binding.xml,内容如下: <binding> <mapping name="customer" class="Customer"> <structure name="person" field="person"> <value name="cust-num" field="customerNumber"/> <value name="first-name" field="firstName"/> <value style="text" field="lastName"/> </structure> <value name="street" field="street"/> <value name="city" field="city"/> <value name="state" field="state"/> <value name="zip" field="zip"/> <value name="phone" field="phone"/> </mapping> </binding> binding.xml 文件中的 name 和 field 属性分别将 XML 中的元素和 Java 对象中的 field 一一对应并绑定起来。 <mapping name="customer" class="Customer"> mapping 元素的 name 和 class 属性将 customer 根元素和 Customer 类绑定在一起。 <structure name="person" field="person"> public Person person; 上面两行定义了 person 是 Customer 的 field,同时也把 person 元素和 person 类绑定在一起。 4.执行 Binding Compiler 过程 以下命令是在 Linux 下执行,如果是 Windows 平台请转换成相应的命令 #javac Person.java #javac -classpath . Customer.java #java -jar lib/jibx-bind.jar binding.xml 执行完后,在当前目录多了四个 class 文件,分别是 Person.class, Customer.class, JiBX_bindingCustomer_access.class, JiBX_bindingFactory.class。 5.执行 binding runtime 过程 首先定制一个 XML 数据文件 data.xml, 内容如下: <customer> <person> <cust-num>123456789</cust-num> <first-name>John</first-name> <last-name>Smith</last-name> </person> <street>12345 Happy Lane</street> <city>Plunk</city> <state>WA</state> <zip>98059</zip> <phone>888.555.1234</phone> </customer> 接着写一个简单的读取 data.xml 测试程序 Test.java,内容如下: import java.io.FileInputStream; import java.io.FileNotFoundException; import org.jibx.runtime.JiBXException; import org.jibx.runtime.IBindingFactory; import org.jibx.runtime.BindingDirectory; import org.jibx.runtime.IUnmarshallingContext; class Test { public static void main(String[] args) { try{ IBindingFactory bfact = BindingDirectory.getFactory(Customer.class); IUnmarshallingContext uctx = bfact.createUnmarshallingContext(); Customer customer = (Customer)uctx.unmarshalDocument(new FileInputStream("data.xml"), null); Person person = customer.person; System.out.println("cust-num:" + person.customerNumber); System.out.println("first-name:" + person.firstName); System.out.println("last-name:" + person.lastName); System.out.println("street:" + customer.street); }catch(FileNotFoundException e){ System.out.println(e.toString()); }catch(JiBXException e){ System.out.println(e.toString()); } } } 编译并运行这个测试程序 #javac -classpath .:lib/jibx-run.jar Test.java #java -cp .:lib/jibx-run.jar:lib/xpp3.jar Test 程序运行的结果是 cust-num:123456789 first-name:John last-name:Smith street:12345 Happy Lane 基本类型与默认值 在JiBX绑定java对象时,如果你使用了基本类型,又使用了字段为可选值时,你得检查实际值与你的期望值是否相等。这样说起有点模糊,举个例子说明吧。如果java类里有一个名为intValue 的int类型的字段和一个名为stringValue的String类型的字段,同时又在绑定文件中为这两个字段配置为usage="optional",如: <value name="intValue" field="intValue" usage="optional"/> <value name="stringValue" field="stringValue" usage="optional" /> 也就是说这两个字段为可选值,可有可无。我们在编排xml文件时,我们可以通过判断stringValue是否等于null来确认是否要在xml文件里显示该element;而该字段是基本类型,比如说之前说的intValue,就不能通过null来判断了,JiBX的做法是判断基本类型是否等于默认值,也就是说判断intValue是否等于0;如果是0,就不显示该element,为非0值时,才能显示该element。 这样讲解后,你应该得出一个结论,如果让基本类型为可选项时,是无法输出默认值的。在解决这个问题上,我们不使用基本类型就行了,JDK5.0也支持自动装箱和拆箱了,再某些属性声明时不是用基本类型对我们的影响并不是很大;或者寄希望于JiBX以后的版本实现装箱和拆箱操作。 - 使用char时要小心 上面提到了在要绑定的字段里不推荐使用基本类型,如果你一定要使用基本类型,就得小心使用char类型,因为char类型比较特殊,你可以把它看作是只有一个字符的String,也可以看成是0-65535之间的一个数字,所以在JiBX使用时,你需要明确该类型字段在转换时的序列化和反序列化操作。幸运的是,JiBX提供了这样的序列化和反序列化类,在绑定文件里应加上如下可选属性: <value name="stringValue" field="stringValue" serializer="org.jibx.runtime.Utility.serializeCharString" deserializer="org.jibx.runtime.Utility.deserializeCharString"/> - 使用接口时需给出工厂方法 从xml文件转换到java对象时,是先要创建该类的实例的,如果转换的是一个实体类,创建实例就不会有什么问题,但是如果我们要使用接口编程,转换的对象就是一个接口,则会出现错误,因为JiBX并不清楚你需要创建这个接口的那个实例,这将会导致错误;所以我们需要在绑定文件中指明该接口的创建工厂方法。例如:我们声明一个List的属性listValue,想要它指向一个ArrayList的实例,我们则需要写一个返回ArrayList实例的工厂方法getArrayListInstance;并在绑定文件中添加factory可选属性,如下: <collection field="listValue" factory="com.gelc.JiBXInterfaceFactory.getArrayListInstance" /> 另外值得一提的就是,JiBX例子里的工厂方法的modifier是private,我用JUint对代码进行测试不会有问题,但是用ant运行同样的测试,就会有IllegalAccessError,这是JiBX存在的一个Bug;对于该问题,我建议将modifier改为public,这样就不会出现问题。 - 如何使用Map 在JiBX的核心包里是不支持Map类型的转换的,不过你不用担心,在JiBX的jibx-extras.jar附加包里提供了对Map的支持。你想使用Map还需要为它重写marshal/ummarshal方法,你不要被这个给吓倒了,因为JiBX已经为你提供了这个实现框架,你只需要作相应的修改就ok了。如果你想了解更多,可以参考JiBX提供的定制JiBX绑定方法指南。 - 中文问题又来了 说到java项目中的中文问题,我相信很多开发者都遇到过,JiBX对中文的支持也不太让人满意,因为JiBX只支持Java标准的字符集,也就是说我们常用的GB2312和GBK不在支持行列之内,如果你用的是UTF-8来处理中文,则没有任何问题,但是你想用GB2312或GBK编码来实现转换的话,则需要你自己实现GB2312和GBK的Escaper类,重写writeAttribute(String, Writer)、writeCData(String, Writer)和writeContent(String, Writer)方法,由于GB2312和GBK的编码规则是相同的,只是字符集大小不一样,所以你可以让同一个Escaper来处理这两种编码。 总结: 在整个jibx开发过程中最重要的有一下两点: 一、binding.xml编写 java类和XML文件的关系文件(bingding.xml)的编写,关于这一点jibx的指南里面举出了22个关系映射例子,有需要的朋友可以好好研究一下这22中关系映射。 二、执行binding编译 好多初学者(包括我)都会忽略这一点,其实这一步是整个jibx绑定过程中必不可少的一步,非常关键,虽然这只要开发者敲几行命令或者写一个ant文件,其余jibx都会自动完成。上面二中提到的是用命令完成绑定的过程,下面附一份ant build.xml文件: 这文件内容需要稍加修改(主要是路径和类名)才能用到上面的例子中。 <?xml version="1.0"?> <!-- This build file can be used within an IDE environment to bind the JiBX binding definition model classes - after building or modifying the model classes, run the default "bind" target of this script to compile the internal bindings. --> <project basedir="." default="bind"> <!-- JiBX binding compiler task definition --> <property envir/> <property name="jibxhome" value="${basedir}"/> <taskdef name="bind" classname="org.jibx.binding.ant.CompileTask"> <classpath> <pathelement location="${jibxhome}/lib/bcel.jar"/> <pathelement location="${jibxhome}/lib/jibx-bind.jar"/> <pathelement location="${jibxhome}/lib/jibx-run.jar"/> <pathelement location="${jibxhome}/lib/jibx-extras.jar"/> <pathelement location="${jibxhome}/lib/xpp3.jar"/> </classpath> </taskdef> <!-- bind the class files --> <target name="bind"> <echo message="Running binding compiler..."/> <bind load="true"> <bindingfileset dir="${basedir}/src"> <include name="com/freeboy/jibx/test/binding0.xml"/> </bindingfileset> <classpathset dir="${jibxhome}/lib/jibx-run.jar"/> <classpathset dir="${jibxhome}/lib/jibx-extras.jar"/> <classpathset dir="${basedir}/classes"/> </bind> </target> </project> |