《Java核心技术 卷Ⅱ 高级特性(原书第10版)》一3.3.1 文档类型定义

简介: 本节书摘来华章计算机《Java核心技术 卷Ⅱ 高级特性(原书第10版)》一书中的第3章 ,第3.3.1节,[美] 凯S.霍斯特曼(Cay S. Horstmann) 著陈昊鹏 译 更多章节内容可以访问云栖社区“华章计算机”公众号查看。

3.3.1 文档类型定义

提供DTD的方式有多种。可以像下面这样将其纳入到XML文档中:
image

正如你看到的,这些规则被纳入到DOCTYPE声明中,位于由[...]限定界限的块中。文档类型必须匹配根元素的名字,比如我们例子中的conf?iguration。
在XML文档内部提供DTD不是很普遍,因为DTD会使文件长度变得很长。把DTD存储在外部会更具意义,SYSTEM声明可以用来实现这个目标。你可以指定一个包含DTD的URL,例如:
image

或者
image

警告:如果你使用的是DTD的相对URL(比如"conf?ig.dtd"),那么要给解析器一个File或URL对象,而不是InputStream。如果必须从一个输入流来解析,那么请提供一个实体解析器(请看下面的说明)。

最后,有一个来源于SGML的用于识别“众所周知的”DTD的机制,下面是一个例子:
image

如果XML处理器知道如何定位带有公共标识符的DTD,那么就不需要URL了。

注意:如果你使用的是DOM解析器,并且想要支持PUBLIC标识符,请调用DocumentBuilder类的setEntityResolver方法来安装EntityResolver接口的某个实现类的一个对象。该接口只有一个方法:resolveEntity。下面是一个典型实现的代码框架:

image

你可以从InputStream、Reader或字符串中构建输入源。

既然你已经知道解析器怎样定位DTD了,那么下面就让我们来看看不同类型的规则。
ELEMENT规则用于指定某个元素可以拥有什么样的子元素。可以指定一个正则表达式,它由表3-1中所示的组成部分构成。
image

下面是一些简单而典型的例子。下面的规则声明了menu元素包含0或多个item元素:
image

下面这组规则声明font是用一个name后跟一个size来描述的,它们都包含文本:
image

缩写PCDATA表示被解析的字符数据。这些数据之所以被称为“被解析的”是因为解析器通过寻找表示一个新标签起始的<字符或表示一个实体起始的&字符,来解释这些文本字符串。
元素的规格说明可以包含嵌套的和复杂的正则表达式,例如,下面是一个描述了本书中每一章的结构的规则:
image

每章都以简介开头,其后是1或多个小节,每个小节由一个标题和1个或多个段落、图片、表格或说明构成。
然而,有一种常见的情况是无法把规则定义得像你希望的那样灵活的。当一个元素可以包含文本时,那么就只有两种合法的情况。要么该元素只包含文本,比如:
image

要么该元素包含任意顺序的文本和标签的组合,比如:
image

指定其他任何类型的包含#PCDATA的规则都是不合法的。例如,以下规则是非法的:
image

必须重写这项规则,以引入另一个caption元素或者允许使用image元素和文本的任意组合。
这种限制简化了XML解析器在解析混合式内容(标签和文本的混合)时的工作。因为在允许使用混合式内容时难免会失控,所以最好在设计DTD时,让其中所有的元素要么包含其他元素,要么只有文本。

注意:实际上,在DTD规则中并不能为元素指定任意的正则表达式,XML解析器会拒绝某些导致非确定性的复杂规则。例如,正则表达式((x,y)|(x,z))就是非确定性的。当解析器看到x时,它不知道在两个选择中应该选取哪一个。这个表达式可以改写成确定性的形式,如(x,(y|z))。然而,有一些表达式不能被改写,如((x,y)*|x?)。Java XML库中的解析器在遇到有歧义的DTD时,不会给出警告。在解析时,它仅仅在两者中选取第一个匹配项,这将导致它会拒绝一些正确的输入。当然,解析器有权这么做,因为XML标准允许解析器假设DTD都是非二义性的。在实际应用中,这不是一个会让你睡不着觉的问题,因为大多数DTD都非常简单,根本不会遇上二义性问题。

