从XML到Java代码的数据绑定之三 从文本到字节码
副标题#e#
本数据绑定系列的第三部门演示了如何利用“JSR-031:数据绑定,Sun 数据绑定类型申请”中指定的要领,将 XML 元素和属性转换成 Java 工具。这部门主要报告从数据的 XML 暗示移到应用措施代码易于利用的 Java 实例。第三部门论及通过将 XML 文档中的嵌套元素打消编构成 Java 工具、测试和用某些实际示例来利用新的东西。
本系列的方针是演示如何将 XML 元素转换成 Java 工具,然后可以利用 Java 语言 accessor 和 mutator 要领直接处理惩罚 XML 数据。 第一部门较量了数据绑定和 Java 应用措施中其它处理惩罚 XML 数据的要领,阐明白设计决定,还界说了示例 Web 处事设置文档的 XML 模式。 第二部门说明白如何从 XML 模式生成接口和实现,以便切合 XML 模式的 XML 文档可以转换成这些生成类的实例。
在第三部门(共四部门)中,将完成基本常识的讲授,而且描写了如何经心设计代码以执行打消编组,打消编组将完成将 XML 转换成 Java 工具的进程。执行了打消编组后,可以利用测试类(已包罗在内)来查抄是否所有部门都已正确组合在一起。本系列的每一部门都成立在其它部门的基本之上,所以假如您还没有看过第一和第二部门,您也许会看不懂本文中的一些描写。假如要回首专门的词汇表,请参阅 术语表明侧栏。
利用第一部门中为 WebServiceConfiguration 界说的 XML 模式(请参阅 更新版本 )和第二部门中的接口,即将建设为设置数据的特定实例提供数据的 XML 文档。任何切合模式的 XML 文档都可以编构成 Java 工具。这些工具应该是利用 SchemaMapper 类生成的类的实例。虽然,最终功效就是数据绑定。
建造 XML 实例文档
建设切合模式的 XML 文档 — 凡是叫做 XML 实例– 很简朴。文档必需只提供与模式中界说的约束相匹配的数据值,如清单 1 所示。
清单 1. 切合示例 XML 模式的 XML 实例文档
<?xml version="1.0"?>
<webServiceConfiguration xmlns="http://www.enhydra.org"
牋牋牋牋牋牋牋牋牋牋牋牋 xmlns:xsi="http://www.w3.org/1999/XMLSchema/instance"
牋牋牋牋牋牋牋牋牋牋牋牋 xsi:schemaLocation="http://www.enhydra.org
牋牋牋牋牋牋牋牋牋牋牋牋牋牋牋牋牋牋牋牋牋牋 configuration.xsd"
牋牋牋牋牋牋牋牋牋牋牋牋 version="1.1"
牋牋牋牋牋牋牋牋牋牋牋牋 name="Unsecured Web Listener"
>
_<port number="80"
牋牋牋_protocol="http"
牋牋牋_protectedPort="false"
_/>
_<document root="/usr/local/enhydra/html"
牋牋牋牋牋_index="*.html,*.xml"
牋牋牋牋牋_error="error.html"
_/>
</webServiceConfiguration>
清单 1 中的示例完整地显示了 WebServiceConfiguration 的实例。实例文档包罗了两个名称空间声明。第一个是缺省名称空间声明,请参考 http://www.enhydra.org。这暗示所有没有前缀的元素会分派到此名称空间。固然,在本示例中不需要声明缺省名称空间,它还给以了文档一些身份。这个缺省名称空间有助于将该文档与其它有相似或等同元素名称的 XML 文档区分出来。
界说的另一个名称空间分派给 xsi 前缀,所以带该前缀的所有元素都分派到此名称空间。它 (http://www.w3.org/1999/XMLSchema/instance) 引用“XML 模式实例类型”的 URI。该类型依次界说了 XML 文档如何引用文档切合的 XML 模式。最后, schemaLocation 属性引用 XML 模式。该属性的第一个变量是受到约束的名称空间(示例缺省名称空间,它包罗文档中的每个元素)。第二个变量,用空格与第一个变量分隔,引用 XML 模式的实际位置。本例中,模式 configuration.xsd 是一个当地文件,它与文档在同一个目次中。也可以通过利用 URL 来引用网络上任意位置的模式。
在缺省名称空间中,附加属性(因为它们没有前缀)界说了版本 (1.1) 和名称 (Unsecured Web Listener)。
接着,声明白模式中的 Port 工具,并界说了它的数据:端标语为 80,协议是 http。正确打消编构成 Java 代码后,该文档就酿成了 WebServiceConfigurationImpl类的实例。然后,Java 代码可以利用本系列第二部门中设计的接口 WebServiceConfiguration,以利用根基 XML 文档中的数据。(请留意,大概会在应用措施中执行验证,如 模式验证侧栏中所概述的。)
#p#副标题#e#
模式验证
较新的 XML 语法阐明器,如 Apache Xerces 语法阐明器的当前刊行版,答允对 XML 实例文档执行模式验证。验证答允在措施名目上确保 XML 文档切合它引用的 XML 模式。请与语法阐明器供给商接洽或参考文档,以确定语法阐明器是否支持模式验证,其验证范畴,以及如何打开验证。
打开前门
#p#分页标题#e#
正式开始之前,需要提供进口点以打消编组 XML 文档,该文档作为返回 Java 工具的要领的输入。(由于您会忆起,本例中打消编组的功效只是 Java 工具。)然后,该工具可以转换成适当的接口,其实,您已经生成了该接口(在本系列第二部门中)。
对付示例 SchemaMapper 类,答允传入 URL 是最有意义的。由于可以利用网络资源作为输入,而不是只答允文件名,这就提供了更多选择。知道了这一点后,下一步就从 URL 建设 JDOM 文档工具 ( org.jdom.Document ),然后处理惩罚文档。请查察清单 2 中执行该操纵的代码。
清单 2. 将字符串映射成 Java 指定的范例
/**
*
* This method is the public entry point for unmarshalling an object from
* an XML instance document. *
* * @param instanceURL URL for the instance document.
* @return Object - the created Java Object, or * null if problems occur in a way
that does not * generate an Exception. * @throws IOException when errors in binding occur.
*/ public static Object unmarshall(URL
instanceURL) throws IOException { // Read in the document SAXBuilder builder = new SAXBuilder();
try { Document doc =
builder.build(instanceURL); Element rootElement = doc.getRootElement();
Unmarshaller unmarshaller = new Unmarshaller(); return
unmarshaller.getJavaRepresentation(rootElement); } catch (JDOMException e)
{ throw new IOException (e.getMessage()); } }
清单 2 中的要领是静态的,答允直接挪用它而无需实例化类的实例。由于对 unmarshall 要领的多个挪用之间没有需要共享的数据,因此该要领可以是静态的。一旦处理惩罚了 XML,就将文档的根元素(以 JDOM 暗示)就被传到执行从 XML 到 Java 工具转换的内部要领。
转换数据
我不规划逐行表明打消编组中利用的完整代码。可以查察 类的完整源码清单 ,它根基上是不需加以说明的。可是,在进口点示例中,有一些值得强调的工作。假如建设了适当类的新实例,将利用 XML 文档提供的值挪用 mutator 要领(全都名为 setXXX )。虽然,这将使 XML 数据在实例的 Java 要领中到处都可用。清单 3 显示了处理惩罚这种查找要领以及随后挪用的代码片断。
清单 3. unmarshaller 类的进口点
// For each attribute, get its name and call mutator
List attributes = rootElement.getAttributes();
Method[] methods = objectClass.getMethods();
for (Iterator i = attributes.iterator(); i.hasNext(); ) {
Attribute att = (Attribute)i.next();
// Only want attributes for this namespace
if ((!att.getNamespace().equals(ns)) &&
(!att.getNamespace().equals(Namespace.NO_NAMESPACE))) {
continue;
}
// Determine method to call
String methodName = new StringBuffer()
.append("set")
.append(BindingUtils.initialCaps(att.getName()))
.toString();
// Find the method to call, and its parameter type
for (int j=0; j
找到了根元素的属性,并确定了每个属性的合用要领。然后,就是处理惩罚实际的 java.lang.reflect.Method 工具。XML 属性的值已确定,并作为挪用的参数传送到要领。可是,需要办理一个映射问题;XML 文档中的所有数据都作为 String 抽取,但通报时必需是适当的 Java 范例。清单 4 将一个要领添加到 DataMapping 帮助类中,以满意转换的需要。
清单 4 将字符串映射成 Java 特定的范例
/**
*
* This will take the Stringvalue supplied and convert it
* to an Object of the type specified in paramType. *
* * @param value String value to convert. * @param paramType Class with type to convert to.
* @return Object - value in correct type.
*/ public static Object getParameter(String value, Class paramType)
{ Object ob = null; String type = paramType.getName(); if
(type.equals("java.lang.String ")) { ob = value; } else if ((type.equals("int")) ||
(type.equals("java.lang.Integer")))
{ ob = Integer.valueOf(value); } else if ((type.equals("long")) ||
(type.equals("java.lang.Long"))) { ob = Long.valueOf(value); }
else if ((type.equals("float")) || (type.equals"java.lang.Float")))
{ ob = Float.valueOf(value); } else if ((type.equals("double"))
|| (type.equals("java.lang.Double"))) { ob = Double.valueOf(value); }
else if ((type.equals("boolean")) ||
(type.equals("java.lang.Boolean"))) { ob = Boolean.valueOf(value); }
return ob; }
#p#分页标题#e#
在清单 4 中,值作为 String 传入,而且还传入了要转换的类和处理惩罚范例转换的要领。虽然,这里包括的数据范例不多。可以添加更多范例(如 java.util.Date )来支持更巨大的数据映射。
一旦数据转换成适当的范例,可以利用反射挪用 accessor 要领,并可传入已转换的数据范例。这就使 XML 文档中的所有属性及其值可以在作为功效的 Java 实例中以要领变量和的值存储。
递归工具树
所剩下的将是生成嵌套工具(如 WebServiceConfiguration 工具中的 PortType 工具)。最后,将嵌套工具通报给 accessor 要领,然后将填充了成员变量值和工具引用的顶级工具返回给挪用措施。这种方法生成了一棵工具树,个中主工具是该树的骨干。每个嵌套工具都形成了自己必需填充的树的分枝。这些分枝可以有它们本身的分枝,带有自己必需填充的的嵌套工具。由此可知,这棵树大概变得很是巨大。
在假如同一操纵必需产生不知几多次的环境下,递归险些老是完成操纵的最佳选择。假如是 unmarshaller 类,则需要在将 XML 绑定到 Java 的完整进程上递归。一旦读取了所有属性并将它们分派给已建设的 Java 实例,就需要取出每个嵌套元素,然后再次执行打消编组。
通过迭代所提供根的子元素,来完成 XML 文档的处理惩罚,如清单 5 所示。这些子元素中的每一个都将成为另一个工具,这就暗示必需以该元素作为根元素从头开始打消编组进程。
清单 5. 用递归处理惩罚嵌套元素
// Now do complex objects
List elements = rootElement.getChildren();
for (Iterator i = elements.iterator(); i.hasNext(); ) {
Element element = (Element)i.next();
// Only want elements for this namespace
if ((!element.getNamespace().equals(ns)) &&
(!element.getNamespace().equals(Namespace.NO_NAMESPACE))) {
continue;
}
// Determine method to call
String methodName = new
StringBuffer()
.append("set")
.append(BindingUtils.initialCaps(element.getName()))
.toString();
// Find the method to call, and its parameter type
for (int j=0; j<methods.length; j++) {
if (methods[j].getName().equals(methodName)) {
// Since all mutators have one param, get the first one
Class[] paramTypes =
methods[j].getParameterTypes();
Class paramType = paramTypes[0];
// Convert the type we have to the correct type
Object param = getJavaRepresentation(element);
// Invoke the method
methods[j].invoke(obj, new Object[] { param });
}
}
}
注:您也许留意到我在清单 5 中的打消编组中做了一个假设,即成员变量老是由 XML 属性暗示,嵌套工具由 XML 元素暗示。那么,这些元素大概有本身的属性和嵌套元素。我的假设是独一配置的限制,而且是公道的。这意味着打消编组进程不必查察引用的 XML 模式并确定特性是由元素暗示,照旧由属性暗示。这也会使编组进程变得更简朴,将在本系列的下一篇文章中呈现。假如所有这一切对您没有难度,那么只利用属性作为变量,元素作为工具,不必思量嵌套和递归。
清单 5 中的代码看来很像上一段代码,其主要区别是用赤色强调的几行。这段代码通过打消编组嵌套元素来获取参数,而不是利用 DataMapping 帮助类将文本值转换成 Java 数据范例。然后,返回的工具提供应适当的 mutator 要领(譬喻, setPort ),迭代继承举办。一旦递归从底层到顶层解开,则建设的 Java 工具将返回到挪用应用措施。很快吗!数据绑定完成了。
#p#分页标题#e#
通过利用运行的 unmarshaller 类,实际上,它最终利用了数据绑定东西。通过利用 XML 模式、XML 文档和一些简朴的 Java 代码,会见 XML 就象会见 JavaBean 一样简朴。
生成类
首先,确保已经从 XML 模式生成了 Java 类,如清单 6 所示。
清单 6. 从示例模式生成 Java 类
/projects/dev/binding> export CLASSPATH=/projects/dev/jdom/lib/xerces.jar
/projects/dev/binding> export CLASSPATH=$CLASSPATH:/projects/dev/jdom/build/jdom.jar
/projects/dev/binding> export CLASSPATH=$CLASSPATH:/projects/dev/binding
/projects/dev/binding> java org.enhydra.xml.binding.SchemaMapper xml/configuration.xsd
/projects/dev/binding> javac -d . *.java
利用 unmarshaller
假如已经从 XML 模式生成了类,并颠末编译,则可以继承。作为确保类是否事情的简朴测试,可以利用清单 7 中的类测试数据绑定的成果性(或 下载这个类)。
清单 7. 数据绑定测试类
import java.io.File;
import org.enhydra.xml.binding.unmarshaller;
public class TestMapper {
public static void main(String[] args) {
System.out.println("Starting unmarshalling...");
try {
File file = new File("xml/example.xml");
Object o = unmarshaller. unmarshall(file.toURL());
System.out.println("Object class: " + o.getClass().getName());
System.out.println("Casting to WebServiceConfiguration...");
WebServiceConfiguration config = ( WebServiceConfiguration)o;
System.out.println("Successful cast.");
System.out.println("Name: " + config.getName());
System.out.println("Version: " + config.getVersion());
System.out.println("Port Number: " + config.getPort().getNumber());
System.out.println("Port Protocol: " + config.getPort().getProtocol());
} catch (Exception e) {
e.printStackTrace();
}
}
}
编译和运行该数据绑定测试类以查察功效,如清单 8 所示。
清单 8. 测试 unmarshaller
/projects/dev/binding> javac -d . TestMapper.java
/projects/dev/binding> java TestMapper
Starting unmarshalling...
Object class: WebServiceConfiguration Impl
Casting to WebServiceConfiguration ...
Successful cast.
Name: Unsecured Web Listener
Version: 1.1
Port Number: 80
Port Protocol: http
启动 Web 处事
作为一个更实用的示例,让我们回首已经在几篇文章中提到的 Web 处事示例。假设有一些可以编程启动的 Java 类(叫做 WebService ),那么可简朴地利用数据绑定来获取该类的设置信息。此刻,从一个 XML 文档(可能甚至几个)中读取和启动新的 Web 侦听措施长短常容易的事 — 这不需要任何 XML 特定 API 的常识,如清单 9 所示。将设置数据打消编构成 Java 工具,然后利用尺度 Java accessor 要领(凡是是 getXXX() 名目)来设置新的 Web 处事。
清单 9. XML 到 Web 侦听措施
// Assume we have a List of URLs
for (Iterator i = urls.iterator(); i.hasNext(); ) {
WebServiceConfiguration config = unmarshaller.unmarshal((URL)i.next());
WebService newService = new WebService();
newService.setName(config.getName());
// Set up port information
newService.setPortNumber(config.getPort().getNumber());
newService.setProtocol(config.getPort().getProtocol());
// Set up document root
newService.setDocRoot(config.getDocument().getRoot());
newService.setErrorPage(config.getDocument().getError());
newService.start();
}
就那么简朴,纵然是低级开拓者也能写出利用这个简朴 XML 文档及其数据的 Java 措施,而他甚至还不知道正在利用 XML!有关 XML 数据绑定代码的更多用法,请存眷 Enhydra 应用处事器即将推出的新版本,在将来的刊行版中将包括这里接头的数据绑定类(并将在下一篇文章中继承接头)。完成了 unmarshaller 的代码之后,就可以接头最终细节了。
跟上不绝成长的 API
就在一个月之前,我们看到 SchemaMapper 类,它从 XML 模式生成 Java 接口和实现。该代码很洪流平地利用了 JDOM API(主要是因为它很利便,是我编写的!)。然而 30 天时间只够举办一届曲棍球季后赛,对付 API,如仍在开拓中的 JDOM,却险些是一生一世。自上一篇文章以来,有几个变动已经在 JDOM API 中生效了,大大都反应了一些更新的要领名。有关变动及其原因的具体信息,请会见 JDOM 网站(请参阅 参考资料),可以在该网站上插手 JDOM-乐趣邮件列表。可是,为了辅佐您利用最新和最好的版本, SchemaMapper 类再次呈此刻因特网上,而且已更新成利用最新版本的 JDOM(直接来自 CVS)。还可以 下载源码。强烈发起从 CVS 获取最新的 JDOM,并利用更新版本的代码。(在第四部门到来之前,大概仍有更多变动。)
#p#分页标题#e#
JSR-031,数据绑定 API,在 Java 社区中仍是处在争论和测试进程的发起书。在这个进程中,它还大概做一些变动。尽量它还未成熟,至今为止很多利用 XML 的 Java 开拓者照旧会利用它,因为它是执行很是有用成果的要领。
竣事语
通过利用本系列这部门中新的具体信息,可以利用数据绑定代码。利用 unmarshaller 类,就可以在 Java 代码中利便地利用 XML 文档,而不必直接借助于 XML API,如 DOM、SAX 或 JDOM。固然示例发起利用数据绑定处理惩罚设置文件,您也许已经有了在应用措施中利用数据绑定的其它想法。也可以利用数据绑定代码来进动作静通报、数据存储和显示等等。
本系列的第四篇,也就是最后一篇文章将主要报告编组,即操作 Marshaller 类获得 Java 类,并将它转换成 XML 文档。该文章将接头转换本来颠末打消编组的 Java 工具,以及未颠末打消编组的 Java 工具。到当时,但愿您喜欢迄今为止呈现的数据绑定代码,下次再见。
术语表明
数据绑定。一种利用 JSP-031 会见 Java 中 XML 数据的新要领。JSP-031 是一个仍在开拓中的 API。
显式范例。具有范例属性的 complexType 元素。模式中的显式范例成为生成的 Java 代码中的接口名称。
隐式范例。 不具有范例属性的 complexType 元素。这种环境下,接口名称由 SchemaMapper 生成。
JSR-031。Sun 公司仍在开拓中的一种新的 Java 类型申请。它用于将 XML 文档编译成一个或多个 Java 类,而在 Java 应用措施中可以利便地利用这些 Java 类。
编组。 将 Java 工具转换为 XML 暗示,拥有当前值。
打消编组。 按照 XML 工具建设 Java 工具,凡是是按照编组生成一个 Java 工具。
本文所有源代码已经包括在文档开始处的源代码包中。
本文配套源码