扫一扫
分享文章到微信
扫一扫
关注官方公众号
至顶头条
清单 5. 验证 XML
import javax.xml.XMLConstants;
import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.SchemaFactory;
import javax.xml.validation.Schema;
import javax.xml.validation.Validator;
...
SchemaFactory schemaFactory =
SchemaFactory.newInstance(XMLConstants.W3C_SCHEMA_NS_URI);
Source schemaSource = new StreamSource(new File("constraints.xml"));
Schema schema = schemaFactory.newSchema(schemaSource);
Validator validator = schema.newValidator();
validator.validate(new StreamSource("my-file.xml"));
这里同样没有什么大的变化。只要知道要使用的类和调用的方法就很容易了。因为要进行验证,所以必须使用 Validator 类。可以使用 newValidator() 方法从 Schema 得到这个类的实例。最后可以调用 validate() 并再次传递 Source 实现,不过这一次它代表要解析和验证的 XML。
调用该方法之后就会解析和验证目标 XML。要记住,即使用 DOMSource 提供 XML(解析过的 XML 表示),解析也可能再次发生。验证仍然和解析紧密联系在一起,因此验证过程需要一点儿时间。
如果出现错误,就会抛出异常说明出了问题。JAXP 的多数实现都包括行号,有时候还有列号,帮助定位违反约束模型的位置。当然,仅仅抛出异常并不一定是解决问题的最佳方式。我将在 下一节 介绍一种更好的方法。
看起来似乎工作不少:得到工厂,得到模式,得到验证器。让 JAXP 提供一个工厂方法来完成这一切是完全可能的,比方说 validate(Source schema, Source xmlDocument) 这样的方法。但是模块化有一定的好处,在 下一节 中将看到同时使用 Schema 和 Validator 类,可以解决 XML 处理中某些非常奇特的个别情况。而且如果确实需要可以自己编写,不妨当作一个很好的练习!
深入了解验证
对于很多应用程序来说,上面介绍的这些内容就足够了。您可以把输入文档和模式交给一个方法让它去验证。简单的 Exception 告诉您遇到了问题,甚至还提供了一些解决问题的基本信息。对于将 XML 作为数据格式的应用程序,可能仅仅是传递某些信息,关于 JAXP 的验证功能可能知道这些就足够了。
但是,我们生活在一个到处都是 XML 编辑器、文件和代码生成器以及 Web 服务的世界中。对于这类应用程序,XML 就不仅仅起辅助作用,而 是 应用程序本身,基本的验证常常就不够了。对于这类应用程序,JAXP 提供了很多特性,这是下面要讨论的。
处理错误
首先,人们认为 Exception 表明发生了异常的行为。但是对于基于 XML 的应用程序而言,文件验证失败可能根本不是异常,仅仅可能的结果之一。比方说支持 XML 的编辑器或者 IDE。在这些环境中,无效的 XML 不应该造成系统崩溃和关闭。另外,如果只能以 Exception 形式报告错误 ,就过于沉重了。
当然,对于 JAXP 老手这并不新鲜,您可能已经习惯为 SAXParser 或 DocumentBuilder 提供 org.xml.sax.ErrorHandler。这个接口提供的三个方法 warning()、error() 和 fatalError() 简化了解析中的错误处理。幸运的是,验证 XML 时也有相同的设施可用。更好的是,使用的还是同一个接口。正是如此,ErrorHandler 接口在验证中与在解析中一样有用。清单 6 提供了一个简单的例子。
清单 6. 处理验证错误
import javax.xml.XMLConstants;
import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.SchemaFactory;
import javax.xml.validation.Schema;
import javax.xml.validation.Validator;
import org.xml.sax.ErrorHandler;
...
SchemaFactory schemaFactory =
SchemaFactory.newInstance(XMLConstants.W3C_SCHEMA_NS_URI);
Source schemaSource = new StreamSource(new File("constraints.xml"));
Schema schema = schemaFactory.newSchema(schemaSource);
Validator validator = schema.newValidator();
ErrorHandler mySchemaErrorHandler = new MySchemaErrorHandler();
validator.setErrorHandler(mySchemaErrorHandler);
validator.validate(new StreamSource("my-file.xml"));
和 SAX 一样,可以使用该接口自定义错误的处理。从而让应用程序从容地退出验证、打印错误消息,甚至可以尝试从错误中恢复并继续验证。如果熟悉这个接口,完全不需要再重新学习!
装入多个模式
某些很少见的情况下,可能需要从多个模式构造 Schema 对象。这有点儿费解;一个 Schema不是 对应一个模式或文件。相反,该对象表示一组约束。这些约束可以来一个文件,也可以来自多个文件。因此,可以通过 newSchema(Source[] sourceList) 为 newSchema() 方法提供一个 Source 实现数组(表示多个约束)。返回的仍然是一个 Schema 对象,表示所提供的模式的组合。
可以预料,这种情况下会出现很多错误。因此建议为 SchemaFactory 设置 ErrorHandler。很多地方都可能出问题,因此要准备好在出现的时候解决问题。
把验证集成到解析中
到目前为止,我们一直把验证作为独立于解析的单独部分。但是并非必须如此。得到 Schema 对象后,就可以将其赋给 SAXParserFactory 或 DocumentBuilderFactory,都通过 setSchema() 方法(参见清单 7)。
清单 7. 把验证集成到解析中
// Load up the document
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
// Set up an XML Schema validator, using the supplied schema
Source schemaSource = new StreamSource(new File(args[1]));
SchemaFactory schemaFactory = SchemaFactory.newInstance(
XMLConstants.W3C_XML_SCHEMA_NS_URI);
Schema schema = schemaFactory.newSchema(schemaSource);
// Instead of explicitly validating, assign the Schema to the factory
factory.setSchema(schema);
// Parsers from this factory will automatically validate against the
// associated schema
DocumentBuilder builder = factory.newDocumentBuilder();
Document doc = builder.parse(new File(args[0]));
要注意,这里 不 需要使用 setValidating() 方法显式地打开验证。任何 Schema 不是 null 的工厂所创建的解析器都会使用那个 Schema 进行验证。可以预料,验证错误都会报告给解析器设置的 ErrorHandler。
重要的警告
虽然看起来不错,我认为还不够好,JAXP 的新验证 API 存在一些严重的问题。首先,即使在 Java 5.0 和 JAXP 1.3 正式版中,我也发现有很多错误和奇怪的行为。新的 API 仍然在增加解析器支持,这意味着个别情况(很少使用的特性)仅仅部分实现了(有时候根本没有实现)。我发现很多时候,能够通过独立验证器如 xmllint验证的文档却不能通过 JAXP 的验证。
直接使用 Validator 类和 validate() 方法,与将 Schema 赋给 SAXParserFactory 或 DocumentBuilderFactory 相比,似乎更可靠。建议您采用比较保险的办法。我并不是要求您避开这种 API,而是说应该使用尽可能多的样本文档,并对验证结果检查两次,对错误处理要小心谨慎。
结束语
坦白地说,JAXP 验证 API 并没有明显的新东西。可以继续使用 SAX 或 DOM 解析和验证 XML,并结合 SAX 的 ErrorHandler 类,通过巧妙的编程也能对验证错误进行即时处理。但是这需要对 SAX 有充分的了解,需要很多时间去测试和调试并且仔细地管理内存(如果最终创建 DOM Document 对象的话)。这正是 JAXP 验证 API 闪光的地方。它提供了一种经过认真测试的、可以随时使用的解决方案,而不仅仅是是否启用模式验证的一个开关。它很容易与已有的 JAXP 代码结合在一起,增加模式验证非常简单。我相信,长期使用 XML 的 Java 开发人员一定会发现 JAXP 验证的一些优点。
如果您非常迫切的想了解IT领域最新产品与技术信息,那么订阅至顶网技术邮件将是您的最佳途径之一。
现场直击|2021世界人工智能大会
直击5G创新地带,就在2021MWC上海
5G已至 转型当时——服务提供商如何把握转型的绝佳时机
寻找自己的Flag
华为开发者大会2020(Cloud)- 科技行者