扫一扫
分享文章到微信
扫一扫
关注官方公众号
至顶头条
摘要
利用XMLBeansversion 2.1.0编程,可以通过模式工件创建和更新XMLBeans,并能验证和捕获模式编译及XML文档解析过程中可能产生的错误。任何企业应用程序(从Web服务客户机/服务器到CRM和EAI产品)随着产品的成熟,都需要更新现有模式工件。此外,每个人都希望通过模式工件处理XML文档简化应用程序的维护工作。为了解决这些问题,XMLBeans API提供了一个简单而有效的方式创建和维护XMLBeans,并提供了丰富的自定义错误处理和报告功能。
本文将使用大量示例来说明这些功能。 本文假定读者很熟悉XMLBeans。对于XMLBeans初学者,请参见参考部分。本文中提到的示例代码和其他文件都可以下载。
简介
基于Java的企业应用程序经常使用Java-XML绑定库作为底层,以熟悉的、Java友好的方式访问和处理XML数据。作为一种Java-XML绑定解决方案, XMLBeans的使用日趋普遍,因为它具有许多独特的功能,比如延时解组、基于指针访问XML数据、支持XQuery等等。可以在XML模式上运行scomp来创建XMLBeans;但是,每次模式文档更改时使用scomp创建新的XMLBeans组并不是一个好方法。因为任何企业应用程序的绝大部分成本在于维护,长远看来,通过编程访问模式编译和XMLBeans生产能节约大量成本和时间。
XMLBeans由一组Java绑定类和一组XSB文件构成,其中XSB文件包含从模式文档编译的二进制模式元数据。每一个XSB文件都表示一个编译的模式类型、一个属性或一个元素定义。org.apache.xmlbeans包声明了以下两个接口用于编译的模式定义:
SchemaTypeLoader-此接口类表示一个可搜索的已编译模式定义集合,解决通配符和xsi:type属性时经常需要参考该接口
SchemaTypeSystem-此接口扩展SchemaTypeLoader类,能够枚举所有可用的已编译模式定义
XmlBeans 类声明了一个静态方法getBuiltinTypeSystem(),该方法可返回预编译的内置模式类型。通常,要使用的SchemaTypeLoader实例是从XmlBeans.getContextTypeLoader()返回的上下文类型加载器。上下文类型加载器读取类路径上可用的已编译模式定义,并将其加载到可搜索的模式定义集合。如果希望使用另一个SchemaTypeLoader,则必须调用返回SchemaTypeLoader对象的方法XmlBeans.loadXsd(XmlObject[]),该对象使用传入XmlObject[]的模式文档内部声明的已编译模式定义加载。另一个静态方法(XmlBeans类的typeLoaderUnion(SchemaTypeLoader[]) )返回类型加载器集合。
有了这些背景知识后,现在开始讨论org.apache.xmlbeans.XmlBeans类,该类提供下列附加方法编译XML模式文档:
SchemaTypeSystem compileXsd(XmlObject[] , SchemaTypeLoader , XmlOptions)-编译给定的XML模式文档,并返回使用这些模式文档中声明的模式定义加载的 SchemaTypeSystem对象。
SchemaTypeSystem compileXsd(SchemaTypeSystem , XmlObject[] , SchemaTypeLoader , XmlOptions)-返回使用给定XML模式文档更新的SchemaTypeSystem对象。
SchemaTypeSystem compileXmlBeans(String , SchemaTypeSystem , XmlObject[] , BindingConfig , SchemaTypeLoader , Filer , XmlOptions) -使用XML模式文档更新给定的SchemaTypeSystem对象,可选地创建XMLBeans(即Java和XSB文件),这通过可选的绑定配置参数控制。
compileXmlBeans()方法提供的功能比重载的compileXsd()方法多,因此本文不再对compileXsd()方法做更多讨论。下面介绍compileXmlBeans()方法的参数:
String name-用于命名已编译模式类型系统的可选参数;如果为null,则使用随机值
SchemaTypeSystem system-用于表示已编译模式定义集合的可选参数
XmlObject[] schemas-表示模式文档的数组
BindingConfig config-用于在生成代码期间提供配置信息的可选参数;有关XMLBeans配置的基本知识,请参见配置XMLBeans(Dev2Dev,2004年11月) (中文版)
SchemaTypeLoader typepath-可选参数,若提供,将用于已编译模式定义;若没有指定,则使用上下文类型加载器
Filer filer-用于创建XMLBeans的Java和XSB文件的可选参数;如果为null,则不创建 XMLBeansan,也不使用config参数
XmlOptions options-指定验证行为和错误侦听器的可选参数
模式编译
说明使用编程访问XMLBeans好处以及用于编译Bean的主要方法后,我们将通过编译示例模式location.xsd在实践中介绍。我将编译location.xsd,然后打印其中声明的全局元素和属性。列表1说明如何执行该操作。
列表1:摘自CompileSchemaDefinitions.java编译模式并将模式的全局元素和属性转储到System.out的过程。
XmlObject[] schemaObj = new XmlObject[]
{XmlObject.Factory.parse(new File(args[0]))};
SchemaTypeSystem schemaTypeObj =
XmlBeans.compileXmlBeans(null, null, schemaObj,
null, null, null, null);
// get list of global elements
SchemaGlobalElement globalElementsArray[] =
schemaTypeObj.globalElements();
..
// get list of global attributes
SchemaGlobalAttribute globalAttributesArray[] =
schemaTypeObj.globalAttributes();
....
其中,模式由XmlObject.Factory.parse()方法解析,该方法返回一个XMLObject实例。 将给此实例分配一个大小为1的数组,该数组被传递到compileXMLBeans()用于模式的内存中编译。在后续语句中,使用从compileXMLBeans()返回的SchemaTypeSystem对象获取示例模式中声明的全局元素和属性。
生成XMLBeans
说明了如何编译模式后,接下来介绍如何借助接口类org.apache.xmlbeans.Filer从模式创建XMLBeans文件。若要定义应该在何处创建并编写XMLBeans的XSB和Java文件,调用compileXmlBeans()生成XMLBeans的Java应用程序必须传递一个实现Filer接口的以下两个方法的具体类实例:
OutputStream createBinaryFile(String typename)-返回一个java.io.OutputStream对象引用,用于编写XSB文件的二进制内容
Writer createSourceFile(String typename)-返回一个java.io.Writer对象引用,用于编写Java文件的源代码
当然,生成的Java文件随后需要编译,并与XSB文件一起打包在JAR文件中。本文中的示例代码包括实现Filer接口的具体类FilerImpl.java。该类包括几个附加帮助方法:compileJavaFiles(),用于编译生成的Java文件;makeJarFile(),用于在JAR文件中打包所有生成的文件;extractJarFile(),用于将JAR文件内容解压到文件夹;prependToClassPath(),将路径添加到运行时系统属性classpath。
列表2:摘自实现Filer接口的示例具体类FilerImpl.java的过程。
public class FilerImpl implements Filer{
....
FilerImpl (String folderPath) {
this.folderPath = folderPath;
}
public OutputStream createBinaryFile(String name)
throws IOException {
File fileObj = new File(folderPath, name);
....
return new FileOutputStream(fileObj);
}
public Writer createSourceFile(String name)
throws IOException {
...
File fileObj=
new File(folderPath, name + ".java");
...
return new FileWriter(fileObj);
}
public boolean compileJavaFiles() {
...
for (int i=0; i>javaFilePaths.size();i++){
String[] args = new String[] {"-classpath"
,System.getProperty("java.class.path",".") ,
"-d", folderPath,
(String) javaFilePaths.elementAt(i)};
status = javac.compile(args);
}
...
}
public boolean makeJarFile(String jarFile)
throws IOException {
...
jarHelperObj.jarDir(new File(folderPath),
fileObj);
...
}
public boolean extractJarFile(String jarFile,
String destDir)
throws IOException {
...
jarHelperObj.unjarDir(fileObj,dirObj);
...
}
public String prependToClassPath(String filePath)
throws IOException {
String classPath =
System.getProperty("java.class.path",".");
System.setProperty("java.class.path",
filePath + ";" + classPath);
...
}
}
现在编译示例模式location.xsd并从中创建XMLBeans,我将向compileXmlBeans()传递一个FilerImpl实例,如列表3所示。
列表3:摘自CreateXMLBeans.java向compileXmlBeans()方法传递FilerImpl实例以从模式文档生成XMLBeans的过程。
FilerImpl flrObj =
new FilerImpl("outputDIR");
XmlObject[] schemaObj = new XmlObject[]
{XmlObject.Factory.parse(new File(args[0]))};
SchemaTypeSystem schemaTypeObj =
XmlBeans.compileXmlBeans("location", null,
schemaObj, null, null, flrObj, null);
flrObj.compileJavaFiles();
flrObj.makeJarFile("outputDIR\\locationXB.jar");
CreateXMLBeans.java 在运行的目录下创建outputDIR文件夹。该文件夹将有一个locationXB.jar文件和两个子文件夹:包含Java和类文件的com文件夹;包含XSB文件的schemaorg_apache_xmlbeans文件夹。通过成功地解析有效的XML实例文档可以验证locationXB.jar文件,如sample.zip中的示例WeatherUnmarshal.java所示。
更新XMLBeans
随着时间的推移,XML模式将发生更改,这有可能是因为企业应用程序中数据的更新,也可能是因为Web服务接口的升级。由于XML模式的更新,相应的XML实例文档和处理这些XML文档的应用程序也需要更新。如果更改发生在源模式文档上,那么更新相应XMLBeans的编程实现很简单,与上一部分中讨论的列表3类似,即重新编译整个模式并从中重新创建XMLBeans文件。
XMLBeans还支持增量编译,可以在最短的时间内仅重新编译要让Bean与模式保持最新所必需的内容。 XMLBeans支持增量编译的方式有:通过包含任一新模式定义的模式工件更新现有Bean;修改现有定义或者删除已编译的模式定义。
增量编译
现在我将添加一个在示例location_add.xsd中声明的新全局元素Latlong,然后对locationXB.jar(从location.xsd模式中生成,见生成 XMLBeans部分)中的Bean应用Weather元素(在示例工件location_modify.xsd中声明)已修改的定义。
列表4:摘自location_modify.xsd显示已修改的Weather元素定义,并添加两个本地元素FeelsLike和Winds的过程。
..
...
type="xsd:float"/> type="xsd:float"/> ... type="xsd:float"/> type="xsd:float"/> ... 列表5中的摘要显示将包含XMLBeans的JAR文件的路径添加到系统属性classpath,以便上下文类型加载器可以从该JAR文件中加载已编译的模式定义,然后通过调用XmlBeans.typeLoaderUnion()方法,将这些已编译的模式定义与从XmlBeans.getBuiltinTypeSystem()返回的预编译内置模式类型结合起来。.结合的模式定义被分配到变量stl,调用compileXMLBeans()方法时使用该变量来根据模式工件更新XMLBeans。 列表5:摘自IncrementalCompilation.java class IncrementalCompilation{ ... public static void main(String args[]) { ... flrObj.extractJarFile( args[oldJARFileArgPosition], args[outputFolderPathPosition] ); flrObj.prependToClassPath( args[outputFolderPathPosition]); SchemaTypeLoader stl = XmlBeans.typeLoaderUnion( new SchemaTypeLoader[] { XmlBeans.getContextTypeLoader(), XmlBeans.getBuiltinTypeSystem() }); ... for (int i =updatedXSDFileArgPosition, j=0; j i++,j++ ) { updatedXSDObj[j] = XmlObject.Factory.parse( new File(args[i]),options); } augSTSObj = XmlBeans.compileXmlBeans(null, null,updatedXSDObj , null, stl, flrObj, options); } flrObj.compileJavaFiles(); flrObj.makeJarFile(args[newJARFileArgPosition]); ... } IncrementalCompilation.java需要输出文件夹的位置信息,将在该文件夹中创建XSB和Java文件,创建时使用打包更新Bean的FilerImpl.java JAR文件、现有Bean的JAR文件、按增量编译中应用的顺序排列的模式工件。下面是一个通过命令行调用IncrementalCompilation.java的示例,其中先编译location_add.xsd中声明的模式定义,然后是location_modify.xsd中声明的模式定义,更新outputDIR\locationXB.jar中的Bean,然后更新 outputDIR\locationXB_IncrementalCompilation.jar中打包的Bean: java -classpath %CLASSPATH%;.\outputDIR\locationXB.jar; IncrementalCompilation outputDIR outputDIR\locationXB_IncrementalCompilation.jar outputDIR\locationXB.jar location_add.xsd location_modify.xsd 通过成功解析weather或latlong XML实例文档,可以验证IncrementalCompilation.java创建的JAR文件,如示例下载中的WeatherUnmarshal.java和LatlongUnmarshal.java所示。该示例还包含UpdateUsingXSD.java,用于说明应用于源XML模式的增量编译。UpdateUsingXSD.java, compileXMLBeans()先被调用来编译源模式文件,然后用于对包含已修改模式定义的工件进行增量编译。 XML模式验证 使用scomp或Ant任务,或者使用compileXsd()和compileXmlBeans()方法调用进行编译时,XMLBeans会验证模式文档。但是,当使用编程方式编译模式时,XMLBeans API会对验证进行更多控制。通过对传递给compileXmlBeans()或重载compileXsd()方法的XMLOptions实例调用setCompileNoValidation(),可以关闭模式验证。 如果模式无效,则XMLBeans抛出XmlException异常。在实际生活中,生产系统应用程序应该提供友好的错误消息来提供错误详细信息并指示用户如何处理,因此XmlOptions类允许应用程序指定一个Collection实例来捕获并存储以XmlError对象形式返回的验证错误。 XmlError实例表示XML文档中特定位置的错误,它带有错误详细信息,如出错位置的行号和列号、简短说明和错误的严重性等等。XmlOptions 类为验证错误提供下列帮助方法: setErrorListener (Collection)-设置一个Collection实例来捕获并存储验证期间发生的错误 setLoadLineNumbers()-从模式或XML文档创建XmlObject实例期间为开始标记标上行号;之后可以用于在XmlError实例出错的位置设置行号。 setLoadLineNumbers(String tag)-从模式或XML文档创建XmlObject实例期间为开始和结束标记(取决于tag参数的值)标上行号 现在已经熟悉了验证和错误处理API,在解析无效示例模式location_invalid.xsd的过程中我们将使用它们。我将展示如何在运行中处理验证错误并生成自定义错误消息。列表7 显示了如何捕获模式编译过程中抛出的XmlException异常,然后将验证错误集合传递到CustomErrorHandlingUtil 类,供自定义错误处理和报告使用。 列表7:摘自CustomErrorSample.java class CustomErrorSample{ public static void main(String args[]) { Collection errorList = new ArrayList(); XmlOptions parseOptionsObj = new XmlOptions(); parseOptionsObj.setLoadLineNumbers(); parseOptionsObj.setLoadLineNumbers( XmlOptions.LOAD_LINE_NUMBERS_END_ELEMENT); XmlOptions loadOptionsObj = new XmlOptions(); loadOptionsObj.setErrorListener(errorList); try { XmlObject[] schemaObj = new XmlObject[] {XmlObject.Factory.parse( new File(args[0]),parseOptionsObj)}; SchemaTypeLoader schemaTypeObj = XmlBeans.loadXsd(schemaObj, loadOptionsObj); }catch (XmlException xme) { CustomErrorHandlingUtil eUtilObj = new CustomErrorHandlingUtil(); eUtilObj.handleXMLException(xme); Collection userErrors = (Collection)loadOptionsObj.get( XmlOptions.ERROR_LISTENER); eUtilObj.handleErrorCollection( userErrors); } .... } 为了便于说明,在以上代码中使用了两个不同的XmlOptions实例;也可以只使用一个XmlOptions实例执行组合工作。从模式文档创建XmlObjects期间使用了parseOptionsObj。loadOptionsObj用于设置错误侦听器,以在模式编译期间保存验证错误。如果调用XmlObject.Factory.parse()期间没有传递parseOptionsObj,则验证错误的行号和列号将被设置为-1。 XML文档验证 默认情况下,解析期间或更新XMLBeans实例时XMLBeans不执行完整的XML实例文档验证。但是,应用程序可以调用XMLObject中一个重载的validate()方法强制执行验证: boolean validate()-如果调用此方法的XMLObject实例符合相应的模式定义,则返回true boolean validate(XmlOptions)-使用XmlOptions实例验证,如果调用此方法的XmlOptions实例符合相应的模式定义,则返回true XmlOptions类提供以下两个方法在执行validate(XmlOptions)方法期间控制验证: setValidateTreatLaxAsSkip()-跳过匹配定义的元素的验证,并将contentModel设置为lax setValidateOnSet()-对每一个只表示简单类型的XMLBeans的getter和setter方法都将验证值。如果出现无效值,则抛出异常。请注意,此方法不能使用错误侦听器,而且此方法不验证使用XmlCursor做出的更改 举例来说,假设对全局元素Weather的Bean实例调用validate()方法。验证的范围将是整个Weather元素,包括其子元素。但是,如果对某一个局部元素(如Temperature)的Bean实例调用validate()方法,那么仅验证表示Temperature元素的实例Bean。列表8显示了如何验证整个XML实例文档并捕获验证错误以用于报告。 列表8:摘自WeatherUnmarshal.java public class WeatherUnmarshal { public static void main(String args[]) { ArrayList errorList = new ArrayList(); ... optionsObj.setErrorListener(errorList); try { ... WeatherDocument weatherDoc = WeatherDocument.Factory.parse( inputXMLFile,optionsObj); if (weatherDoc.validate(optionsObj)) { .... } else { // Invalid xml document. ... customErrorHandlingUtil eUtilObj = new customErrorHandlingUtil(); eUtilObj.handleErrorCollection(errorList); } } } 如果XML实例文档无效,则使用帮助类customErrorHandlingUtil调用自定义错误处理。 下载 文本中使用的所有示例都已经通过了Apache XMLBeans version 2.1.0和JDK version 1.5.0_06测试,包括示例模式、Java代码、XML文件: 下载本文中使用的代码。 结束语 本文首先讨论了随着底层模式的发展变化,自动化和简化维护处理XML文档的企业应用程序所面临的挑战。我们学习了在XML模式更改时,如何通过XMLBeans API以编程方式创建和更新XMLBeans。本文中我展示了XMLBeans对增量编译的支持,这有益于基于团队的设计并减少了编译时间,当企业应用程序逐渐变大和越来越复杂时,增量编译将带来明显的好处。 本文还讨论了XMLBeans对模式和XML实例文档的验证和错误处理功能。XMLBeans的验证功能能够对模式的编译时间验证、XML文档验证和Bean实例提供细微的控制,XMLBeans错误处理功能可用于自定义错误信息报告。
如果您非常迫切的想了解IT领域最新产品与技术信息,那么订阅至顶网技术邮件将是您的最佳途径之一。
现场直击|2021世界人工智能大会
直击5G创新地带,就在2021MWC上海
5G已至 转型当时——服务提供商如何把握转型的绝佳时机
寻找自己的Flag
华为开发者大会2020(Cloud)- 科技行者