Java使用Schema模式对XML验证

XML允许创作者定义自己的标签,因其灵活的特性让其难以编写和解析。因此必须使用某种模式来约束其结构。目前最流行的这种模式有两种:DTD和SCHEMA,而后者以其独特的优势即将取代DTD模式,目前只是过渡时期。XML教程请参考这里

为什么要用Schema

DTD 的局限性

  • DTD不遵守XML语法(写XML文档实例时候用一种语法,写DTD的时候用另外一种语法)
  • DTD数据类型有限(与数据库数据类型不一致)
  • DTD不可扩展
  • DTD不支持命名空间(命名冲突)

.Schema的新特性

  • Schema基于XML语法
  • Schema可以用能处理XML文档的工具处理
  • Schema大大扩充了数据类型,可以自定义数据类型
  • Schema支持元素的继承—Object-Oriented’
  • Schema支持属性组

每设计一个模式(DTD或Schema),都对应一种新的标记语言,比如使用xhtml1-strict.dtd诞生了html。基于DTD即将被Schema取代,本篇只讨论Schema模式。Schema教程请参考这里

Schema验证XML

note.xml(d:\MyTemp\xml\note.xml)

<?xml version="1.0"?>
<note xmlns="http://www.oseye.net" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.oseye.net note.xsd"> <to>George</to>
<from>John</from>
<heading>Reminder</heading>
<body>Don't forget the meeting!</body>
</note>

note.xsd(d:\MyTemp\xml\note.xsd)

<?xml version="1.0"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://www.oseye.net" xmlns="http://www.oseye.net"
elementFormDefault="qualified"> <xs:element name="note">
<xs:complexType>
<xs:sequence>
<xs:element name="to" type="xs:string" />
<xs:element name="from" type="xs:string" />
<xs:element name="heading" type="xs:string" />
<xs:element name="body" type="xs:string" />
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>

代码解释:

xmlns:xs="http://www.w3.org/2001/XMLSchema"

显示 schema 中用到的元素和数据类型来自命名空间 "http://www.w3.org/2001/XMLSchema"。同时它还规定了来自命名空间 "http://www.w3.org/2001/XMLSchema" 的元素和数据类型应该使用前缀 xs。

targetNamespace="http://www.oseye.net" 

显示被此 schema 定义的元素 (note, to, from, heading, body) 来自命名空间: "http://www.oseye.net"。

xmlns="http://www.oseye.net" 

指出默认的命名空间是 "http://www.oseye.net"。

elementFormDefault="qualified" 

指出任何 XML 实例文档所使用的且在此 schema 中声明过的元素必须被命名空间限定。

App.java

package net.oseye.SchemaXML;

import java.io.File;

import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import javax.xml.validation.Validator; import org.xml.sax.ErrorHandler;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
/**
* xml的schema验证器
* @author oseye.net
*/
public class App {
public static void main(String[] args) throws Exception {
if (args.length < 2) {
System.out.print("usage:cmd xsdfile xmlfile");
System.exit(0);
}
SchemaFactory schemaFactory = SchemaFactory
.newInstance("http://www.w3.org/2001/XMLSchema"); Schema schema = schemaFactory.newSchema(new File(args[0]));
Validator validator = schema.newValidator();
validator.setErrorHandler(new ErrorHandler() { public void warning(SAXParseException exception)
throws SAXException {
System.out.println("警告:" + exception);
} public void fatalError(SAXParseException exception)
throws SAXException {
System.out.println("致命:" + exception);
} public void error(SAXParseException exception) throws SAXException {
System.out.println("错误:" + exception); }
});
validator.validate(new StreamSource(new File(args[1])));
}
}

PS:你或许对“SchemaFactory.newInstance(String schemaLanguage) ”的参数模式语言有疑问,这里摘录JDK文档中的描述:

模式语言
此规范使用名称空间 URI 来指定模式语言。下表显示了此规范定义的值。
要遵守此规范,实现只需支持 W3C XML 模式 1.0 即可。但是,如果实现选择支持其他模式语言,那么它必须遵守此规范中所描述的相关行为。
此处未列出的模式语言应引用它们自己的 URI 来表示它们本身。SchemaFactory 类能够在运行时定位其他模式语言的其他实现。
注意,因为 XML DTD 与解析过程有紧密联系,并对解析过程产生很大影响,因此不能将 DTD 验证定义为与解析无关的过程。出于此原因,此规范不定义 XML DTD 的语义。这并不限制实现者以他们了解的合适方式来实现它,但这里提醒用户,此接口上实现的任何 DTD 验证必然偏离 XML 1.0 中所定义的 XML DTD 语义。
值:XMLConstants.W3C_XML_SCHEMA_NS_URI ("http://www.w3.org/2001/XMLSchema"),表示的语言是:W3C XML Schema 1.0;
值:XMLConstants.RELAXNG_NS_URI ("http://relaxng.org/ns/structure/1.0"),表示的语言是:RELAX NG 1.0;

