目录
1 关于Ontology1.1 Ontology的定义1.2 Ontology的建模元语1.3 Ontology和语义网络1.4 Ontology的描述语言1.5 已有的Ontology及其分类1.6 构造Ontology的规则2 Ontology的研究和应用2.1 Ontology的理论研究2.2 Ontology在信息系统中的应用2.3 Ontology和语义Web3 Web Ontology Language (OWL)概述3.1 OWL简介3.2 OWL在语义网中的地位3.3 OWL的三个子语言——OWL Lite、OWL DL、OWL Full3.4 OWL语言大纲3.5 OWL Lite语言描述3.6 增量语言OWL DL和OWL Full描述4 OWL文档结构举例4.1 命名空间4.2 Ontology头4.3 基本定义4.4 Ontology映射4.5 复杂的Class【OWL DL】5 一个完整的制酒行业的Ontology的OWL建模5.1 相关背景5.2 建模决策5.3 模型6 紧急联动的一个简单建模6.1 UML模型(应急联动机构)6.2 Ontology模型7 一个比较复杂的应急联动的建模8 Jena—语义网建模的Java API8.1 简介——几个RDF建模的实例8.2 Jena与整个建模流程
此为几年前刚开始学本体时查到的资料,目前感觉还是有参考作用
作者未知
《Ontology研究综述》、w3c Ontology研究组文档以及Jena编程应用总结
其中代码仅供参考。Jena已为Apache项目,版本已更新
1 关于Ontology
1.1 Ontology的定义
Ontology最早是一个哲学的范畴,后来随着人工智能的发展,被人工智能界给予了新的定义。然后最初人们对Ontology的理解并不完善,这些定义也出在不断的发展变化中,比较有代表性的定义列表如下:
关于最后一个定义的说明体现了Ontology的四层含义:
概念模型(cerptualization)
通过抽象出客观世界中一些现象(Phenomenon)的相关概念而得到的模型,其表示的含义独立于具体的环境状态
明确(explicit)
所使用的概念及使用这些概念的约束都有明确的定义
形式化(formal)
Ontology是计算机可读的。
共享(share)
Ontology中体现的是共同认可的知识,反映的是相关领域中公认的概念集,它所针对的是团体而不是个体。
Ontology的目标是捕获相关的领域的知识,提供对该领域知识的共同理解,确定该领域内共同认可的词汇,并从不同层次的形式化模式上给出这些词汇(术语)和词汇之间相互关系的明确定义。
1.2 Ontology的建模元语
Perez等人用分类法组织了Ontology,归纳出5个基本的建模元语(Modeling Primitives):
类(classes)或概念(concepts)指任何事务,如工作描述、功能、行为、策略和推理过程。从语义上讲,它表示的是对象的集合,其定义一般采用框架(frame)结构,包括概念的名称,与其他概念之间的关系的集合,以及用自然语言对概念的描述。
关系(relations)在领域中概念之间的交互作用,形式上定义为n维笛卡儿积的子集:R:C1×C2×…×Cn。如子类关系(subclass-of)。在语义上关系对应于对象元组的集合。
函数(functions)一类特殊的关系。该关系的前n-1个元素可以唯一决定第n个元素。形式化的定义为F:C1×C2×…×Cn-1→Cn。如Mother-of就是一个函数,mother-of(x,y)表示y是x的母亲。
公理(axioms)代表永真断言,如概念乙属于概念甲的范围。
实例(instances)代表元素。从语义上讲实例表示的就是对象。
另外,从语义上讲,基本的关系共有4种:
在实际建模过程中,概念之间的关系不限于上面列出的4种基本关系,可以根据领域的具体情况定义相应的关系。
1.3 Ontology和语义网络
Ontology和语义网络的联系和区别列表如下:
联系:它们都是知识表示的形式,均可以通过带标记的有向图来表示,适合于逻辑推理。区别
【例子】:语义网络中可以表达“我的汽车是红色的”,而Ontology则适合表达如“团体组织的内部构成”等整体内容。
1.4 Ontology的描述语言
目前在具体应用中Ontology的表示方式主要有4类:
非形式化语言半非形式化语言半形式化语言形式化语言
可以用自然语言来描述Ontology,也可以用框架、语义网络或逻辑语言来描述。
目前普遍使用的方法列表如下:
1.5 已有的Ontology及其分类
目前广泛使用的Ontology列表如下:
Guarino提出以详细程度和领域依赖度两个维度对Ontology进行划分。具体说明如下:
1999年Perez和Benjamins归纳出了10种Ontologies:
知识表示Ontologies普通Ontologies顶级Ontologies元(核心)Ontologies领域Ontologies语言Ontologies任务Ontologies领域-任务Ontologies方法Ontologies应用Ontologies
但它们之间有交叉,层次不够清晰。
1.6 构造Ontology的规则
出于对各自问题域和具体工程的考虑,构造Ontology的过程各不相同。目前没有一个标准的Ontology的构造方法。最有影响的是Gruber在1995年提出的5条规则:
明确性和客观性:Ontology应该用自然语言对所定义的术语给出明确、客观的语义定义。完全性:所给出的定义是完整的,完全能表达所描述的术语的含义。一致性:由术语得出的推论与术语本身的含义是相容的,不会产生矛盾。最大单调可扩展性:向Ontology中添加通用或专用的术语时,不需要修改已有的内容。最小承诺:对待建模对象给出尽可能少的约束。
目前大家公认在构造特定领域的Ontology的过程中需要领域专家的参与。
2 Ontology的研究和应用
Ontology的研究和应用主要包括以下3方面:
理论上的研究,主要研究概念及其分类,Ontology上的代数;信息系统中的应用,主要包括处理信息组织、信息检索和异构信息系统互操作问题;Ontology作为一种能在知识层提供知识共享和重用的工具在语义Web中的应用。
2.1 Ontology的理论研究
Ontology的理论研究包括概念和概念分类、Ontology上的代数。最有代表性的是Guarino等人对概念的分类所做的深入和细致的研究,他们从一般的意义上分析了什么是概念、概念的特性、概念之间的关系以及概念的分类,提出了一套用于指导概念分类的可行理论。基于这个理论,他又提出了Ontology驱动的建模方法,在理论上为建模提供了一个通用的模式。
Guarino认为概念之间的差别不仅体现在概念的定义上,同时也体现在概念的某些特性上。从这些特性出发,归纳出概念的元特性(最基本的特性),从而用公式给出元特性的严格的形式定义。在此基础上,他们又讨论了元特性之间的关系和约束,最终把研究结果作为概念分类的基本理论工具并提出一套完成的概念分类体系结构。
Guarino的理论可以归纳如下:
概念分类理论的基础是概念的元特性。以概念的元特性为出发点,按照一定的规则,把具有相同元特性组合的概念归为一类,进而给出一般意义上的概念分类体系。概念的基本元特性包括:持久特性、非持久特性、反持久特性、半持久特性、载体标识特性、支持标识特性、外部依赖特性等。
以下是对各种特性的说明:
2.2 Ontology在信息系统中的应用
目前信息检索技术的分类和对他们的描述列举如下:
Ontology具有良好的概念层次结构和对逻辑推理的支持,在知识检索中有广泛应用。基于Ontology的信息检索的基本思想有:
在领域专家的帮助下,建立相关领域的Ontology;收集信息源中的数据,并参照已建立的Ontology把收集来的数据按规定格式存储在元数据库(RDB,KDB等)中;对用户检索界面获取的查询请求,查询转换器按照Ontology把查询请求转换成规定的格式,在Ontology的帮助下从元数据库中匹配出符合条件的数据集合;检索的结果经过定制处理返回给用户。
关于Ontology的表达,主要分为两种情况进行处理:
检索系统如不需要太强的推理能力,Ontology可用概念图的形式表示并存储,数据可以保存在一般的关系数据库中,采用图匹配技术完成检索;如要求较强的推理能力,一般需要一种描述语言(Loom等)表示Ontology,数据保存在知识库中,采用描述语言的逻辑推理能力完成检索。
目前Ontology用于信息检索的项目列举如下:
2.3 Ontology和语义Web
提高Web信息检索的质量包括两方面的内容:
如何在现有的资源上面设计更好的检索技术;如何为Web上的资源附加上计算机可以理解的内容,便于计算机处理,即给出一种计算机能够理解的表示资源的手段。
基于后一种考虑,Berners-Lee在2000-12-18的XML2000的会议上提出了语义Web。语义Web的目标是使得Web上的信息具有计算机可以理解的语义,满足智能软件代理(Agent)对WWW上异构和分布信息的有效访问和检索。下面是Berners-Lee为未来Web发展提出的基于语义的体系结构-语义Web体系结构
* 核心层,用于表示Web信息的语义。
XML和RDF都能为所表述的资源提供一定的语义。但是XML中的标签(tags)和RDF中的属性(properties)集都没有任何限制。一个例子是:XML可以用“<Author>TOM</Author>
”表示TOM是教师。而“<rdf:Description about=/Home/Lassila><s:Creator>Ora Lassila</s:Creator></rdf:Description>
”这个RDF片断描述了Web页的创建者问题。而上面的Author和Creator完全可以用Writer来代替。另一个例子是:某医院和某大学的Web页上都有<Doctor>
,但是不知道它代表医生还是博士。综上,XML和RDF在处理语义上存在的问题是:
同一概念有多种词汇表示(多词同义);同一个词汇有多种概念(含义)(一词多义)。
Ontology通过对概念的严格定义和概念之间的关系来确定概念精确含义,表示共同认可的、可共享的知识,从而解决上面的问题。因此在语义Web中,Ontology具有非常重要的地位,是解决语义层次上Web信息共享和交换的基础。
为了便于Web上应用程序使用方便,需要有一个通用的标准语言来表示Ontology,就像XML作为标准的数据交换语言一样。目前正在开发中的语言有:SHOE、OML、XOL、Riboweb、RDFS和OIL。下面将就w3c提出的OWL(Web Ontology Language)做进一步的分析。
目前语义Web是一个新兴的研究方向,Ontology在其中的应用刚刚起步。
3 Web Ontology Language (OWL)概述
3.1 OWL简介
OWL(Web Ontology Language)适用于这样的应用,在这些应用中,不仅仅需要提供给用户可读的文档内容,而且希望处理文档内容信息。OWL能够被用于清晰地表达词汇表中的词条(term)的含义以及这些词条之间的关系。而这种对词条和它们之间的关系的表达就称作Ontology。OWL相对XML、RDF和RDFSchema拥有更多的机制来表达语义,从而OWL超越了XML、RDF和RDFSchema仅仅能够表达网上机器可读的文档内容的能力。
3.2 OWL在语义网中的地位
语义网是对未来网络的一个设想,在这样的网络中,信息都被赋予了明确的含义,机器能够自动地处理和集成网上可用的信息。语义网使用XML来定义定制的标签格式以及用RDF的灵活性来表达数据,下一步需要的就是一种Ontology的网络语言(比如OWL)来描述网络文档中的术语的明确含义和它们之间的关系。
OWL是w3c推荐的语义网络“栈”中的一部分,这个“栈”被表达如下:
下图给出了w3c的Ontology语言栈描述:
W3C2002年7月31日透露了发行OWL Web 本体论语言(OWL Web Ontology Language) 工作草案的细节,其目的是为了更好地开发语义网(Semantic Web)。
W3C 发言人Ian Jacobs说,开发语义网的目的是能够在互联网上进行更结构化的智能处理,例如,当一个人确定要参加某个城市的会议后,就可以自动查找有关航班和酒店的信息。
W3C称,W3C Web 本体论工作小组正在对OWL Web本体论语言进行设计,OWL是本体论Web 语言(Ontology Web Language)的字母缩写。设计的最终目的是为了提供一种可以用于各种应用的语言,这些应用需要理解内容,从而代替只是采用人类易读的形式来表达内容。作为语义网的一部分,XML、RDF和RDF-S支持通过提供针对术语描述的词汇表,共同推进了机器的可靠性。
W3C发行的三种工作草案名为《特色大纲》(Web Ontology Language (OWL) Guide Version 1_0)、《抽象句法》(OWL Web Ontology Language 1_0 Abstract Syntax)和《语言参考》。
W3C本周还发行了其Web 服务架构使用方案集合的工作草案,目的是为下一代的Web服务提供使用案例和方案。
W3C Web服务架构工作小组特别发行的方案包括诸如旅行社使用案例和类似电子数据交换的采购等情形。Jacobs说:“W3C官员正在制定有关Web服务架构范围的文件。”
3.3 OWL的三个子语言——OWL Lite、OWL DL、OWL Full
3.3.1 子语言描述
OWL的三个子语言描述列表如下:
3.3.2 子语言间以及子语言和RDF的关系
这三种子语言之间的关系是:
每个合法的OWL Lite都是一个合法的OWL DL;
每个合法的OWL DL都是一个合法的OWL Full;
每个有效的OWL Lite结论都是一个有效的OWL DL结论;
每个有效的OWL DL结论都是一个有效的OWL Full结论。
用户在选择使用哪种语言时的主要考虑是:
选择OWL Lite还是OWL DL主要取决于用户需要整个语言在多大程度上给出了约束的可表达性;
选择OWL DL还是OWL Full主要取决于用户在多大程度上需要RDF的元模型机制(如定义类型的类型以及为类型赋予属性);
在使用OWL Full而不是OWL DL时,推理的支持不可预测,因为目前还没有完全的OWL Full的实现。
这三种子语言与RDF的关系是:
OWL Full可以看成是RDF的扩展;
OWL Lite和OWL Full可以看成是一个约束化的RDF的扩展;
所有的OWL文档(Lite,DL,Full)都是一个RDF文档;所有的RDF文档都是一个OWL Full文档;
只有一些RDF文档是一个合法的OWL Lite和OWL DL文档。
3.4 OWL语言大纲
【说明】:以下用斜体标出的为OWL中的词条(term),rdf:和rdfs:前缀表示这些词条已经在RDF和RDF Schema中出现。
3.4.1 OWL Lite语言大纲
3.4.2 OWL DL和OWL Full大纲
下面给出了在OWL Lite基础上添加的OWL DL和OWL Full语言架构
3.5 OWL Lite语言描述
和OWL DL和OWL Full相比,OWL Lite只是使用了OWL语言的一些feature,并且做了限制。
Class只能根据命名了的superclass(它不能是任意的逻辑表达式)进行定义,而且只能使用特定类型的class restriction。
类之间的Equivalence以及子类关系只能在命名了的class上做声明,不能应用于任意的类型表达式。
OWL Lite只允许属性限制应用于命名类型。
OWL Lite对cardinality的概念也有限制——它的取值范围只能是0和1。
以下列出了OWL Lite大纲中各类feature的描述。
3.5.1 OWL Lite RDF Schema Features
3.5.2 OWL Lite Equality 和Inequality
3.5.3 OWL Lite Property Characteristics
3.5.4 OWL Lite Property Type Restriction
该约束针对某个属性,属于局部约束。
3.5.5 OWL Lite Restricted Cardinality
同上,该约束也是局部约束,而且OWL Lite在Cardinality上的局部约束只允许Cardinality的取值为0和1(这不同于其他两类OWL允许任意数目的Cardinality)。
3.5.6 OWL Lite Class Intersection
3.5.7 Datatypes
OWL 使用了RDF的datatype schema,而后者又参考了XML Schema的datatype。这些datatype能够通过URI被识别。每当有一个datatype的实例出现时,必须有一个RDF属性rdf:datatype,它的值为URI引用的XML Schema datatype。
3.5.8 OWL Lite Header Information
OWL支持ontology引用、包含以及元信息描述。上面提到的三个层次的OWL都包含了用于指定导入的ontology、ontology版本信息和前版本信息、可向后兼容的ontology信息以及不兼容的ontology信息等一系列信息的方法。
3.6 增量语言OWL DL和OWL Full描述
尽管OWL DL添加了一些约束,它和OWL Lite实际上共享了词汇表。总的来讲,OWL DL引入了类型分割(一个class不能是一个property或者一个individual,一个property也不能是一个class或者individual);它要求property或者是ObjectProperties,或者是DatatypeProperties。后者RDF literal、XML Datatype以及class实例之间的关系。前者是两个class实例之间的关系。下面继续列出了OWL DL和OWL Full的扩展词汇
4 OWL文档结构举例
4.1 命名空间
这是使用OWL的一系列词条的前提,我们必须准确说明正在使用的特定的词汇表。一个Ontology的标准的初始模块是包含在rdf:RDF标签中的一系列命名空间(namespace)的声明。这些声明用以准确解释文档中的标识符,从而使得Ontology的其他部分具有可读性。以下是一个典型的命名空间声明的例子:
<rdf:RDF xmlns ="/wine#" xmlns:vin ="/wine#" xmlns:food="/food#" xmlns:owl ="/2002/07/owl#"xmlns:rdf ="/1999/02/22-rdf-syntax-ns#"xmlns:rdfs="/2000/01/rdf-schema#"xmlns:xsd ="/2000/10/XMLSchema#"xmlns:dte ="/wine-dt#">
我们也可以在Ontology定义之前在文档类型定义DocType一节中通过定义一些实体来给出命名空间的说明。例如:
<!DOCTYPE owl [<!ENTITY vin "/wine#" ><!ENTITY food "/food#" > ]>
另外,命名空间只是对标签的说明,对属性没有约束。但是在OWL文档中我们又经常要用到和命名空间相关的词条,这时就必须写清楚整个URI。例如/owl/wine#merlot。但是如果有了类似上面的实体定义,我们也可以简写成“&vin;merlot”。
4.2 Ontology头
完成了命名空间的定义,我们必须以下面的断言来开始一个OWL Ontology:
<owl:Ontology rdf:about="/wine">
接下来可以以一些标签标明注释、版本控制、以及对其他Ontology的引入等信息。例如:
<owl:Ontology rdf:about="/wine"> <rdfs:comment>An example OWL ontology</rdfs:comment><owl:versionInfo>$Id: Overview.html,v 1.2 2002/11/08 16:42:25 connolly Exp $</owl:versionInfo><owl:imports rdf:resource="/TR/2002/WD-owl-guide-20021104/food.owl"/>
其中引入另外一个Ontology将会将它的整个定义的集合加入到知识库中来。需要注意的是,这个标签只是说明了引入一个Ontology的意图,但不总是成功的。在语义网上对网上资源的访问不总是成功的,这需要依照工具实现的情况而定。
最后,给出相应的Ontology头的结束标签:
</owl:Ontology>
4.3 基本定义
4.3.1 简单的Classe和Individual
所有用户定义的class都缺省是owl:Thing的subclass。而领域相关的根class的定义只要给出一个命名的类声明就可以了。例如在制酒业的三个根class定义如下:
<owl:Class rdf:ID="Winery"/> <owl:Class rdf:ID="Region"/> <owl:Class rdf:ID="ConsumableThing"/>
这时我们只是用ID为类起了名字,并没有指定有关类的其他任何信息,例如该类的成员等等。rdf:ID
属性类似于XML中的ID属性,这样我们能够通过类似“documentURI#Region”
在其他的Ontology中引用region这一class。也可以采用类似“rdf:about="#x”
的方法引用。
Class的基本的分类构造器是subclassof。例如:
<owl:Class rdf:ID="PotableLiquid"> <rdfs:subClassOf rdf:resource="#ConsumableThing" />...</owl:Class>
这里我们定义了PotableLiquid是ConsumableThing的subclass。
类定义除了包括命名和引用以外,还包括限制。上面的subclassof就是一个限制。下面给出了Wine这个class的简单定义:
<owl:Class rdf:ID="Wine"> <rdfs:subClassOf rdf:resource="#PotableLiquid"/> <rdfs:label xml:lang="en">wine</rdfs:label> <rdfs:label xml:lang="fr">vin</rdfs:label> ... </owl:Class>
rdfs:label
标签给出了人们可读的类名。属性lang表示支持多语言表达。
一个Individual可以通过声明它是某个类的成员得以表达。例如:
<Region rdf:ID="CentralCoastRegion" />
下面的表达是等价的:
<owl:Thing rdf:ID="CentralCoastRegion" /> <owl:Thing rdf:about="#CentralCoastRegion"> <rdf:type rdf:resource="#Region"/> </owl:Thing>
type这个RDF词条将一个Individual和一个class的成员绑定起来。
下面的例子给出了一个grape分类,并用一个individual表达了Cabernet Sauvignon品种的grape:
<owl:Class rdf:ID="Grape"><owl:Class rdf:ID="WineGrape"><rdfs:subClassOf rdf:resource="#Grape"/></owl:Class><WineGrape rdf:ID="CabernetSauvignonGrape" />
4.3.2 简单的Property
property可以被用来说明class的共同特征以及某些individual的专有特征。一个property是一个二元关系。有两类property:
datatype property:class元素和XML datatype之间的关系;
object property:两个类元素之间的关系。
可以通过指定property的domain和range以及定义subproperty来约束一个property。下面是一个例子:
<owl:ObjectProperty rdf:ID="madeFromGrape"> <rdfs:domain rdf:resource="#Wine"/><rdfs:range rdf:resource="#WineGrape"/> </owl:ObjectProperty>
通过上面的定义property madeFromGrape给出了class Wine的元素和WineGrape的元素之间的关系。下面的例子给出了property的层次定义:
<owl:ObjectProperty rdf:ID="WineDescriptor" /><owl:Class rdf:ID="WineColor"><rdfs:subClassOf rdf:resource="#WineDescriptor" />...</owl:Class><owl:ObjectProperty rdf:ID="hasWineDescriptor"><rdfs:domain rdf:resource="#Wine" /><rdfs:range rdf:resource="#WineDescriptor" /></owl:ObjectProperty><owl:ObjectProperty rdf:ID="hasColor"><rdfs:subPropertyOf rdf:resource="#hasWineDescriptor" /><rdfs:range rdf:resource="#WineColor" /></owl:ObjectProperty>
下面是locatedIn property的定义:
<owl:ObjectProperty rdf:ID="locatedIn">...<rdfs:domain rdf:resource="/2002/07/owl#Thing" /><rdfs:range rdf:resource="#Region" /></owl:ObjectProperty>
该定义允许locatedIn的domain为任何一个thing。
下面是Vintage这个class的定义:
<owl:Class rdf:ID="Vintage"> <rdfs:subClassOf rdf:resource="/2002/07/owl#Thing"/> <rdfs:subClassOf><owl:Restriction> <owl:onProperty rdf:resource="#vintageOf"/><owl:minCardinality>1</owl:minCardinality></owl:Restriction></rdfs:subClassOf></Class>
propertyvintageOf
将Vintage关联到wine。
Datatype利用XML Schema datatype定义的简单类型完成定义。如果我们想把vintage的年限约束到1700以后,我们需要在其他文件中创建几个XML Schema datatype定义:
<xsd:schema xmlns:xsd="/2001/XMLSchema" xmlns="/wine-dt.xsd"><xsd:simpleType name="year"> <!-- year is an XMLS datatype based on integer --> <xsd:restriction base="xsd:decimal"/> </xsd:simpleType><xsd:simpleType name="wineYear"> <!-- wineYear is an XMLS datatype based on year --> <!-- with the added restriction that values must be GEQ 1700 --> <xsd:restriction base="year"> <xsd:minInclusive value="1700"/></xsd:restriction> </xsd:simpleType></xsd:schema>
而后我们再给出WineYear这个class的定义:
<owl:Class rdf:ID="WineYear" /><owl:DataTypeProperty rdf:ID="yearValue"><rdfs:domain rdf:resource="#WineYear" /> <rdfs:range rdf:resource="&dt;wineYear"/></owl:DataTypeProperty>
关于individual的property定义,一个例子如下:
<CaliforniaRegion rdf:ID="SantaCruzMountainsRegion" /> <Winery rdf:ID="SantaCruzMountainVineyard" /><CabernetSauvignon rdf:ID="SantaCruzMountainVineyardCabernetSauvignon" ><locatedIn rdf:resource="#SantaCruzMountainsRegion"/> <hasMaker rdf:resource="#SantaCruzMountainVineyard" /> </CabernetSauvignon>
下面创建了一个WineYear的实例并将它和一个特殊的datatype dte:wineYear关联起来:
<WineYear rdf:ID="Year1998"><yearValue rdf:datatype="&dte;wineYear">1998</yearValue></WineYear>
4.3.3 Property特征
下面给出了用于扩展定义property的各类特征。
TransitiveProperty
被标记为Transitive的property P满足下面的公理:
P(x,y) and P(y,z) -> P(x,z)
例如:
<owl:ObjectProperty rdf:ID="locatedIn"><rdf:type rdf:resource="&owl;TransitiveProperty" /><rdfs:domain rdf:resource="&owl;Thing" /><rdfs:range rdf:resource="#Region" /></owl:ObjectProperty><Region rdf:ID="SantaCruzMountainsRegion"><locatedIn rdf:resource="#CaliforniaRegion" /></Region><Region rdf:ID="CaliforniaRegion"><locatedIn rdf:resource="#UsRegion" /></Region>
其中locatedIn就是Transitive的。
SymmetricProperty
被标记为symmetric的property P满足下面的公理:
P(x,y) iff P(y,x)
例如:
<owl:ObjectProperty rdf:ID="adjacentRegion"><rdf:type rdf:resource="&owl;SymmetricProperty" /><rdfs:domain rdf:resource="#Region" /><rdfs:range rdf:resource="#Region" /></owl:ObjectProperty><Region rdf:ID="MendocinoRegion"><locatedIn rdf:resource="#CaliforniaRegion" /><adjacentRegion rdf:resource="#SonomaRegion" /></Region>
adjacentRegion就是symmetric的。
Functional Property
被标记为functional的property P满足下面的公理:
P(x,y) and P(x,z) -> y = z
例如:
<owl:Class rdf:ID="WineYear" /><owl:ObjectProperty rdf:ID="hasVintageYear"><rdf:type rdf:resource="&owl;FunctionalProperty" /><rdfs:domain rdf:resource="#Vintage" /><rdfs:range rdf:resource="#WineYear" /></owl:ObjectProperty>
hasVintageYear就是functional的,因为每种wine只有一个VintageYear。
inverseOf
一个property P1被标记为inverseof 一个property P2,满足下面的公理:
P1(x,y) iff P2(y,x)
例如:
<owl:ObjectProperty rdf:ID="hasMaker"><rdf:type rdf:resource="&owl;FunctionalProperty" /></owl:ObjectProperty><owl:ObjectProperty rdf:ID="producesWine"><owl:inverseOf rdf:resource="#hasMaker" /></owl:ObjectProperty>
InverseFunctionalProperty
一个被标记为InverseFunctional的property P满足下面的公理:
P(y,x) and P(z,x) -> y = z
例如:
<owl:ObjectProperty rdf:ID="hasMaker" /><owl:ObjectProperty rdf:ID="producesWine"><rdf:type rdf:resource="&owl;InverseFunctionalProperty" /><owl:inverseOf rdf:resource="#hasMaker" /></owl:ObjectProperty>
producesWine就是InverseFunctional的。
4.3.4 Property 约束
还可以进一步通过约束来规定property的range在某些范围内。
allValuesFrom, someValuesFrom
上面已经给出的property都是全局的属性约束。而这两个约束相对包含它们的类定义而言是局部的。例如:
<owl:Class rdf:ID="Wine"><rdfs:subClassOf rdf:resource="&food;PotableLiquid" />...<rdfs:subClassOf><owl:Restriction><owl:onProperty rdf:resource="#hasMaker" /><owl:allValuesFrom rdf:resource="#Winery" /></owl:Restriction></rdfs:subClassOf>...</owl:Class>
其中在hasMaker这个property上的allValuesFrom约束只是对wine这个class有意义。上面的allValuesFrom可以被替换为someValuesFrom,约束范围也是类似的。
Cardinality
我们可以通过Cardinality直接指定于一个class相关的class的数目,例如:
<owl:Class rdf:ID="Vintage"> <rdfs:subClassOf><owl:Restriction><owl:onProperty rdf:resource="#hasVintageYear"/> <owl:cardinality>1</owl:cardinality></owl:Restriction></rdfs:subClassOf></owl:Class>
在OWL Lite中只能指定Cardinality为0或者1,而在OWL Full中,可以使用owl:maxCardinality来指定一个上界。
hasValue [OWL DL]
hasValue允许我们定义基于特定property的值存在的class。一个individual要成为一个类的成员,它在这个property上的取值必须满足hasValue的规定。例如:
<owl:Class rdf:ID="Burgundy"><rdfs:subClassOf><owl:Restriction><owl:onProperty rdf:resource="#hasSugar" /><owl:hasValue rdf:resource="#Dry" /></owl:Restriction></rdfs:subClassOf>...</owl:Class>
4.4 Ontology映射
我们需要在Ontology上定义映射关系来重用已有的Ontology的class和property。
sameClassAs, samePropertyAs
下面是重用类的一个例子:
<owl:Class rdf:ID="Wine"><owl:sameClassAs rdf:resource="&vin;Wine"/></owl:Class>
下面是重用一个类并在其基础上进行约束的例子:
<owl:Class rdf:ID="TexasThings"> <owl:sameClassAs><owl:Restriction><owl:onProperty rdf:resource="#locatedIn" /><owl:allValuesFrom rdf:resource="#TexasRegion" /></owl:Restriction></owl:sameClassAs></owl:Class>
sameIndividualAs
它的定义和class类似,说明了两个individual是同一的,例如:
differentIndividualFrom
该定义与sameIndividualAs的效果正好相反
4.5 复杂的Class【OWL DL】
OWL通过下面的机制给出了定义类表达式的基本方法,从而能够通过嵌套定义给出一个复杂的class。
4.5.1 集合操作符
Intersection [OWL DL]下面给出了操作符intersectionOf的用法:
<owl:Class rdf:ID="WhiteWine"><owl:intersectionOf rdf:parseType="Collection"><owl:Class rdf:about="#Wine" /><owl:Restriction><owl:onProperty rdf:resource="#hasColor" /><owl:hasValue rdf:resource="#White" /></owl:Restriction></owl:intersectionOf></owl:Class>
注意类操作符是封闭的。
下面是一个通过两次嵌套定义的例子:
<owl:Class rdf:about="#Burgundy"><owl:intersectionOf rdf:parseType="Collection"><owl:Class rdf:about="#Wine" /><owl:Restriction><owl:onProperty rdf:resource="#locatedIn" /><owl:hasValue rdf:resource="#BourgogneRegion" /></owl:Restriction></owl:intersectionOf></owl:Class><owl:Class rdf:ID="WhiteBurgundy"><owl:intersectionOf rdf:parseType="Collection"><owl:Class rdf:about="#Burgundy" /><owl:Class rdf:about="#WhiteWine" /></owl:intersectionOf> </owl:Class>
Union [OWL DL]
下面是一个使用UnionOf的例子:
<owl:Class rdf:ID="Fruit"><owl:unionOf rdf:parseType="Collection"><owl:Class rdf:about="#SweetFruit" /><owl:Class rdf:about="#NonSweetFruit" /></owl:unionOf></owl:Class>
可以将它和下面的定义作比较:
<owl:Class rdf:ID="Fruit"><rdfs:subClassOf rdf:resource="#SweetFruit" /><rdfs:subClassOf rdf:resource="#NonSweetFruit" /></owl:Class>
Complement [OWL DL]
下面是一个使用complementOf的例子:
<owl:Class rdf:ID="ConsumableThing" /><owl:Class rdf:ID="NonConsumableThing"><owl:complementOf rdf:resource="#ConsumableThing" /></owl:Class>
下面是一个混和使用了各种集合操作符的例子:
<owl:Class rdf:ID="NonFrenchWine"><owl:intersectionOf rdf:parseType="Collection"><owl:Class rdf:about="#Wine"/><owl:Class><owl:complementOf><owl:Restriction><owl:onProperty rdf:resource="#locatedIn" /><owl:hasValue rdf:resource="#FrenchRegion" /></owl:Restriction></owl:complementOf></owl:Class></owl:intersectionOf></owl:Class>
4.5.2 枚举Class
oneOf [OWL DL]OWL通过one of操作符给出了枚举一个class的成员的基本方法,例如下面的例子定义了WineColor这个class拥有3个成员:
<owl:Class rdf:ID="WineColor"><rdfs:subClassOf rdf:resource="#WineDescriptor"/><owl:oneOf rdf:parseType="Collection"><owl:Thing rdf:about="#White"/><owl:Thing rdf:about="#Rose"/><owl:Thing rdf:about="#Red"/></owl:oneOf></owl:Class>
我们也可以通过直接指定每个成员的类型进行定义:
<owl:Class rdf:ID="WineColor"><rdfs:subClassOf rdf:resource="#WineDescriptor"/><owl:oneOf> rdf:parseType="Collection"><WineColor rdf:about="#White" /><WineColor rdf:about="#Rose" /><WineColor rdf:about="#Red" /></owl:oneOf></owl:Class>
4.5.3 不交的Class
disjointWith [OWL DL]它用于表达一个individual是一个class的成员,同时不能是另外一个class的成员。
例如:
<owl:Class rdf:ID="Pasta"><rdfs:subClassOf rdf:resource="#EdibleThing"/><owl:disjointWith rdf:resource="#Meat"/><owl:disjointWith rdf:resource="#Fowl"/><owl:disjointWith rdf:resource="#Seafood"/><owl:disjointWith rdf:resource="#Dessert"/><owl:disjointWith rdf:resource="#Fruit"/></owl:Class>
定义一个class是几个不交的subclass的union的例子如下:
<owl:Class rdf:ID="SweetFruit"><rdfs:subClassOf rdf:resource="#EdibleThing" /></owl:Class>
<owl:Class rdf:ID="NonSweetFruit"><rdfs:subClassOf rdf:resource="#EdibleThing" /><owl:disjointWith rdf:resource="#SweetFruit" /></owl:Class>
<owl:Class rdf:ID="Fruit"><owl:unionOf rdf:parseType="Collection"><owl:Class rdf:about="#SweetFruit" /><owl:Class rdf:about="#NonSweetFruit" /></owl:unionOf></owl:Class>
定义的规模随着不相交类的数目(n)的增长是n2级的。
5 一个完整的制酒行业的Ontology的OWL建模
这个例子是w3c的Guus Schrieber开发的关于制酒行业的更为精细的Ontology。
5.1 相关背景
对于制酒业而言,“产品地域”(production area)是一个非常重要的feature。根据生产地域的谷类的大小酒的类型存在很大的变化,从一个国家到一个特定的葡萄园。我们可以给出四种不同的产品地域:
国家(country),例如France,Italy
区域(region),例如Bordeaux,Medoc,Tuscany
城镇(town),例如Margaux, Montalcino, Montepulciano
葡萄园(vineyard),例如Chateau Margaux,Avignonesi
而且我们必须为不同产品地域之间的关系建模:
区域是国家的一部分:Tuscany在Italy
区域有子区域:Medoc是Bordeaux的子区域
城镇在某区域中:Montalcino在Tuscany
葡萄园在城镇中:Chateau Margaux在Margaux,Avignonesi在Montepulciano
5.2 建模决策
我们决定去掉“town”,将它们都看作region。这样做简化了模型,并且这附和实际中town作为一个产品地域在城镇周边,比城镇面积稍大或稍小的事实。
5.3 模型
5.3.1 Class
<owl:Class rdf:ID="&vin;ProductionArea"/ ><owl:Class rdf:ID="&vin;Country:"><rdfs:subClassOf rdf:resource="&vin;ProductionArea"/></owl:Class> <owl:Class rdf:ID="&vin;Region:"> <rdfs:subClassOf rdf:resource="&vin;ProductionArea"/> </owl:Class> <owl:Class rdf:ID="&vin;Vineyard:"> <rdfs:subClassOf rdf:resource="&vin;ProductionArea"/> </owl:Class>
用三元组可以表示为:
vin:ProductionArea rdf:type rdfs:Class.vin:Country rdfs:subClassOf vin:ProductionArea.vin:Region rdfs:subClassOf vin:ProductionArea.vin:Vineyard rdfs:subClassOf vin:ProductionArea.
5.3.2 Property
<owl:ObjectProperty rdf:ID="&vin;hasSubArea"> <rdf:type rdf:resource="&owl;TransitiveProperty" /></owl:ObjectProperty> <owl:ObjectProperty rdf:ID="&vin;subAreaOf"> <owl:inverseOf rdf:resource="&vin;hasSubArea"/> </owl:ObjectProperty> <owl:ObjectProperty rdf:ID="&vin;hasRegion"> <rdfs:subPropertyOf rdf:resource="&vin;hasSubArea"/> <owl:allValuesFrom rdf:resource="&vin;Region"/></owl:ObjectProperty> <owl:ObjectProperty rdf:ID="&vin;regionOf"> <owl:inverseOf rdf:resource="&vin;hasRegion"/> <owl:allValuesFrom rdf:resource="&vin;Country"/><owl:cardinality>1</owl:cardinality></owl:ObjectProperty> <owl:ObjectProperty rdf:ID="&vin;hasSubRegion"> <rdfs:subPropertyOf rdf:resource="&vin;hasSubArea"/> <owl:allValuesFrom rdf:resource="&vin;Region"/></owl:ObjectProperty> <owl:ObjectProperty rdf:ID="&vin;subRegionOf"> <owl:inverseOf rdf:resource="&vin;hasSubRegion"/> <owl:allValuesFrom rdf:resource="&vin;Region"/><owl:cardinality>1</owl:cardinality></owl:ObjectProperty> <owl:ObjectProperty rdf:ID="&vin;hasVineyard"> <rdfs:subPropertyOf rdf:resource="&vin;hasSubArea"/> <owl:allValuesFrom rdf:resource="&vin;Vinyard"/></owl:ObjectProperty> <owl:ObjectProperty rdf:ID="&vin;vineyardRegion"> <owl:inverseOf rdf:resource="&vin;hasVineyard"/> <owl:allValuesFrom rdf:resource="&vin;Region"/><owl:cardinality>1</owl:cardinality></owl:ObjectProperty>
用三元组表示为:
vin:hasSubArea rdf:type rdfs:Property. vin:hasSubArea rdf:type owl:TransitiveProperty. vin:subAreaOf owl:inverseOf vin:hasSubArea. vin:hasRegion rdfs:subPropertyOf vin:hasSubArea.vin:hasRegion owl:allValuesFrom vin:Region.vin:regionOf owl:inverseOf vin:hasRegion.vin:regionOf owl:allValuesFrom vin:Country.vin:regionOf owl:cardinality 1.vin:hasSubRegion rdfs:subPropertyOf vin:hasSubArea.vin:hasSubRegion owl:allValuesFrom vin:Region.vin:subRegionOf owl:inverseOf vin:hasSubRegion.vin:subRegionOf owl:allValuesFrom vin:Region.vin:subRegionOf owl:cardinality 1.vin:hasVineyard rdfs:subPropertyOf vin:hasSubArea.vin:hasVineyard owl:allValuesFrom vin:Vineyard.vin:vineyardRegion owl:inverseOf vin:hasVineyard..vin:vineyardRegion owl:allValuesFrom vin:Region.vin:vineyardRegion owl:cardinality 1.
5.3.3 UML注释
6 紧急联动的一个简单建模
6.1 UML模型(应急联动机构)
6.2 Ontology模型
<?xml version="1.0" encoding="GB2312"?><rdf:RDF xmlns="http://gis./应急联动机构#" xmlns: 应急联动机构="/应急联动机构#"xmlns:owl ="/2002/07/owl#"xmlns:rdf ="/1999/02/22-rdf-syntax-ns#"xmlns:rdfs="/2000/01/rdf-schema#"xmlns:xsd =/2000/10/XMLSchema#><owl:Ontology rdf:about=" http://gis./应急联动机构"> <rdfs:comment>中国城市应急联动机构的Ontology</rdfs:comment><owl:versionInfo>$Id: test.html,v 1.0 /03/20 22:22:00 Lxp $</owl:versionInfo></owl:Ontology><owl:Class rdf:ID="&应急联动机构;联动单位"/ ><owl:Class rdf:ID="&应急联动机构;消防局:"><rdfs:subClassOf rdf:resource="&应急联动机构;联动单位"/></owl:Class><owl:Class rdf:ID="&应急联动机构;联动单位调度中心"/ ><owl:Class rdf:ID="&应急联动机构;处置力量分支"/ ><owl:Class rdf:ID="&应急联动机构;消防指挥调度中心:"><rdfs:subClassOf rdf:resource="&应急联动机构;联动单位调度中心"/></owl:Class><owl:Class rdf:ID="&应急联动机构;消防支队:"><rdfs:subClassOf rdf:resource="&应急联动机构;处置力量分支"/></owl:Class><owl:Class rdf:ID="&应急联动机构;处置力量部门"/ ><owl:Class rdf:ID="&应急联动机构;消防中队:"><rdfs:subClassOf rdf:resource="&应急联动机构; 处置力量部门"/></owl:Class><owl:Class rdf:ID="&应急联动机构;消防车"/ ><owl:ObjectProperty rdf:ID="&应急联动机构;包含中心"> <rdf:type rdf:resource="&owl;FunctionalProperty" /></owl:ObjectProperty><owl:ObjectProperty rdf:ID="&应急联动机构;包含分支"> </owl:ObjectProperty><owl:ObjectProperty rdf:ID="&应急联动机构;包含联动调度中心"> <rdfs:subPropertyOf rdf:resource="&应急联动机构;包含中心"/> <rdfs:domain rdf:resource="&应急联动机构;联动单位" /><rdfs:range rdf:resource="&应急联动机构;联动单位调度中心" /></owl:ObjectProperty><owl:ObjectProperty rdf:ID="&应急联动机构;包含力量分支"> <rdfs:subPropertyOf rdf:resource="&应急联动机构;包含分支"/> <rdfs:domain rdf:resource="&应急联动机构;联动单位" /><rdfs:range rdf:resource="&应急联动机构;处置力量分支" /></owl:ObjectProperty><owl:ObjectProperty rdf:ID="&应急联动机构;包含力量部门"> <rdfs:subPropertyOf rdf:resource="&应急联动机构;包含分支"/> <owl:allValuesFrom rdf:resource="&应急联动机构;处置力量部门"/></owl:ObjectProperty><owl:ObjectProperty rdf:ID="&应急联动机构;包含消防调度中心"> <rdfs:subPropertyOf rdf:resource="&应急联动机构;包含中心"/> <rdfs:domain rdf:resource="&应急联动机构;消防局" /><rdfs:range rdf:resource="&应急联动机构;消防指挥调度中心" /></owl:ObjectProperty><owl:ObjectProperty rdf:ID="&应急联动机构;包含消防支队"> <rdfs:subPropertyOf rdf:resource="&应急联动机构;包含分支"/> <rdfs:domain rdf:resource="&应急联动机构;消防局" /><rdfs:range rdf:resource="&应急联动机构;消防支队" /></owl:ObjectProperty><owl:ObjectProperty rdf:ID="&应急联动机构;包含中队"> <rdfs:subPropertyOf rdf:resource="&应急联动机构;包含分支"/> <owl:allValuesFrom rdf:resource="&应急联动机构;消防中队"/></owl:ObjectProperty><owl:ObjectProperty rdf:ID="&应急联动机构;包含车"> <rdfs:subPropertyOf rdf:resource="&应急联动机构;包含分支"/> <rdfs:domain rdf:resource="&应急联动机构;消防中队" /><rdfs:range rdf:resource="&应急联动机构;消防车" /></owl:ObjectProperty></rdf:RDF>
7 一个比较复杂的应急联动的建模
略
8 Jena—语义网建模的Java API
8.1 简介——几个RDF建模的实例
【例一】: 给出下面的RDF有向图:
其中,椭圆中的URI给出了一个resource,有向边给出了该resource的一个property:FN(Full Name)。vcard是一个命名空间的前缀。该property的值在方框中给出,是一个literral类型的“John Smith”。
Jena一组能够用来创建和操纵诸如此种类型的有向图的Java API。Jena用对象类来表示图、resource、property和literal,用于表达后三者的接口分别叫做Resource、Property和Literal,而图被表示为一个model。用于创建上面的图的代码如下:
// some definitionsstatic String personURI = "http://somewhere/JohnSmith";static String fullName= "John Smith";// create an empty graphModel model = new ModelMem();// create the resource Resource johnSmith = model.createResource(personURI);// add the propertyjohnSmith.addProperty(VCARD.FN, fullName);
ModelMem是实现了接口Model的类。Jena还包括了其他用于实现Model的类,它们可以被存储在Berklay的DB数据库以及关系数据库中。VCARD是一个常数类,里面定义了所有VCARD Schema的常数。Jena还提供了其他常数类,如RDF、RDF Schema、Dublin Core 和DAML。其中,创建resource和添加property的两条语句可以被压缩为下面的一句:
Resource johnSmith = model.createResource(personURI).addProperty(VCARD.FN, fullName);
【例二】:下面给出了在例一的基础上得到的一个相对复杂的有向图:
与上面不同的是,vcard:N属性以一个resource作为它的值,而该resource没有名字,称作一个空结点(blank Node)。
下面是Jena的创建代码:
// some definitionsString personURI = "http://somewhere/JohnSmith";String givenName = "John";String familyName = "Smith";String fullName= givenName + " " + familyName;// create an empty graphModel model = new ModelMem();// create the resource// and add the properties cascading styleResource johnSmith = model.createResource(personURI).addProperty(VCARD.FN, fullName).addProperty(VCARD.N, model.createResource().addProperty(VCARD.Given, givenName).addProperty(VCARD.Family, familyName));
8.2 Jena与整个建模流程
8.2.1 陈述(statement)
如上面的两个例子所示,RDF有向图中的每条边就是一个陈述,它肯定了resource的一个事实。一个陈述包含三个部分:
subject,即有向边离开的resource;主语
predicate,即带标记的有向边;谓词
object,有向边指向的resource或者literal。宾语
所以陈述又叫做三元组。一个RDF有向图包含了一系列的陈述(statement),他是陈述的集合,所以重复的陈述可以被加进一个图中。Jena的model接口提供了一个listStatements()方法用于得到一个在这个集合上的迭代器,它的返回类型为Statement。Statement接口提供了用于访问该陈述的subject、predicate和object的方法。一个例子如下:
// list the statements in the graphStmtIterator iter = model.listStatements(); // print out the predicate, subject and object of each statementwhile (iter.hasNext()) {Statement stmt= iter.next(); // get next statementResource subject = stmt.getSubject(); // get the subjectProperty predicate = stmt.getPredicate(); // get the predicateRDFNode object = stmt.getObject(); // get the objectSystem.out.print(subject.toString());System.out.print(" " + predicate.toString() + " ");if (object instanceof Resource) {System.out.print(object.toString());} else {// object is a literalSystem.out.print(" \"" + object.toString() + "\");}System.out.println(" .");}
由于object可以为一个resource或者一个literal,所以getObject()
方法返回一个类型为RDFNode的对象,该类是resource和literal的公共超类。
8.2.2 书写RDF
Jena提供了用XML格式读写RDF的方法,这就使得我们能够将一个RDF model存为一个文件并在以后将它读出来。
下面的代码片断给出了如何将一个model存入文件的方法:
// now write the model in XML form to a filemodel.write(new PrintWriter(System.out));
即调用model的write方法通过一个PrintWriter
写入文件中。
对于上面的例子,调用这样的输入语句可能得到下面的输出结果:
<rdf:RDFxmlns:rdf='/1999/02/22-rdf-syntax-ns#'xmlns:vcard='/2001/vcard-rdf/3.0#'><rdf:Description rdf:about='http://somewhere/JohnSmith'><vcard:FN>John Smith</vcard:FN><vcard:N rdf:resource='#A0'/></rdf:Description><rdf:Description rdf:about='#A0'><vcard:Given>John</vcard:Given><vcard:Family>Smith</vcard:Family></rdf:Description></rdf:RDF>
不过该表达并不完全与我们上面给出的例子相符。在XML表达中不能描述一个空的节点,在原图中的空结点被赋予了一个URI。
Jena还提供了扩展接口用于支持可插入的新的writer来序列化RDF。上面使用的是标准的“dumb”writer,Jena还支持一个更为成熟的RDF/XML writer:
model.write(new PrintWriter(System.out), "RDF/XML-ABBREV");// now write the model in XML form to a file
这个writer也叫做PrettyWriter
,它能够使用RDF/XML feature
的缩写语法更为紧凑的将一张图输出到文件。它同时也支持空结点。但是对于很大的图则不太适用,因为这样做的性能不能接收。要想输出很大的文件并且维持空结点,则使用N-Triples
格式:
// now write the model in XML form to a filemodel.write(new PrintWriter(System.out), "N-TRIPLE");
8.2.3 读取RDF
下面的代码给出了用于读取一个RDF文件并且将它另外写回的方法:
// create an empty modelModel model = new ModelMem(); // use the class loader to find the input fileInputStream in = Tutorial05.class.getClassLoader().getResourceAsStream(inputFileName);if (in == null) {throw new IllegalArgumentException("File: " + inputFileName + " not found");}// read the RDF/XML filemodel.read(new InputStreamReader(in), "");// write it to standard outmodel.write(new PrintWriter(System.out));
其中,read方法的第二个参数应该是相对的URI,因为这里没有引用相对URI,所以可以为空。
8.2.4 关于Jena的RDF包
最新版本已不再使用使用
com.hp.hpl.mesa
命名空间
l 语义网应用开发人员使用Jena时的最重要的包是com.hp.hpl.mesa.rdf.jena.model
。这个包包含了用于表达model、resource、property、literal、statements以及其他RDF的关键的接口;
lcom.hp.hpl.mesa.rdf.jena.tutorial
包包含了与Jena一同发布的指南中所使用的例子的源代码;
lcom.hp.hpl.mesa.rdf.jena.mem
包包含了用于将整个模型状态装入内存的Jena API的实现。凡是创建基于内存的模型(最典型的是创建ModelMem类的实例)的实现都在本包中;
lcom.hp.hpl.mesa.mon
包包含了对于诸多实现通用的实现类。例如,它定义了类ResourceImpl, PropertyImpl, LiteralImpl。开发者一般不要直接使用这里的方法,例如不直接使用ResourceImpl,而使用createResource方法。这是为了保证在实现发生优化后不需要进行类型转换;
lcom.hp.hpl.jena.rdf
为RDF包。
8.2.5 在RDF有向图中的导航
给出一个resource的URL,可以使用Model.getResource(String uri)方法从图中得到该资源所对应的对象。如果对应的resource存在,则返回该resource的对象,否则将创建一个新的对象。例如:
Resource vcard = model.getResource(johnSmithURI);// retrieve the John Smith vcard resource from the model
Resource接口定义了一系列方法用于访问一个Resource的Property。Resource.getProperty(Property p)方法用于访问一个resource的一个property。同以往的Java API不同的是该方法返回的是整个Statement对象,然后通过该对象的访问方法来得到Property的值。例如想得到vcard:N这个property的值所对应的resource,代码如下:
Resource name = (Resource) vcard.getProperty(VCARD.N).getObject();// retrieve the value of the N property
由于property的值可能为resource或者literal,而我们已经知道上面得到的值为resource,所以进行了强制类型转换。Jena也提供了特定类型的访问方法,从而不需要在编译时进行类型转换。所以上面的代码也可以写成:
Resource name = vcard.getProperty(VCARD.N).getResource();// retrieve the value of the FN property
类似,如果property的值为literal,则可以写成:
String fullName = vcard.getProperty(VCARD.FN) .getString();// retrieve the given name property
上面的property VCARD.FN只有一个,但RDF允许一个resource有重复的property。例如Adam可能有多于一个的nickname:
// add two nick name properties to vcardvcard.addProperty(VCARD.NICKNAME, "Smithy").addProperty(VCARD.NICKNAME, "Adman");
当调用getProperty方法时,Jena并没有定义要返回哪一个property,所以vcard.getProperty(VCARD.NICKNAME)的结果是不确定的。Jena只是返回其中任意一个,但并不保证连续两条调用都有可能返回同一个值。
这样,当proeprty连续出现时,可以使用Resource.listProperties(Property p)方法来得到一个迭代器从而列举出它们。例如下面的代码能够列举出上面添加的nickname:
// set up the outputSystem.out.println("The nicknames of \""+ fullName + "\" are:");// list the nicknamesStmtIterator iter = vcard.listProperties(VCARD.NICKNAME);while (iter.hasNext()) {System.out.println(" " + iter.next().getObject().toString());}
该代码的输出结果是:
The nicknames of "John Smith" are:SmithyAdman
8.2.6 关于图的查询
Jena核心只是提供了有限的查询元语。另外对RDF还有更为强大的RDQL查询语言。
Model.listStatements()
方法用于列举一个model中的所有statement,可能是最为原始的对一个model的查询。该查询不适于在很大的图上面做查询。Model.listSubjects()
方法类似,只是返回在所有有property的resource上的迭代器。Model.listSubjectsWithProperty(Property p, RDFNode o)
则返回所有在property p上有值o的resource的迭代器。例如:
// retrieve all resource of type Vcard.ResIterator iter = model.listSubjectsWithProperty(RDF.type, VCARD.Vcard);
如果我们使用的vcard schema并没有为vcard定义一个类型,那我们可以假定只有类型为vcard的resource有property vcard:FN,而且在我们的数据中,所有这样的resource都有一个这样的property,那我们可以这样进行查询:
// list vcardsResIterator iter = model.listSubjectsWithProperty(VCARD.FN);while (iter.hasNext()) {Resource r = iter.next();...}
所有上面的查询都基于这样一个查询元语:model.listStatements(Selector s)
。该方法返回建立在经过s选择得到的statement上的迭代器。Selector被定义为可扩展的,目前只有一个实现:com.hp.hpl.mesa.mon
包中的SelectorImpl类。SelectorImpl构造函数有三个参数:
Selector selector = new SelectorImpl(subject, predicate, object)
显然它返回匹配参数给出的三元组的statement。如果在这三个参数的位置上任意一个为null,则认为匹配所有。所以
Selector selector = new SelectorImpl(null, null, null);
返回一张图中的所有statement。
Selector selector = new SelectorImpl(null, VCARD.FN, null);
返回满足predicate为VCARD.FN
的statement而不论其他两个参数的值为什么。以下的代码列出了数据库中所有vcard的full name:
// select all the resources with a VCARD.FN propertyResIterator iter = model.listSubjectsWithProperty(VCARD.FN);if (iter.hasNext()) {System.out.println("The database contains vcards for:");while (iter.hasNext()) {System.out.println(" " + iter.next().getProperty(VCARD.FN).getString());}} else {System.out.println("No vcards were found in the database");}
输出结果可以是:
The database contains vcards for:Sarah JonesJohn SmithMatt JonesBecky Smith
下面的例子是采用SelectorImpl来实现查询的例子:
// select all the resources with a VCARD.FN property// whose value ends with "Smith"StmtIterator iter = model.listStatements(new SelectorImpl(null, VCARD.FN, (RDFNode) null) {public boolean selects(Statement s) {try {return s.getString().endsWith("Smith");} catch (RDFException e) {throw new RDFError(e);}}});
以上的代码使用了Java的内置代理方法定义技术。其中select方法确保full name以“Smith”结束,而该过滤只是对subject起作用。
以下的两段代码可被认为是有着相同的功能:
//【1】// do all filtering in the selects methodStmtIterator iter = model.listStatements(new SelectorImpl(null, null, (RDFNode) null) {public boolean selects(Statement s) {try {return (subject == null || s.getSubject().equals(subject))&& (predicate == null || s.getPredicate().equals(predicate))&& (object == null || s.getObject().equals(object))} catch (RDFException e) {throw new RDFError(e);}}});//【2】StmtIterator iter = model.listStatements(new SelectorImpl(subject, predicate, object)
前者列出图中所有的statement而后依次测试之,而后者允许使用应用实现本身维护的索引来提高性能。
8.2.7 对图的操作
Jena提供了3种针对图这一整体进行的操作——即典型的集合操作:并(union)、叫(intersection)和差(different)。
对两张图取并就是对两张图所包含的statement取并,这是为了支持RDF所给出的一个关键操作,它使得不同数据源的数据能够被合并。给出下面的两张图:
它们可以被合并为:
上面的操作的代码是:
// read the RDF/XML filesmodel1.read(new InputStreamReader(in1), "");model2.read(new InputStreamReader(in2), "");// merge the graphsModel model = model1.union(model2); // print the graph as RDF/XMLmodel.write(new PrintWriter(System.out), "RDF/XML-ABBREV");
生成的RDF为:
<?xml version='1.0'?><rdf:RDFxmlns:rdf='/1999/02/22-rdf-syntax-ns#'xmlns:RDFNsId1='/2001/vcard-rdf/3.0#'><rdf:Description rdf:about='http://somewhere/JohnSmith/'><RDFNsId1:NRDFNsId1:Given='John'RDFNsId1:Family='Smith'/><RDFNsId1:FN>John Smith</RDFNsId1:FN><RDFNsId1:EMAILrdf:value='John@'rdf:type='/2001/vcard-rdf/3.0#internet'/></rdf:Description></rdf:RDF>
交和差操作与此类似。
8.2.8 相关异常
目前Jena的异常机制不很受欢迎,它将在将来得到改进。由于Jena被设计成具有很高的灵活性并且支持不同的存储系统,所以存储管理器可以在任何时候给出一个非预期的错误。所以几乎所有的Jena方法在结尾都要注明要抛出RDFException异常。经验证明这不是一个好方法,当抛出异常时,通常这些异常都应该被忽略,检查出这些异常没有任何好处。
8.2.9 容器(Containers)
RDF给出了表达事物集合的特殊类型的resource。这些resource叫做容器。容器的成员或者是resource,或者是literal。共有3类容器:
BAG是一个无序的容器;ALT是一个用于表达选择的无序的容器;SEQ是一个有序的集合。
下图是一个含有BAG的RDF有向图的示意:
注意BAG的成员标号rdf:_1
和rdf:_2
的顺序并不重要,它们可以被交换。而ALT的成员标号除了第一个是重要的(它是缺省的选择)外,也是顺序无关的。
Jena提供了明确的类接口和类实现用于表达容器,例如:
// create a bagBag smiths = model.createBag();// select all the resources with a VCARD.FN property// whose value ends with "Smith"StmtIterator iter = model.listStatements(new SelectorImpl(null, VCARD.FN, (RDFNode) null) {public boolean selects(Statement s) {try {return s.getString().endsWith("Smith");} catch (RDFException e) {throw new RDFError(e);}}});// add the Smith's to the bagwhile (iter.hasNext()) {smiths.add(iter.next().getSubject());}
由它得到的RDF为:
<rdf:RDFxmlns:rdf='/1999/02/22-rdf-syntax-ns#'xmlns:vcard='/2001/vcard-rdf/3.0#' >...<rdf:Description rdf:about='#A3'><rdf:type rdf:resource='/1999/02/22-rdf-syntax-ns#Bag'/><rdf:_1 rdf:resource='http://somewhere/JohnSmith/'/><rdf:_2 rdf:resource='http://somewhere/RebeccaSmith/'/></rdf:Description></rdf:RDF>
容器接口提供了一个迭代器用于列举它的成员:
// print out the members of the bagNodeIterator iter2 = smiths.iterator();if (iter2.hasNext()) {System.out.println("The bag contains:");while (iter2.hasNext()) {System.out.println(" " +(Resource) iter2.next()).getProperty(VCARD.FN).getString());}} else {System.out.println("The bag is empty");}
上面的输出为:
The bag contains:John SmithBecky Smith
Jena类提供的用于操纵容器的方法包括:添加新成员、插入成员到容器的成员中部、删除成员等。
8.2.10 有关Literal和DataType的细节
RDF的literal不是简单的string。它可以包含一个语种标签来说明该literal的语种。语种标签为English的Literal“chat”和语种标签为“French”的Literal “chat”被认为是不同的literal。
进一步说,一共有两类literal:一种是普通的string,一种是一个定义良好的XML片断。例如下面的代码:
// create the resourceResource r = model.createResource(); // add the propertyr.addProperty(RDFS.label, model.createLiteral("chat", "en")).addProperty(RDFS.label, model.createLiteral("chat", "fr")).addProperty(RDFS.label, model.createLiteral("<em>chat</em>", true));// write out the graphmodel.write(new PrintWriter(System.out));
它产生的RDF为:
<rdf:RDFxmlns:rdf='/1999/02/22-rdf-syntax-ns#'xmlns:rdfs='/2000/01/rdf-schema#'><rdf:Description rdf:about='#A0'><rdfs:label xml:lang='en'>chat</rdfs:label><rdfs:label xml:lang='fr'>chat</rdfs:label><rdfs:label xml:lang='en' rdf:parseType='Literal'><em>chat</em></rdfs:label></rdf:Description></rdf:RDF>
两个literal可以被看作相等的条件是:或者都是简单的literal,或者都是XML literal。而且要么都没有语种标签,要么有的话标签相同。