还可以指定描述合法的元素属性的规则,其通用语法为:
image

表3-2显示了合法的属性类型(type),表3-3显示了属性默认值(default)的语法。
image

以下是两个典型的属性规格说明:
image

第一个规格说明描述了font元素的style属性。它有4个合法的属性值,默认值是plain。第二个规格说明表示size元素的unit属性可以包含任意的字符数据序列。

注意:一般情况下,我们推荐用元素而非属性来描述数据。按照这个推荐,font style应该是一个独立的元素,例如<font><style>plain</style>...</font>。然而,对于枚举类型,属性有一个不可否认的优点,那就是解析器能够校验其取值是否合法。例如,如果font style是一个属性,那么解析器就会检查它是不是4个允许的值之一,并且如果没有为其提供属性值,那么解析器还会为其提供一个默认值。

CDATA属性值的处理与你前面看到的对#PCDATA的处理有着微妙的差别,并且与...部分没有多大关系。属性值首先被规范化,也就是说,解析器要先处理对字符和实体的引用(比如é或<),并且要用空格来替换空白字符。
NMTOKEN(即名字标记)与CDATA相似,但是大多数非字母数字字符和内部的空白字符是不允许使用的,而且解析器会删除起始和结尾的空白字符。NMTOKENS是一个以空白字符分隔的名字标记列表。
ID结构是很有用的,ID是在文档中必须唯一的名字标记,解析器会检查其唯一性。在下一个示例程序中,你会看到它的应用。IDREF是对同一文档中已存在的ID的引用,解析器也会对它进行检查。IDREFS是以空白字符分隔的ID引用的列表。
ENTITY属性值将引用一个“未解析的外部实体”。这是从SGML那里沿用下来的,在实际应用中很少见到。在http://www.xml.com/axml/axml.html处的被注解的XML规范中有该属性的一个例子。
DTD也可以定义实体,或者定义解析过程中被替换的缩写。你可以在Firefox浏览器的用户界面描述中找到一个很好的使用实体的例子。这些描述被格式化为XML格式,包含了如下的实体定义:
image

其他地方的文本可以包含对这个实体的引用,例如:
image

解析器会用替代字符串来替换该实体引用。如果要对应用程序进行国际化处理,只需修改实体定义中的字符串即可。其他的实体使用方法更加复杂,且不太常用,详细说明参见XML规范。
这样我们就结束了对DTD的介绍。既然你已经知道如何使用DTD了,那么你就可以配置你的解析器以充分利用它们了。首先,通知文档生成工厂打开验证特性。
image

这样,生成器将不会报告文本节点中的空白字符。这意味着,你可以依赖font节点拥有2个子元素这一事实。你再也不用编写下面这样的单调冗长的循环代码了:
image
image

而只需仅仅通过如下代码访问第一个和第二个子元素:
image

这就是DTD如此有用的原因。你不会为了检查规则而使程序负担过重。在得到文档之前,解析器已经做完了这些工作。

提示:许多刚开始使用XML的程序员都对验证不习惯,并且最终还是在程序运行过程中分析DOM树。如果要说服你的同事让他们信服使用验证过的文档所带来的好处,那么就给他们看上述两种不同的编码方式,这样才能使他们相信你。

当解析器报告错误时,应用程序希望对该错误执行某些操作。例如,记录到日志中,把它显示给用户,或是抛出一个异常以放弃解析。因此,只要使用验证,就应该安装一个错误处理器,这需要提供一个实现了ErrorHandler接口的对象。这个接口有三个方法:
image
image

