使用验证解析器

现在,已经对非验证解析器进行了大量实验。应该来看看验证解析器并看看用它解析示例表示时会发生什么。

关于验证解析器首先需要知道的两件事为:

·   需要模式或文档类型定义 (DTD) 。

·   由于使用了模式/DTD,任何时候都有可能调用ignorableWhitespace 方法。

配置Factory

第一步要修改Echo程序,确保它使用的是验证解析器而不是非验证解析器。


注意:本节的代码包含在 Echo10.java中。


要使用验证解析器,必须作出下面的改变:

public static void main(String argv[])
{
  if (argv.length != 1) {
    ...
  }
  // Use the default (non-validating) parser
  // Use the validating parser
  SAXParserFactory factory = SAXParserFactory.newInstance();
  factory.setValidating(true);
  try {
    ... 

这里,配置了工厂,所以在调用newSAXParser 时它会产生验证解析器。也可以配置它使用setNamespaceAware(true)返回可理解命名空间的解析器。JWSDP实现支持配置选项的任何组合。(如果任何特定的实现不支持某个组合,就必须产生工厂配置错误)。

使用XML Schema进行验证

虽然完整的XML Schema处理已经超出了本教程的范围,本节仍会介绍验证一个使用XML Schema语言编写的现有模式的XML文档的步骤。(如果想获得关于XML Schema的更多信息,请查看http://www.w3.org/TR/xmlschema-0/中的在线教程XML Schema 部分 0:入门,也可以查看JAXP下载中的示例程序。它们使用简单的XML Schema定义来验证存储在XML文件中的个人数据。)


注意:有多种模式-定义语言,包括RELAX NG、Schematron和W3C "XML Schema" 标准。(甚至DTD也可能是一个"模式",虽然只有它不使用XML语法来描述模式约束。)然而,"XML Schema" 对我们带来了术语上的挑战。虽然"XML Schema 模式"一词更加精确,但我们将使用词 "XML Schema 定义" 来避免重复。


如果要获得文档中验证错误的通知,必须配置解析器工厂来创建验证解析器,如前一节所示。另外,

1. 必须正确设置SAX解析器属性。

2. 必须正确设置错误处理程序。

3. 文档必须和一个模式相关联。

设置SAX解析器属性

首先来定义一些在设置属性的时候需要使用的常量:

static final String JAXP_SCHEMA_LANGUAGE =
    "http://java.sun.com/xml/jaxp/properties/schemaLanguage";
 
static final String W3C_XML_SCHEMA =
    "http://www.w3.org/2001/XMLSchema"; 

然后,需要配置解析器工厂,以产生能识别并验证命名空间的解析器:

...
  SAXParserFactory factory = SAXParserFactory.newInstance();
  factory.setNamespaceAware(true);
  factory.setValidating(true); 

在使用命名空间 中会进一步介绍命名空间。现在,需要知道模式验证是一种面向命名空间的处理。由于默认情况下,遵守JAXP的解析器不是能识别命名空间的解析器,所以必须设置属性,以便运行模式验证器运行。

最后一步是配置解析器告诉它使用哪种模式语言。这里,将使用前面定义的常量来值定W3C的 XML Schema 语言。

saxParser.setProperty(JAXP_SCHEMA_LANGUAGE, W3C_XML_SCHEMA); 

然而,在这个过程中,要处理另外一个错误。下面会具体介绍该错误。

建立合适的错误处理程序

除了前面已经学习到的错误处理程序外,在对解析器进行基于模式验证的配置时会出现另外一种错误。如果解析器不遵守JAXP 1.2,也就是不支持XML Schema,它有可能抛出SAXNotRecognizedException

要处理这种情况,将setProperty() 语句包装在try/catch 块中,如下代码所示:

...
SAXParser saxParser = factory.newSAXParser();
try {
  saxParser.setProperty(JAXP_SCHEMA_LANGUAGE, W3C_XML_SCHEMA);
} 
catch (SAXNotRecognizedException x) {
  // Happens if the parser does not support JAXP 1.2
  ...
}
... 

关联文档和模式

既然程序准备使用XML Schema定义验证数据,只需要确保XML文档和其中的一个结合起来了。可以通过两种方法来实现:

·   通过XML文档中的模式声明

·   指定应用程序中使用的模式


注意:应用程序指定要使用的模式时,它会覆盖文档中的任何模式声明。


要指定文档中的模式定义,需要创建类似于下面的XML:

<documentRoot
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:noNamespaceSchemaLocation='YourSchemaDefinition.xsd'
>
  ... 

第一个属性定义了XML命名空间(xmlns)前缀"xsi",其中"xsi"代表了"XML Schema 实例"。第二行指定了没有使用命空间前缀的文档中的元素要使用的模式——也就是说,用于专门在简单的、不复杂的XML文档中定义的元素的模式。


注意:在使用命名空间会介绍命名空间。现在,将这些属性视作“魔咒”,用来验证没有使用它们的简单的XML文件。你一旦学习了命名空间,你就会知道如何使用XML Schema来验证复杂的使用命名空间的文档。在使用多个命名空间进行验证中会讨论这些观点。


也可以使用类似于下面所示的代码在应用程序中指定模式文件:

static final String JAXP_SCHEMA_SOURCE =
    "http://java.sun.com/xml/jaxp/properties/schemaSource";
 
...
SAXParser saxParser = spf.newSAXParser();
...
saxParser.setProperty(JAXP_SCHEMA_SOURCE,
    new File(schemaSource)); 

既然你已经知道如何使用XML Schema定义,下面就介绍当应用程序验证传入数据时可能出现的各类错误。为此,在进行验证时需要使用一个文档类型定义(DTD)。

试验验证错误

要查看XML文档没有指定DTD时会发生什么,从XML文件中删除DOCTYPE 语句,并且在它上面运行Echo程序。


注意:这里的输出结果保存在Echo10-01.txt中。(可浏览版本是Echo10-01.html


看到的结果跟下面的内容很相似:

<?xml version='1.0' encoding='UTF-8'?>
** Parsing error, line 9, uri .../slideSample01.xml
  Document root element "slideshow", must match DOCTYPE root 
"null" 

注意:上面的消息是由JAXP 1.2库产生的。如果使用不同的解析器,错误消息可能会有所不同。


该消息说明文档的根元素必须和DOCTYPE 声明中指定的元素匹配。该声明指定了文档的DTD。由于现在还没有,它的值为"null"。也就是说,该消息说明你正在验证文档,但是没有声明任何DTD ,因为没有任何DOCTYPE 声明。

现在,你知道了DTD是有效文档的必要组成部分。当在幻灯片放映程序的当前版本上运行解析器,并且指定了DTD时会发生什么?


注意:这里显示的输出结果,是由slideSample07.xml 产生的,保存在Echo10-07.txt.中。(可浏览版本是 Echo10-07.html)


这次,解析器给出了不同的错误消息:

  ** Parsing error, line 29, uri file:...
  The content of element type "slide" must match 
"(image?,title,item*) 

注意:上面的消息是由JAXP 1.2库产生的。如果使用不同的解析器,错误消息可能有所不同。


该消息表明第29行找到的元素 (<item>) 跟DTD中<slide> 元素的定义不匹配。出现错误是因为该定义表明slide 元素需要title 。该元素不是可选的,但是版权幻灯片中没有它。要解决这个问题,需要添加下面的问号,使得title 成为可选元素:

<!ELEMENT slide (image?, title?, item*)> 

运行程序的时候会发生什么呢?


注意:你可以删除版权幻灯片,该幻灯片产生的结果跟下面显示的相同,如 Echo10-06.txt中所示。(可浏览的版本是Echo10-06.html)


答案是一切都很正常,直到解析器运行到包含在概述幻灯片中的<em> 标签时。由于该标签不是在DTD中定义的,所以验证文档的试图失败了。输出结果如下所示:

  ...
  ELEMENT: <title>
  CHARS:   Overview
  END_ELM: </title>
  ELEMENT: <item>
  CHARS:   Why ** Parsing error, line 28, uri: ...
Element "em" must be declared.
org.xml.sax.SAXParseException: ...
... 

注意:上面的消息是由JAXP 1.2库产生的。如果使用不同的解析器,错误消息可能会有所不同。


错误消息显示了引起验证失败的DTD中的相应部分。本例中是将item 元素定义成 (#PCDATA | item)的那一行。

练习: 复制文件并删除该文件中出现的所有<em> 。文件现在是否还能验证? (在下一节中,会介绍如何定义参数条目,这样就可以在定义成幻灯片放映的元素中使用XHTML)

验证解析器中的错误处理

必须要意识到当文件验证失败时,抛出异常的唯一理由是,它是本教程前面阶段输入的错误处理程序代码的结果。重新产生的代码如下所示:

public void error(SAXParseException e)
throws SAXParseException
{
  throw e;
} 

如果没有抛出异常,那么就忽略验证错误。

练习: 给抛出异常的行写注释。现在运行解析器会出现什么情况?

总的说来,SAX解析错误是验证错误,虽然可以看到如果文件指定了一个解析器没有准备解析的XML版本,那么也会产生这个错误。要记住的是应用程序不会产生验证异常除非你提供了一个跟前面一样的错误处理程序。

定义参数实体和条件段

就跟通用实体允许你在多个地方重用XML数据一样,参数实体也允许你在多个地方重用DTD的部分内容。在教程的本节中,会介绍如何定义并使用参数实体。你也会看到在DTD中如何使用条件段参数实体。

创建并引用参数实体

回想一下,幻灯片放映的现有版本是不能被验证的,这是因为文档使用了<em> 标签,并且这些不是DTD的一部分。总的说来,在幻灯片文本中使用全部种类的HTML-风格标签,不是仅仅使用一个或两个,所以,XHTML中更适合使用现有的DTD而不定义可能遇到的所有标签。参数实体就是用于这个目的的。


注意:这里显示的DTD规范包含在slideshow2.dtd 中。引用它的XML文件是slideSample08.xml。 (可浏览版本是slideshow2-dtd.htmlslideSample08-xml.html.)


为幻灯片放映程序打开DTD文件,添加下面的文本,定义引用外部DTD文件的参数实体:

<!ELEMENT slide (image?, title?, item*)>
<!ATTLIST slide 
      ...
> 
<!ENTITY % xhtml SYSTEM "xhtml.dtd">
%xhtml; 
<!ELEMENT title ... 

这里,使用<!ENTITY> 标签来定义参数实体,就跟通用实体一样,但是使用不同的语法。在定义实体的时候,在实体名前使用一个百分号(%),并且在引用它的时候,使用百分号替代&符号。

同样,注意使用参数实体一般有两个步骤。第一步是定义一个实体名。第二步是引用该实体名,这一步实际上是在当前DTD中引用外部定义。由于外部实体的URI不能包含斜杠(/)或其他在XML名中无效的字符,第一步允许将有效的XML名和实际文档关联起来。(在定义命名空间时和其他任何XML结构需要引用外部文档时使用相同的技术)

注意:

·   该定义引用的DTD文件是xhtml.dtd。可以将该文件复制到系统中或修改<!ENTITY> 标签中的SYSTEM 标识符让它指向正确的URL。

·   该文件是XHTML规范的子集,在模块化的XHTML草稿后建模,目的是将XHTML 的DTD 分解为很小的厚片,可以组合它们创建不同的XHTML子集实现不同的目的。模块化XHTML草案工作没有完成时,应该使用其他更好的东西来替代该版本的DTD。目前的版本已经能够满足我们的需要了。

使用基于XHTML的DTD是为了访问定义的涉及到HTML风格(如<em><b>)的实体。查看xhtml.dtd 中的下列实体,它们实现了我们所希望的功能:

  <!ENTITY % inline "#PCDATA|em|b|a|img|br">  

这是在模块化XHTML草案中定义的实体的简单版本。它定义了最常用的HTML-风格标签——加重、加粗和换行,以及其他在幻灯片放映中可能使用也可能不用的图像和锚标签。要使用inline 实体,在DTD文件中做如下改动:

<!ELEMENT title (#PCDATA %inline;)*>
<!ELEMENT item (#PCDATA %inline; | item)* > 

这些变化使用inline 实体替代简单的#PCDATA 项。必须要注意的是,#PCDATA 首先在inline 实体中,并且使用的时候先使用它。这是混合内容模型的XML定义要求的。根据该模型,必须在title 定义的结尾部分加上星号。(在下面两节中,你会发现title 元素的定义跟xhtml.dtd 中定义的版本冲突,并且使用不同的方法解决该问题。)


注意:模块化的XHTML DTD 定义了inlineInline 实体,并且在定义的时候稍有不同。没有指定#PCDATA|em|b|a|img|Br,它们的定义类似于 (#PCDATA|em|b|a|img|Br)*。因此,使用其中的一个定义,看起来更像:

<!ELEMENT title %Inline; >

条件段

在进行下一个编程练习之前,必须要提一下使用参数实体来控制条件段的问题。虽然不能够条件化XML文档的内容,但只要指定了include就能够在DTD中定义条件段并使它成为DTD的一部分。然而,如果指定了ignore,那就不会包括条件段。

例如,假设你希望使用稍微有些不同的DTD版本,这取决于是将文档视为XML文档还是SGML文档。可以使用如下方法进行DTD定义:

someExternal.dtd: 
  <![ INCLUDE [
    ... XML-only definitions
  ]]>
  <![ IGNORE [
    ... SGML-only definitions
  ]]>
  ... common definitions  

条件段引用由"<!["开始,后面接着是INCLUDEIGNORE 关键字和另一个"["。这之后是条件段的内容,最后是结束符"]]>"。这里,包含了XML定义,而SGML定义被排除在外。这很适合XML文档,但是可以在SGML文档中使用DTD。当然,可以改变关键字,但是这只会使问题变得相反。

解决方案是在INCLUDEIGNORE关键字处使用参数实体的引用:

someExternal.dtd: 
  <![ %XML; [
    ... XML-only definitions
  ]]>
  <![ %SGML; [
    ... SGML-only definitions
  ]]>
  ... common definitions  

然后每个使用DTD的文档能够建立适合的实体定义:

<!DOCTYPE foo SYSTEM "someExternal.dtd" [
  <!ENTITY % XML  "INCLUDE" >
  <!ENTITY % SGML "IGNORE" >
]>
<foo>
  ...
</foo>  

该过程使得每个文档受到DTD的控制。并且使用更能反映条件段目的的变量名来替代INCLUDEIGNORE 关键字,产生一个更具有可读性的自文档化(self-documenting)的DTD版本。

解析参数化的DTD

在本节中使用Echo程序查看在slideshow.dtd 中引用xhtml.dtd 时会发生什么。这也涉及到使用DTD时SAX解析器产生的警告信息。


注意:本节中描述的输出结果在Echo10-08.txt中。(可浏览版本是Echo10-08.html)


在回送幻灯片放映时,你会发现现在有一个新错误。下面是输出结果的相关部分(按易读格式显示):

<?xml version='1.0' encoding='UTF-8'?>
** Parsing error, line 22, uri: .../slideshow.dtd
Element type "title" must not be declared more than once. 

注意:上面的消息是JAXP 1.2库产生的。如果使用不同的解析器,错误消息可能会有所不同。


看起来xhtml.dtd 定义的title 元素跟幻灯片显示DTD中定义的title元素完全不同。这是因为在DTD中没有层次,这两个定义发生冲突。

注意:模块化的XHTML DTD 也定义了一个 title 元素,该元素将成为文档标题,所以改变 xhtml.dtd并不能够避免冲突——后面还会遇到这个问题。


也可以使用XML命名空间来解决该冲突,或使用模式标准中提出的其他更具有层次性的模式。现在,仅仅是简单地重命名slideshow.dtd中的title元素。


注意:这里的XML包含在slideshow3.dtdslideSample09.xml中,它们引用了copyright.xmlxhtml.dtd。处理结果保存在Echo10-09.txt中。 (可浏览版本是slideshow3-dtd.htmlslideSample09-xml.html copyright-xml.htmlxhtml-dtd.html和Echo10-09.html)。


为了隔开这两个标题元素, 使用“用连字符连接的层次结构”。做出如下所示的变动,将slideshow.dtd中的title元素的名字变成slide-title

<!ELEMENT slide (image?, slide-title?, item*)>
<!ATTLIST slide 
      type   (tech | exec | all) #IMPLIED
> 
<!-- Defines the %inline; declaration -->
<!ENTITY % xhtml SYSTEM "xhtml.dtd">
%xhtml; 
<!ELEMENT slide-title (%inline;)*> 

下一步是修改XML文件使用新元素名。为此,执行下面的改动:

...
<slide type="all">
<slide-title>Wake up to ... </slide-title>
</slide> 
... 
<!-- OVERVIEW -->
<slide type="all">
<slide-title>Overview</slide-title>
<item>... 

现在,在幻灯片放映的当前版本上运行Echo程序。它应运行至完成并显示如Echo10-09中所示的输出结果。

恭喜!你现在已经阅读了一个完全验证过的XML文档。你所做的变化对将DTD的 title 元素放入幻灯片显示“命名空间” 有影响,该命名空间是使用连接符连接名字人工构成的。现在“幻灯片命名命名空间”中的title 元素(slide-title)不会再跟xhtml.dtd 中的title 元素冲突。教程的下一节,会介绍如何不重命名定义就实现它。在结束本节之前,来看一看验证解析器处理DTD时可能产生的警告的类型。

DTD警告

本教程的前面已经提过了,只有当SAX解析器处理DTD的时候才会产生警告。一些警告只能由验证解析器产生。非验证解析器的主要目标是提高处理速度,越快越好,但是它也产生一些警告。 (下面的解释介绍了警告及其内容)

 

XML规范建议在下面的情况下可以产生警告:

·   为实体、属性或符号提供其他定义。

(忽略这类声明。仅使用第一个。同样,注意验证时复制元素的定义通常会产生致命错误)

·   引用没有定义的元素类型。

(仅在XML文档中使用没有定义的类型时会出现验证错误。DTD中引用没有定义的元素时会产生警告。)

·   为没有定义的元素类型定义属性。

Java XML SAX解析器也在其他情况下发出警告,例如:

·   验证时没有<!DOCTYPE ...>。

·   没有验证就引用一个没有定义的参数实体。

(验证时产生错误。虽然不需要非验证解析器来读取参数实体,但Java XML解析器完成了该功能。由于这并不是必须的,Java XML解析器产生警告,而并不产生错误。)

·   字符编码声明看起来并不正确的情况下。

这时,你已经领会了许多XML概念,包括DTD、外部实体。你也按照自己的方法学习了SAX解析器。SAX教程的下面部分介绍一些高级主题,只有当你编写基于SAX的应用程序时才需要理解它们。因此如果你的主要目标是编写基于DOM的应用程序,你可以直接跳到文档对象模型。

处理词法事件

前面已经看到,如果将文本写成XML,需要知道是否位于在CDATA 段中。如果是,那么输出的时候一定不要改变尖扩号(<)和符号(&)。但是如果不在CDATA 段中,应该使用预定义实体&lt;&amp;替代它们。但是如何知道是否正在处理CDATA 段呢?

如果你正在使用某种方法过滤XML,你将再次希望传递注释。通常情况下,解析器忽略注释。如何才能获得注释然后回送它们?

最后涉及到一些已析实体的定义。如果XML过滤应用程序看到&myEntity; 它需要回送相同的字符串——而不是插入在它的位置上的文本。如何进行处理来实现呢?

教程的本节回答了这些问题?它显示了如何使用org.xml.sax.ext.LexicalHandler t来识别注释、CDATA 段和对已析实体的引用。

注释、CDATA 标签和对已析实体的引用构成了词法信息——就是涉及XML自身文本的信息,而不是XML信息内容。当然,绝大多数应用程序只关心XML文档的内容。这类应用程序不使用LexicalEventListener API 。但是输出XML文本的应用程序将会发现它非常宝贵。


注意:词法事件处理程序是可选的解析器功能。并没有要求解析器实现一定要支持它(JWSDP支持它)。该讨论假设使用的解析器支持该功能。


LexicalHandler是如何工作的

为了在SAX解析器看到词法信息的时候能够得到通知,需要配置带有LexicalHandler 的解析器之下的XmlReaderLexicalHandler 接口定义了这些事件处理方法:

comment(String comment)

将命令传递给应用程序。

startCDATA(), endCDATA()

说明CDATA 段何时开始何时结束,告诉应用程序下次调用characters() 时会使用什么字符。

startEntity(String name), endEntity(String name)

给出已析实体的名字。

startDTD(String name, String publicId, String systemId), endDTD()

说明何时处理DTD,并识别它。

使用LexicalHandler

本节的剩下部分,将Echo应用程序转变成词法处理程序并使用它的性能。


注意:本节的代码在Echo11.java中。输出结果在Echo11-09.txt中。(可浏览版本是 Echo11-09.html)


首先,添加下面的代码实现LexicalHandler 接口并添加合适的方法。

import org.xml.sax.*;
import org.xml.sax.helpers.DefaultHandler;
import org.xml.sax.ext.LexicalHandler;
...
public class Echo extends HandlerBase
  implements LexicalHandler
{ 
  public static void main(String argv[])
    {
      ...
      // Use an instance of ourselves as the SAX event handler
      DefaultHandler handler = new Echo();
      Echo handler = new Echo();
      ... 

这时,Echo 类扩展一个类并实现一个附加接口。根据变量改变处理程序的类,可以使用跟DefaultHandlerLexicalHandler相同的实例。

然后,添加下面的代码,得到解析器授权的XMLReader ,并配置它将词法事件发送给词法处理程序:

public static void main(String argv[])
{
  ...
  try {
    ...
    // Parse the input
    SAXParser saxParser = factory.newSAXParser();
    XMLReader xmlReader = saxParser.getXMLReader();
    xmlReader.setProperty(
      "http://xml.org/sax/properties/lexical-handler",
      handler
      ); 
    saxParser.parse( new File(argv[0]), handler);
  } catch (SAXParseException spe) {
    ... 

这里,使用XMLReader 类中定义的setProperty()方法配置XMLReader 。定义成SAX标准一部分的的属性名是URL, http://xml.org/sax/properties/lexical-handler.

最后,添加下面的代码,定义实现该接口的适当方法。

public void warning(SAXParseException err)
  ...
}
 
public void comment(char[] ch, int start, int length)throws SAX-
Exception
{
}
 
public void startCDATA()
throws SAXException
{
}
 
pubic void endCDATA()
throws SAXException
{
}
 
public void startEntity(String name)
throws SAXException
{
}
 
public void endEntity(String name)
throws SAXException
{
}
 
public void startDTD(
  String name, String publicId, String systemId)
throws SAXException
{
}
 
public void endDTD()
throws SAXException
{
}
 
private void echoText()
  ... 

现在已经将Echo 类变成了词法处理程序。在下一节,将测试词法事件。

回送注释

下一步与其中的一个新方法有关。添加如下代码来回送XML文件中的注释:

public void comment(char[] ch, int start, int length)
  throws SAXException
{
  String text = new String(ch, start, length);
  nl(); 
  emit("COMMENT: "+text);
} 

当编译Echo程序并在你的XML文件上运行它时,结果如下所示:

COMMENT:   A SAMPLE set of slides 
COMMENT:  FOR WALLY / WALLIES 
COMMENT: 
  DTD for a simple "slide show". 
COMMENT:  Defines the %inline; declaration 
COMMENT:  ... 

将注释中的行结尾作为注释字符串的一部分传递,再次将新行正常化。还会看到DTD中的注释是和文件中的注释一起回送的。(当只想回送数据文件中的注释时,这会出现问题。要避免该问题,需要使用startDTDendDTD 方法)

回送其他词法信息

在结束本节前,来练习一下其他LexicalHandler 方法。


注意:本节的代码在Echo12.java中。它工作在slideSample10.xml文件上(可浏览版本是slideSample10-xml.html)处理的结果在Echo12-10中。


作出如下改动,删除注释回送(不再需要了)并回送其他事件和事件出现时积累的所有字符:

public void comment(char[] ch, int start, int length)
throws SAXException
{
  String text = new String(ch, start, length);
  nl(); 
  emit("COMMENT: "+text);
} 
public void startCDATA()
throws SAXException
{
  echoText();
  nl(); 
  emit("START CDATA SECTION");
} 
public void endCDATA()
throws SAXException
{
  echoText();
  nl(); 
  emit("END CDATA SECTION");
} 
public void startEntity(String name)
throws SAXException
{
  echoText();
  nl(); 
  emit("START ENTITY: "+name);
} 
public void endEntity(String name)
throws SAXException
{
  echoText();
  nl(); 
  emit("END ENTITY: "+name);
} 
public void startDTD(String name, String publicId, String 
systemId)
throws SAXException
{ 
  nl(); 
  emit("START DTD: "+name
    +"          publicId=" + publicId
    +"          systemId=" + systemId); 
} 
public void endDTD()
throws SAXException
{ 
  nl(); 
  emit("END DTD"); 
} 

这里是处理DTD时看到的内容:

START DTD: slideshow
      publicId=null
      systemId=file:/..../samples/slideshow3.dtd
START ENTITY: ...
...
END DTD 

注意: 要想查看处理DTD时出现的事件,请使用org.xml.sax.ext.DeclHandler


下面是使用该程序的最新版本处理内部定义的products 实体时得到的其他输出:

START ENTITY: products
CHARS:   WonderWidgets
END ENTITY: products 

下面是处理外部复制的实体得到的其他输出:

  START ENTITY: copyright
  CHARS: 
This is the standard copyright message that our lawyers
make us put everywhere so we don't have to shell out a
million bucks every time someone spills hot coffee in their
lap...
 
  END ENTITY: copyright 

最后,得到处理CDATA 段时显示的结果:

  START CDATA SECTION
  CHARS:   Diagram:
 
frobmorten <--------------fuznaten
     |          <3>          ^
     | <1>                  |   <1> = fozzle
    V                  |   <2> = framboze 
  staten----------------------+   <3> = frenzle
           <2>
 
 
  END CDATA SECTION 

总而言之,LexicalHandler 给出了需要用来精确反映源XML文本的事件符号。


注意:为了精确回送输入,需要修改characters()方法,以便按适当的格式回送文本,这取决于程序是否使用CDATA 模式。

使用DTDHandler和EntityResolver

该教程的这一节中,主要简单地讨论一下剩下的两个SAX事件处理程序:DTDHandlerEntityResolver。当DTD遇到未析实体或符号声明时,它就调用DTDHandler 。当URN (公共ID)必须被解析成URL (系统ID)时,EntityResolver就派上了用场

DTDHandler API

引用二进制实体一节中介绍了一个引用文件的方法,其中该文件包含了使用MIME数据类型的二进制数据,比如图像文件。这是一个最简单,扩展性能最好的机制。为了和以前的SGML-风格数据相兼容,它也能定义未析实体。

NDATA 关键字定义未析实体,如下所示:

  <!ENTITY myEntity SYSTEM "..URL.." NDATA gif> 

NDATA 关键字表明该实体中的数据不是可解析的XML数据,而是使用其他符号的替代数据。这时,该符号名为“gif”。DTD必须引入该符号的声明,如下所示:

  <!NOTATION gif SYSTEM "..URL.."> 

当解析器遇到未析实体或符号声明时,它对这些信息不做任何处理,只是使用DTDHandler 接口将它们传递到应用程序中。该接口定义两个方法:

notationDecl(String name, String publicId, String systemId)  
unparsedEntityDecl(String name, String publicId, 
  String systemId, String notationName)  

notationDecl 方法传递符号的名字和公共或系统标识符或两个都传递,这取决于DTD中声明了什么。unparsedEntityDecl 方法传递实体的名字、适当的标识符和使用的符号名。

注意:DTDHandler接口由DefaultHandler 类实现。

属性声明中也能使用符号。例如,下面的声明需要对GIF和PNG 图像文件格式进行注释:

<!ENTITY image EMPTY>
<!ATTLIST image 
    ...
    type  NOTATION  (gif | png) "gif"
> 

这里,声明的typegifpng。如果两个都没有指定,那么默认为gif

是否使用符号引用来描述未析实体或属性,取决于应用程序,由它进行适当的处理。解析器根本就不知道符号的语义。它仅传递声明。

EntityResolver API

可以通过EntityResolver API将公共ID (URN)转换为系统ID (URL)。例如,应用程序可能需要将href="urn:/someName" 转换为http://someURL

EntityResolver接口定义了一个简单方法:

  resolveEntity(String publicId, String systemId) 

该方法返回一个可用来访问实体内容的InputSource 对象。将URL转换为InputSource 并不很容易。但是被作为系统ID传送的URL将会成为原始文档的位置,该原始文档很可能不在Web上。要访问本地副本, 如果有的话,必须维护系统中的一个目录,该目录将名字 (公共ID)映射成本地URL。

更多信息

想获得Simple API for XML处理(SAX)标准的更多信息,请查看:

·   SAX标准页面: http://www.saxproject.org/

要获得基于方案的验证机制的更多信息,请查看:

·   W3C标准验证机制, XML方案:http://www.w3c.org/XML/Schema

·   RELAX NG的基于正则表达式的验证机制:

 http://www.oasis-open.org/committees/relax-ng/

·   Schematron的基于断言的验证机制:http://www.ascc.net/xml/resource/schematron/schematron.html

 

 

 

常见问答

下载中心

产品简介

 

 

Solaris论坛