Shane Curcuru (shane_curcuru@us.ibm.com), 顾问软件工程师, IBM Research
2003 年 12 月 01 日
越来越多的项目使用 XML Schema 来定义它们的数据结构。随着您的 XML Schema 储存库越来越大,您需要一些工具来管理您的这些 XML Schema 。Eclipse XSD Schema Infoset Model 提供了强大的查询和编辑功能。在本文中, Shane Curcuru 将为我们演示如何更新一个与 SOAP 一起使用的 Schema,采用的方式是自动地将这些属性使用转变为元素声明。<!--START RESERVED FOR FUTURE USE INCLUDE FILES--><!-- include java script once we verify teams wants to use this and it will work on dbcs and cyrillic characters --><!--END RESERVED FOR FUTURE USE INCLUDE FILES-->
阅读本文要求您对 XML Schema 和 SOAP 机制有一定的了解。在 zip 文件中包含了一些样本代码,可以独立运行,也可以运行在 Eclipse 工作台中。
引言:一个更新 XML Schema 的示例
如果您构建了一个 Schema 的库,您也许会在新建的应用程序中重用它们。如果您已经建立了内部购买订单的数据模型,如果您想把它转变为 Web 服务,那么您需要更新这个 Schema ,使它能为 SOAP 所用。SOAP 允许您跨网络传递 XML 消息;XML 主体必须遵循 Schema 。然而, SOAP 通常是使用元素数据作为它自己的 XML 主体,而不是属性数据。这里,您可以看一段程序,它自动更新现有的 Schema 文档,将任何属性声明转换成基本“对等”的元素声明。
![]() ![]() |
![]()
|
概述:如何进行
考虑到 XML Schema 的复杂性,您当然不乐意使用 Notepad 来编辑 .xsd 文件。一个良好的 XML 编辑器也无法一步到位,虽然它可以清楚的组织您的元素和属性,但是难以显示在 Schema 规范中定义的许多抽象 Infoset 关联。这也就是产生 Schema Infoset Model 的根源;它可以显示一个 Schema 文档集的具体 DOM 表示,也可以显示 Schema 的完全抽象 Infoset 模型。通过 Model 的编程 API以及 Model 内置的 Schema 样本编辑器,可以显示两者。
![]() |
|
在本例中,我将把这个问题稍微简化一下。 MakeSoapCompatible.java
是一个程序,试图从 Schema 中获取 attributeDeclarations
,然后转换成为基本对等的 elementDeclaration
s ,并显示一些提示。
很显然,如果在 complexType
的 particle 中已经存在同名的元素,一开始您无法将属性转变成为元素。因此,首先检查命名冲突,然后拒绝更改这些属性。为了简化这个实例,任意声明一些条件,使得 Schema 和程序不匹配。我并不更改那些带通配符的 Schema ,因为需要进行复杂的命名检查,才能确保更改的属性不会和元素发生冲突。也不更新那些使用了 #all
或者 #choice
作为排序的组,因为这将以无法预期的方式更改组的含义。
![]() ![]() |
![]()
|
评估 Schema 的不匹配
使用抽象 Infoset 关联的功能
首先,您需要查找 Schema 中的 complexType
s ,因为只有这些地方才会真正地使用属性。您不需要搜索所有的属性声明,因为,之后您可以询问 complexType ,知道它真正使用的属性。查询您的 Schema 文档是一个非常简单的过程:遍历这些内容,查找复杂类型。注意:有多种查询方式可以用来验证 Schema 内容,在这里只是用了其中一种而已。
清单 1. 查找 complexType
// Find type definitions: for our purposes, the simplest // way to get all complexTypes is to drop down to the // underlying EMF model of a schema to iterate through // all concrete components contained within this schema List complexTypeDefinitions = new ArrayList(); for (Iterator iter = schema.eAllContents(); iter.hasNext(); ) { XSDConcreteComponent concreteComponent = (XSDConcreteComponent)iter.next(); if (concreteComponent instanceof XSDComplexTypeDefinition) { complexTypeDefinitions.add(concreteComponent); } } // An alternate method would be to use the abstract Infoset // relationship of schema.getTypeDefinitions(), which would // get all globally-visible typedefs (simple and complex) // within the whole schema, however that would miss any // types that were nested inside of other components |
现在,您已经得到了所有需要更新的 complexType
s 列表,下面我们排除那些与样本程序不兼容的类型。因为您只是查询不同 Schema 组件的信息,您可以有效使用许多抽象 Infoset 关联,以及 Model 提供的方法。这些抽象方法自动考察这些基本类型和派生类型;对声明的引用;以及导入、包含和重定义 Schema 文档的结果。
清单 2. 查找不匹配的地方
// Detect name collisions between top-level elems and attrs List elementNames = getElementNames(complexType); List attributeNames = getAttributeNames(complexType); attributeNames.retainAll(elementNames); if (!attributeNames.isEmpty()) { // Report the name collision and return... } // Now check for any attribute wildcards, which we // can't really change into elements XSDWildcard attributeWildcard = complexType.getAttributeWildcard(); if (null != attributeWildcard) { // Report an incompatible wildcard and return... } // Check the content for other incompatible conditions like // groups with choice or all or a simpleType XSDComplexTypeContent complexTypeContent = complexType.getContent(); if (complexTypeContent instanceof XSDSimpleTypeDefinition) { // Report a simple type as incompatible and return... } else if (null != complexTypeContent) { XSDTerm particleTerm = ((XSDParticle)complexTypeContent).getTerm(); if (particleTerm instanceof XSDModelGroup) { XSDCompositor compositor = ((XSDModelGroup)particleTerm).getCompositor(); if ((XSDCompositor.ALL_LITERAL == compositor) || (XSDCompositor.CHOICE_LITERAL == compositor)) { // Report an incompatible group type and return... } } // more checks for wildcards, etc. } |
注意:这里没有显示所有检查不匹配的代码。可以下载所有的样本 zip 文件(参见 参考资料),从中可以看到所有的过程。 MakeSoapCompatible.java
程序已经精心的设计,添加了详尽的注释,可以很清楚的了解如何处理 Model 中的 Schema 。如果您想学习更多内容,这是您的下一个目标。
![]() ![]() |
![]()
|
创建元素声明
添加和处理组件的具体步骤
一旦您已经发现了那些您想更新的 complexType
s ,您需要做一些具体的事情。对于每一个 complexType
,您需要该类型的具体属性列表 getAttributeContents()
。对于每一个属性,您要先确保您已经指向了属性的真实声明,那怕是指向了其他地方的声明。然后,您需要创建和属性具有相同名称和类型的 elementDeclaration
,这是一个非常直接的过程。您也需要将 elementDeclaration 加入到 getContents()
的新 Particle 中, 然后您将这个 particle 添加到您的 complexType
。
清单 3. 将属性更改为元素
if (attrDecl.isAttributeDeclarationReference()) attrDecl = attrDecl.getResolvedAttributeDeclaration(); // Create a blank element and simply copy over the // pertinent data about the attribute XSDElementDeclaration elemDecl = XSDFactory.eINSTANCE.createXSDElementDeclaration(); elemDecl.setName(attrDecl.getName()); elemDecl.setTypeDefinition(attrType); // Note that since an annotation's elements are only modeled // in the concrete tree that we must explicitly ask to clone them if (null != attrDecl.getAnnotation()) { cloneAnnotation(attrDecl, elemDecl); } // Wrap this element in a particle XSDParticle particle = XSDFactory.eINSTANCE.createXSDParticle(); particle.setContent(elemDecl); |
有一个地方很清楚地显示了抽象模型和具体模型之间的区别。当您查看您的 schemaDocument.xsd
文件的时候,您也许会想知道一个 particle 是什么,因为您没有看到任何 xsd:particle
元素。您可以阅读 specification for a particle ,这里介绍得相当详细。一个 particle 本质上是元素声明、模型组或者任意(通配符)的容器; particle 定义了 Schema 中特定地方的 min/maxOccurs 约束。因为 Model 可以显示 Schema 的具体和抽象表示,所以很容易和各种表示一起工作。
注释是另外一种 Schema 组件,只在模型的具体表示中建模,所以它们需要稍微特殊的处理。在样本代码中,您需要将属性声明中的任何注释复制到您所创建的新元素声明中。您使用 DOM cloneNode()
方法来执行注释组件中内容的复制操作,然后,将这些注释添加到新创建的元素声明中。
清单 4. 复制具体注释
XSDAnnotation oldAnnotation = attrDecl.getAnnotation(); XSDAnnotation newAnnotation = XSDFactory.eINSTANCE.createXSDAnnotation(); try { Element oldAnnElem = oldAnnotation.getElement(); // Use the DOM method to do a deep clone of the element Element newAnnElem = (Element)oldAnnElem.cloneNode(true); newAnnotation.setElement(newAnnElem); elemDecl.setAnnotation(newAnnotation); } catch (Exception e) { // Report the error and return } |
![]() ![]() |
![]()
|
交换元素和属性
现在您已经创建了新的元素声明,用来替代您的属性。那么,您需要在您的 complexType 组件中交换这两者。因为在循环中您已经使用了 complexType.getAttributeContents()
的具体包容关系,您只需添加新的 elementDeclaration
,然后调用 attrContentsIter.remove()
来从您的类型中除去真正的属性。
清单 5. 使用具体列表来删除条目
// Use this concrete relationship, since we're going to // actually remove the attributes from this type for (ListIterator iter = complexType.getAttributeContents().listIterator(); iter.hasNext(); /* no-op */ ) { if (changeAttributeIntoElement(complexType, (XSDAttributeGroupContent)iter.next(), changedAttrs)) { // Note that list manipulation calls like remove() // will only work properly on concrete lists; // attempting to manipulate 'abstract' lists will // either throw an exception or will silently fail iter.remove(); } else { // Report the error and continue... } } |
![]() ![]() |
![]()
|
输出 Schema
在执行 MakeSoapCompatible
样本程序的时候,会把它的状态输出到 System.out 。如果您检查到在 Schema 中没有用到属性,将报告没有进行更改,然后退出。否则的话,将修改过的 Schema 文档输出到新的文件,然后报告其状态——或者是一系列属性已经成功的更改为元素,或者提示说一些属性存在着命名冲突,尚未更改。
假定您已经将部分属性声明更改为对等的元素声明,您希望保存这些 Schema,以待在 SOAP 应用程序中使用。 用于构架 Model 的 EMF 框架提供了资源处理服务,可以使用多种方式来装载和保存 Schema 文档。在本样本代码中显示了一种简单的方法,直接把它序列化为 URI ,在这里其实就是以原始输入文件命名的硬盘输出文件。
清单 6. 将 Schema 输出到文件
File outFile = new File(newLocation); FileOutputStream fos = new FileOutputStream(outFile); // Ensure that the abstract model is synchronized with the // concrete tree: this will ensure that the Model has // updated the concrete Element in the schema document // with any changes that may have been made in the // abstract model schema.updateElement(); // Simply ask the XSDResourceImpl to serialize the schema to // a document for us; this is just one way we can easily use // the XSD/EMF framework to manage resources for us XSDResourceImpl.serialize(fos, schema.getElement()); fos.close(); |
![]() ![]() |
![]()
|
结束语
如上所述,对 Schema 文档执行简单的编辑操作(将属性转换成为元素),需要一定的工作量。然而,Schema Infoset Model 可以显示 Schema 的抽象 Infoset 以及 Schema 文档的具体表示,这就使得这成为一个可管理的工作。 Model 也提供了简单的工具,用来装载和保存 Schema 文档为多种源文件。这样就提供了一个完整的管理 Schema 库的编程方案。
一些用户也许会问:“为什么比用 XSLT 或者其他 XML 处理程序来编辑 Schema 文档?”。实际上,虽然 XSLT 可以方便的处理 Schema 文档集的具体模型,它无法方便的看到它们所代表的整个 Schema 中的抽象关系。例如,您只想更新一个未知的新 UNK 枚举值中所包含的任意枚举 simpleType 。当然,您只想使用长度为三的字符串更新那些符合格式的枚举;您不想更新数值或者其他枚举。
虽然 XSLT 可以发现所有的 simpleType 声明,它无法理解这些类型和基本类型之间的关联,也无法方便的评估这些类型的含义。 Schema 中抽象 Infoset 关联的 Model 表示包含了 simpleType.getEffectiveEnumerationFacets()
,可以获得 Schema 中的类型、引用和其他关联。如果合适的话,它返回完整的 simpleType
枚举列表,可以方便的查询或者更新为新数值。
![]() ![]() |
![]()
|
样本代码
本文提供了一个样本程序 MakeSoapCompatible.java ,显示了一个实例。任何有更多兴趣的人可以从那里阅读更清楚的注释。另外,也包含了一个简单的 Schema 文档 MakeSoapCompatible.xsd ,显示了一个基本的订单,其中的属性将转变为元素;对您的其他 Schema 文档也可以运行该样本程序。如果要独立运行,至少需要 XSD Schema Infoset Model 和 Eclipse Modeling Framework 。
您可以下载样本 Zip 文件中的样本程序和实用程序(参见 参考资料)。
在 XSD Schema Infoset Model(版本 1.0.1 或者更高版本)中通常附带了其他两个实用 .java 文件,其中附带注释的代码也显示了其他一些实用技术。它们包括:
XSDSchemaQueryTools.java 展示了其他查询 Schema 组件的高级方法。
XSDSchemaBuildingTools.java 有一些易用的方法,可以通过编程来构建 Schema 。
相关推荐
在.NET中使用XML<br><br>3.1 XML如何适合.NET<br>3.1.1 XML<br>3.1.2 文档对象模型(DOM)<br>3.1.3 命名空间<br>3.1.4 DTD和XML Schema<br>3.1.5 XPath <br>3.1.6 XSLT<br>3.2 .NET Framework使用XML<br>3.2.1 配置...
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> <xs:element name="person"> <xs:complexType> <xs:sequence> <xs:element name="firstName" type="xs:string"/> <xs:element name="lastName" type=...
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"> <SOAP-ENV:Header/> <SOAP-ENV:Body> <PlaceOrder xmlns="http://example.com/soap/orders"> <Order> <OrderNumber>12345</...
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <soap:Header> <TransactionID ...
<soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/> <operation name="sayHello"> <soap:operation soapAction="urn:example.com/helloworld#sayHello"/> <input> <soap:body use=...
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"> <SOAP-ENV:Header/> <SOAP-ENV:Body> <PlaceOrder xmlns="urn:oreilly-jaws"> <Order> <Item> <ProductID>076790424X</...
例如,SOAP(Simple Object Access Protocol)和RESTful API常常使用XML作为消息格式,XML Schema用于定义XML文档的结构,RSS(Really Simple Syndication)和Atom feeds也是基于XML的。 XML的学习并不困难,关键...
`<message>` 消息中的 `<part>` 元素可以使用 XML Schema 内置类型、复合类型、或者 `<types>` 中定义的元素,也可以使用通过 `<import>` 元素链接的外部 WSDL 元素定义的类型。此外,还能描述 SOAP 头的内容,以及...
`XmlSchema-1.4.6.jar` 是一个包含Java绑定的XML Schema API的库,它为处理XML Schema文档提供了支持。在开发Web服务客户端时,尤其是与基于SOAP(简单对象访问协议)的Web服务交互时,这个库扮演了至关重要的角色。...
`xmlschema-core-2.0.3.jar` 是一个Java库,专门用于解析和操作XML Schema文档,它支持XML Schema 1.1标准。 在Spring Web服务中,XML Schema Core库扮演着关键角色。Spring Web Service框架是基于合同优先...
XmlSchema库则为Java开发者提供了处理这些XML Schema文档的API,使得在Java应用程序中解析、验证和生成符合XML Schema的XML文档变得容易。 在开发过程中,使用XmlSchema-1.4.7.jar可以帮助开发者实现以下功能: 1. ...
它可能涉及XML的解析、创建、更新、查询等功能。在实际开发中,这样的脚本可能用于从XML文件中提取数据,将数据转换为PHP数组,或者将PHP数据结构转换成XML格式以便在网络间传输。 总的来说,XML和PHP的结合使得...
### WebService之WSDL自学笔记 #### 1、WSDL概述 WSDL(Web Services Description Language,网络服务描述语言)是一种基于XML的标准语言,用于描述Web服务及其如何被访问。...<definitions xmlns:soap=...
- 嵌套规则:XML元素必须正确嵌套,即开始标签和结束标签配对且顺序正确,如`<book><title>...</title></book>`。 - 标签大小写敏感:XML标签和属性名是大小写敏感的,`<Book>`与`<book>`是不同的。 - 结束标签:...
<faultcode>SOAP-ENV:Client</faultcode> <faultstring>It is not allowed to send greetings on Tuesdays.</faultstring> <detail> <errorDetail>...</errorDetail> </detail> </SOAP-ENV:Fault> </SOAP-ENV...
xmlns:xsd="http://www.w3.org/2001/XMLSchema"> </xsd:schema> </types> <!-- 定义消息 --> <message name="操作名 Request"> <part name="term" type="xsd:string"/> </message> <message name="操作名 ...
<title>XML入门教程</title> <author>张三</author> </book> ``` - 开始标签与结束标签必须匹配。 - 元素可以嵌套,但不能交叉嵌套。 - 标签名称区分大小写。 3. **属性**:元素可以包含零个或多个属性,...
<soap:Envelope xmlns:xsi=".w3.org/2001/XMLSchema-instance" xmlns:xsd=".w3.org/2001/XMLSchema" xmlns:soap="schemas.xmlsoap.org/soap/envelope/"> <soap:Body> <Header> <ErrorFlag>int</ErrorFlag> ...
XML文档由一系列元素组成,每个元素都有其特定的开始标签(<elementname>)和结束标签(</elementname>)。元素可以包含文本内容、子元素或者两者都有。例如,下面是一个简单的XML片段: ```xml <book> <title>XML...
XML还与其他技术紧密相关,如SOAP(Simple Object Access Protocol)用于创建Web服务,以及XML Schema和DTD(Document Type Definition)用于定义XML文档的结构和数据类型。 总的来说,XML作为一种灵活的数据表示和...