执行

java net.oseye.SchemaXML.App d:\MyTemp\xml\note.xsd d:\MyTemp\xml\note.xml

没有任何的输出,说明是验证通过的,如果我们把note.xml修改成这样:

<?xml version="1.0"?>
<note xmlns="http://www.oseye.net" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.oseye.net note.xsd"> <name>George</name>
<from>John</from>
<heading>Reminder</heading>
<body>Don't forget the meeting!</body>
</note>

执行验证将输出:

错误:org.xml.sax.SAXParseException; systemId: file:/d:/MyTemp/xml/note.xml; lin
eNumber: 5; columnNumber: 8; cvc-complex-type.2.4.a: 发现了以元素 'name' 开头的
无效内容。应以 '{"http://www.oseye.net":to}' 之一开头。

你也能从中看到了to是使用了默认命名空间的作用。

命名空间

上面提到了命名,其实命名空间这个东东最难理解,难以理解的原因是因为它是URL,其实这个URL完全可以不用URL来表示,及时使用URL也完全可以是不可用的,因为它只是一个标示。标准建议使用URL,只是为了方便其唯一性。

上文说到,每设计一个模式(DTD或Schema),都对应一种新的标记语言。在各种各样的XML实例标记语言如雨后春笋般不断涌现的过程中,将会产生这样的一种应用需求,即在一个XML文档中,包含由多个DTD描述的元素。这个想法显然是达到“物尽其用”的一个好办法,它帮助我们最大程度低利用了现有的资源。

但是,这就造成了XML元素的冲突,如在一个XML文档中有两个table元素,而他们表达的含义却不同,前者表示表格,后者表示家具:

<?xml version="1.0"?>
<root>
<table>
<tr>
<td>Apples</td>
<td>Bananas</td>
</tr>
</table>
<table>
<name>African Coffee Table</name>
<width>80</width>
<length>120</length>
</table>
</root>

我们可以使用前缀来区分

<?xml version="1.0"?>
<root>
<h:table>
<h:tr>
<h:td>Apples</h:td>
<h:td>Bananas</h:td>
</h:tr>
</h:table>
<f:table>
<f:name>African Coffee Table</f:name>
<f:width>80</f:width>
<f:length>120</f:length>
</f:table>
</root>

前缀太简短,不易阅读,于是标准规定了命名空间来和前缀一同使用,既易阅读也不会太繁冗,名空间格式:

xmlns:namespace-prefix="namespaceURI"

当命名空间被定义在元素的开始标签中时,所有带有相同前缀的子元素都会与同一个命名空间相关联。

PS:用于标示命名空间的地址不会被解析器用于查找信息。其惟一的作用是赋予命名空间一个惟一的名称。不过,很多公司常常会作为指针来使用命名空间指向实际存在的网页,这个网页包含关于命名空间的信息。

<?xml version="1.0"?>
<root>
<h:table xmlns:h="http://www.w3.org/TR/html4/">
<h:tr>
<h:td>Apples</h:td>
<h:td>Bananas</h:td>
</h:tr>
</h:table>
<f:table xmlns:f="http://www.w3school.com.cn/furniture">
<f:name>African Coffee Table</f:name>
<f:width>80</f:width>
<f:length>120</f:length>
</f:table>
</root>

如果不使用前缀则是默认命名空间,命名空间有其作用域的,具体细节请参考XML文档。然后你可以定义一个XSD或多个XSD来验证此XML格式是否正确,如何编写XSD,请参考这里

schemaLocation 属性 XML 架构实例命名空间 http://www.w3.org/2001/XMLSchema-instance(通常与前缀 xsi 关联)定义仅适用于 XML 实例文档而适用于 XML 架构文档区别于 XML 架构命名空间 http://www.w3.org/2001/XMLSchema 定义 schemaLocation 属性。
xsi:schemaLocation 属性提供种方法来查找 XML 实例文档定义命名空间 XML 架构定义值用空白分隔统资源标识符 (URI) 对列表其每对 URI 都依次包含命名空间及该命名空间 XML 架构定义(通常 .xsd 文件)位置
当 XML 文档反序列化对象时XmlSerializer 类忽略 xsi:schemaLocation 属性验证 XML 文档时XmlValidatingReader 类使用该属性值来获取 XML 架构定义

本文主要是针对《使用Spring构建RMI服务器和客户端》遇到问题后的延伸,如果你遇到了什么问题,欢迎留言交流。

上一篇:SQL Server 跨库复制表方法小笔记


下一篇:微软加速Visual Studio和Azure DevOps 云升级