2010年2月10日- 作为读者评论的后续活动,作者对Validation下的最后一段进行了更新,只有模式是线程安全的,而验证程序和模式工厂则不是。
验证是一个强大的工具。 它使您能够快速检查输入内容是否大致符合您的期望,并快速拒绝任何与流程无法处理的文档。 如果数据有问题,最好早发现。
在可扩展标记语言(XML)的上下文中,验证通常涉及使用几种架构语言(例如,万维网联合会(W3C)XML架构语言(XSD),RELAX NG,文档类型)中的文档内容编写详细的规范。定义(DTD)和Schematron。 有时验证是在解析时执行的,有时是在解析之后立即执行的。 但是,通常是在对输入进行任何进一步处理之前完成。 (此描述用粗笔描画-有例外。)
直到最近,程序要求进行验证的确切应用程序编程接口(API)随架构语言和解析器的不同而变化。 通常在XML的简单API(SAX),文档对象模型(DOM)和XML处理的Java™API(JAXP)中将DTD和XSD作为配置选项进行访问。 RELAX NG需要自定义库和API。 Schematron可能会使用XML(TrAX)的Transformations API; 甚至其他模式语言也要求程序员学习更多的API,即使它们执行的基本操作相同。
Java 5引入了javax.xml.validation包,以向验证服务提供独立于模式语言的接口。 当单独安装JAXP 1.3时,此包在Java 1.3和更高版本中也可用。 在其他产品中,该库的实现包含在Xerces 2.8中。
javax.xml.validation API使用三个类来验证文档: SchemaFactory , Schema和Validator 。 它还广泛使用了TrAX的javax.xml.transform.Source接口来表示XML文档。 简而言之, SchemaFactory会从中读取创建Schema对象的架构文档(通常是XML文件)。 Schema对象创建一个Validator对象。 最后, Validator对象验证表示为Source的XML文档。
清单1显示了一个简单的程序,用于根据DocBook XSD模式验证在命令行上输入的URL。
这是使用Java 2 Software Development Kit(JDK)5.0附带的Xerces版本检查无效文档时的一些典型输出:
file:///Users/elharo/CS905/Course_Notes.xml is not valid because cvc-complex-type.2.3: Element 'legalnotice' cannot have character [children], because the type's content type is element-only.
您可以轻松更改要验证的架构,要验证的文档,甚至架构语言。 但是,在所有情况下,验证都遵循以下五个步骤:
加载用于模式编写语言的模式工厂。 从源代码编译模式。 从编译的架构中创建一个验证器。 为要验证的文档创建一个Source对象。 StreamSource通常是最简单的。 验证输入源。 如果文档无效,则validate()方法将引发SAXException 。 否则,它将安静地返回。您可以连续多次重复使用相同的验证器和相同的架构。 但是,只有架构是线程安全的。 验证器和模式工厂不是。 如果同时在多个线程中进行验证,请确保每个线程都有自己的Validator和SchemaFactory对象。
一些文档通常使用xsi:noNamespaceSchemaLocation和/或xsi:schemaLocation属性来指定希望对其进行验证的架构,如下所示:
<document xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://www.example.com/document.xsd"> ...如果在不指定URL,文件或源的情况下创建模式,则Java语言会创建一种语言,该语言会在正在验证的文档中查找以查找应使用的模式。 例如:
SchemaFactory factory = SchemaFactory.newInstance("http://www.w3.org/2001/XMLSchema"); Schema schema = factory.newSchema();但是,通常这不是您想要的。 通常,文档使用者应选择架构,而不是文档生产者。 此外,这种方法仅适用于XSD。 所有其他架构语言都需要一个明确指定的架构位置。
SchemaFactory是一个抽象工厂。 抽象工厂设计模式使这一API能够支持许多不同的模式语言和对象模型。 单个实现通常仅支持众多语言和模型的一部分。 但是,一旦学习了用于根据RELAX NG模式验证DOM文档的API(例如),就可以使用相同的API来针对W3C模式验证JDOM文档。
例如, 清单2显示了一个根据DocBook的RELAX NG模式验证DocBook文档的程序。 它与清单1几乎相同。 唯一更改的是架构的位置和标识架构语言的URL。
如果使用现有的Sun JDK运行此程序,而没有额外的库,则可能会看到以下内容:
Exception in thread "main" java.lang.IllegalArgumentException: http://relaxng.org/ns/structure/1.0 at javax.xml.validation.SchemaFactory.newInstance(SchemaFactory.java:186) at DocbookRELAXNGCheck.main(DocbookRELAXNGCheck.java:14)这是因为,开箱即用,JDK不包含RELAX NG验证器。 如果无法识别模式语言,则SchemaFactory.newInstance()会抛出IllegalArgumentException 。 但是,如果您安装诸如jing和JAXP 1.3适配器之类的RELAX NG库,则它应产生与W3C模式相同的答案。
javax.xml.constants类定义几个常量以标识模式语言:
XMLConstants.W3C_XML_SCHEMA_NS_URI : http://www.w3.org/2001/XMLSchema : http://www.w3.org/2001/XMLSchema XMLConstants.RELAXNG_NS_URI : http://relaxng.org/ns/structure/1.0 XMLConstants.RELAXNG_NS_URI XMLConstants.XML_DTD_NS_URI : http://www.w3.org/TR/REC-xml : http://www.w3.org/TR/REC-xml这不是封闭列表。 实现是免费的,可以将其他URL添加到此列表中以标识其他架构语言。 通常,URL是模式语言的名称空间统一资源标识符(URI)。 例如,URL http://www.ascc.net/xml/schematron标识Schematron模式。
Sun的JDK 5仅支持XSD模式。 尽管支持DTD验证,但是无法通过javax.xml.validation API进行访问。 对于DTD,必须使用常规的SAX XMLReader类。 但是,您可以安装其他库,以添加对这些和其他架构语言的支持。
Java编程语言不限于单个架构工厂。 当您将标识特定模式语言的URI传递给SchemaFactory.newInstance() ,它将按此顺序搜索以下位置以找到匹配的工厂:
由"javax.xml.validation.SchemaFactory: schemaURL "系统属性命名的类 在$java.home/lib/jaxp.properties文件中找到的由"javax.xml.validation.SchemaFactory: schemaURL "属性命名的类 在任何可用的Java归档(JAR)文件的META-INF / services目录中找到javax.xml.validation.SchemaFactory服务提供者 JDK 5中的平台默认SchemaFactory , com.sun.org.apache.xerces.internal.jaxp.validation.xs.SchemaFactoryImpl为了增加对自己的自定义模式语言和相应的验证器的支持,您要做的就是编写SchemaFactory , Schema和Validator子类,它们知道如何处理模式语言。 然后,在这四个位置之一中安装JAR。 这对于添加约束是很有用的,这些约束使用Java之类的图灵完备语言比声明性语言(如W3C XML Schema语言)更容易检查。 您可以定义一种迷你模式语言,编写一个快速实现,然后将其插入验证层。
模式的默认响应是,如果有问题,则抛出SAXException如果没有问题,则不执行任何操作。 但是,可以提供SAX ErrorHandler来接收有关文档问题的更多详细信息。 例如,假设您要记录所有验证错误,但是当您遇到一个验证错误时,您不想停止处理。 您可以安装清单3中所示的错误处理程序。
要安装此错误处理程序,请创建它的一个实例,然后将该实例传递给Validator的setErrorHandler()方法:
ErrorHandler lenient = new ForgivingErrorHandler(); validator.setErrorHandler(lenient);有些模式所做的不只是验证。 除了提供有关文档是否有效的问题的真假答案外,他们还使用其他信息来扩充文档。 例如,它们可以提供默认属性值。 他们还可以将int或gYear之类的类型分配给元素或属性。 验证器可以创建这种类型增强的文档,并将其写入到javax.xml.transform.Result对象中。 您需要做的就是传递一个Result作为第二个参数来验证。 例如, 清单4既验证了输入文档,又根据输入与模式的组合创建了增强的DOM文档。
此过程无法将任意源转换为任意结果。 它对于流源和结果根本不起作用。 可以将SAX源扩展为SAX结果,将DOM源扩展为DOM结果。 但是SAX来源不能增加到DOM结果,反之亦然。 如果需要这样做,请首先将匹配结果扩充为SAX(对于SAX来说是SAX,对于DOM是DOM),然后使用TrAX的标识转换来更改模型。
不过,不建议使用此技术。 将文档所需的所有信息放在实例中比在实例和模式之间分割文档要可靠得多。 您可能会验证,但并非所有人都会。
W3C XML Schema Language很大程度上基于类型的概念。 元素和属性声明为int,double,date,duration,person,PhoneNumber或其他任何您可以想象的类型。 Java验证API包括一种报告此类类型的方法,尽管它出人意料地独立于该软件包的其余部分。
类型由org.w3c.dom.TypeInfo对象标识。 清单5中概述了这个简单的界面,它告诉您类型的本地名称和名称空间URI。 您还可以判断一个类型是否以及如何从另一个类型派生。 除此之外,了解类型取决于您的程序。 Java语言不会告诉您这是什么意思,也不会将数据转换为Java类型(例如double或java.util.Date 。
要获取TypeInfo对象,请向Schema对象请求ValidatorHandler而不是Validator 。 ValidatorHandler实现SAX的ContentHandler接口。 然后,您将此处理程序安装在SAX解析器中。
您还可以在ValidatorHandler (而不是解析器中)安装自己的ContentHandler 。 ValidatorHandler会将增强事件转发到您的ContentHandler 。
ValidatorHandler使TypeInfoProvider可用, ContentHandler可以随时调用该TypeInfoProvider来查找当前元素的类型或其属性之一。 它还可以告诉您属性是否为ID,以及该属性是在文档中显式指定还是在架构中默认设置。 清单6总结了该类。
最后,使用SAX XMLReader解析文档。 清单7显示了一个简单的程序,该程序使用所有这些类和接口来打印出文档中所有元素类型的名称。
这是在典型的DocBook文档上运行此代码的输出的开始:
book: #AnonType_book title: #AnonType_title subtitle: #AnonType_subtitle info: #AnonType_info copyright: #AnonType_copyright year: #AnonType_year holder: #AnonType_holder author: #AnonType_author personname: #AnonType_personname firstname: #AnonType_firstname othername: #AnonType_othername surname: #AnonType_surname personblurb: #AnonType_personblurb para: #AnonType_para link: #AnonType_link如您所见,DocBook模式为大多数元素分配匿名复杂类型。 显然,这从一个模式到下一个模式将有所不同。
如果每个人只讲一种语言,世界将是一个贫穷的地方。 如果程序员只有一种编程语言可供选择,他们将不满意。 不同的语言更好地适合不同的任务,有些任务需要不止一种语言。 XML模式没有什么不同。 您可以从大量有用的模式语言中进行选择。 在具有javax.xml.validation Java 5中,您具有可以处理所有这些内容的API。
翻译自: https://www.ibm.com/developerworks/java/library/x-javaxmlvalidapi/index.html