相关文章
|
1月前
|
存储 算法 安全
Java集合框架:理解类型多样性与限制
总之,在 Java 题材中正确地应对多样化与约束条件要求开发人员深入理解面向对象原则、范式编程思想以及JVM工作机理等核心知识点。通过精心设计与周密规划能够有效地利用 Java 高级特征打造出既健壮又灵活易维护系统软件产品。
85 7
|
2月前
|
Java 开发者
Java 函数式编程全解析:静态方法引用、实例方法引用、特定类型方法引用与构造器引用实战教程
本文介绍Java 8函数式编程中的四种方法引用:静态、实例、特定类型及构造器引用,通过简洁示例演示其用法,帮助开发者提升代码可读性与简洁性。
|
3月前
|
安全 算法 Java
Java泛型编程:类型安全与擦除机制
Java泛型详解:从基础语法到类型擦除机制,深入解析通配符与PECS原则,探讨运行时类型获取技巧及最佳实践,助你掌握泛型精髓,写出更安全、灵活的代码。
|
3月前
|
安全 IDE Java
Java记录类型(Record):简化数据载体类
Java记录类型(Record):简化数据载体类
420 143
|
3月前
|
Java 测试技术
Java浮点类型详解:使用与区别
Java中的浮点类型主要包括float和double,它们在内存占用、精度范围和使用场景上有显著差异。float占用4字节,提供约6-7位有效数字;double占用8字节,提供约15-16位有效数字。float适合内存敏感或精度要求不高的场景,而double精度更高,是Java默认的浮点类型,推荐在大多数情况下使用。两者都存在精度限制,不能用于需要精确计算的金融领域。比较浮点数时应使用误差范围或BigDecimal类。科学计算和工程计算通常使用double,而金融计算应使用BigDecimal。
1716 102
|
3月前
|
安全 Java 编译器
Java类型提升与类型转换详解
本文详解Java中的类型提升与类型转换机制,涵盖类型提升规则、自动类型转换(隐式转换)和强制类型转换(显式转换)的使用场景与注意事项。内容包括类型提升在表达式运算中的作用、自动转换的类型兼容性规则,以及强制转换可能引发的数据丢失和运行时错误。同时提供多个代码示例,帮助理解byte、short、char等类型在运算时的自动提升行为,以及浮点数和整型之间的转换技巧。最后总结了类型转换的最佳实践,如避免不必要的转换、使用显式转换提高可读性、金融计算中使用BigDecimal等,帮助开发者写出更安全、高效的Java代码。
223 0
|
7月前
|
存储 Java 数据挖掘
Java 中数组的多种定义方式
本文深入解析了Java中数组的多种定义方式,涵盖基础的`new`关键字创建、直接初始化、动态初始化,到多维数组、`Arrays.fill()`方法以及集合类转换为数组等高级用法。通过理论与实践结合的方式,探讨了每种定义方法的适用场景、优缺点及其背后的原理,帮助开发者掌握高效、灵活的数组操作技巧,从而编写更优质的Java代码。
364 0
|
7月前
|
Java
java中一个接口A,以及一个实现它的类B,一个A类型的引用对象作为一个方法的参数,这个参数的类型可以是B的类型吗?
本文探讨了面向对象编程中接口与实现类的关系,以及里氏替换原则(LSP)的应用。通过示例代码展示了如何利用多态性将实现类的对象传递给接口类型的参数,满足LSP的要求。LSP确保子类能无缝替换父类或接口,不改变程序行为。接口定义了行为规范,实现类遵循此规范,从而保证了多态性和代码的可维护性。总结来说,接口与实现类的关系天然符合LSP,体现了多态性的核心思想。
178 0
|
8月前
|
存储 Java 编译器
Java泛型类型擦除以及类型擦除带来的问题
本文主要讲解Java中的泛型擦除机制及其引发的问题与解决方法。泛型擦除是指编译期间,Java会将所有泛型信息替换为原始类型,并用限定类型替代类型变量。通过代码示例展示了泛型擦除后原始类型的保留、反射对泛型的破坏以及多态冲突等问题。同时分析了泛型类型不能是基本数据类型、静态方法中无法使用泛型参数等限制,并探讨了解决方案。这些内容对于理解Java泛型的工作原理和避免相关问题具有重要意义。
462 0
|
8月前
|
前端开发 Cloud Native Java
Java||Springboot读取本地目录的文件和文件结构,读取服务器文档目录数据供前端渲染的API实现
博客不应该只有代码和解决方案,重点应该在于给出解决方案的同时分享思维模式,只有思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~
Java||Springboot读取本地目录的文件和文件结构,读取服务器文档目录数据供前端渲染的API实现