以文本方式查看主题

-  W3CHINA.ORG讨论区 - 语义网·描述逻辑·本体·RDF·OWL  (http://bbs.xml.org.cn/index.asp)
--  『 Semantic Web(语义Web)/描述逻辑/本体 』  (http://bbs.xml.org.cn/list.asp?boardid=2)
----  Jena文档《An Introduction to RDF and the Jena RDF API》的译文  (http://bbs.xml.org.cn/dispbbs.asp?boardid=2&rootid=&id=20595)


--  作者:april1019
--  发布时间:7/22/2005 10:35:00 AM

--  Jena文档《An Introduction to RDF and the Jena RDF API》的译文
RDF和Jena RDF API入门
________________________________________
前言
本文是一篇对W3C的资源描述框架(RDF)和 Jena(一个Java的RDF API)的教程性介绍. 本文是为那些不熟悉RDF的, 以及那些通过建立原形可以达到最好学习效果的, 或是因为其他原因希望能快速操作Jena的程序员而写的. 我们假设读者在阅读本文前已具有一定的XML和Java知识.
如果读者在没有理解RDF数据模型的基础上就迅速进入操作阶段,往往会导致失败和失望. 然而,如果光学习数据模型又是十分枯燥乏味的, 并常常会导致曲折的形而上学的难题. 更好的学习办法是在理解数据模型的同时练习操作它. 可以先学习一点数据模型再动手试一试.然后在学习一点再试一试. 这样一来就能达到理论实践相结合的效果.数据模型本身十分简单,所以学习过程不会太长.
RDF具有XML的语法, 所以许多熟悉XML的人就会认为以XML语法的形式来思考RDF. 然而, 这是不对的. RDF应该以它数据模型的形式来被理解. RDF数据可是用XML来表示, 但是理解数据模型的重要性更在理解此语法重要性之上.
Jena API的一个运行例子, 包括本教程中所有例子的工作源代码都可以在http://www.hpl.hp.com/semweb/下载.
________________________________________
目录
1. 导言
2. 陈述Statements
3. RDF写操作
4. RDF读操作
5. Jena RDF 包
6. 操纵模型
7. 查询模型
8. 对模型的操作
9. 容器Containers
10. 关于Literals和数据类型的更多探讨
11. 术语表
________________________________________
导言
资源描述框架是(RDF)是描述资源的一项标准(在技术上是W3C的推荐标准). 什么是资源? 这实在是一个很难回答的问题, 其精确的定义目前尚在争论中. 出于我们的目的, 我们可以把资源想象成任何我们可以确定识别的东西. 在本教程中,读者你本身就是一个资源, 而你的主页也是一个资源, 数字1和故事中巨大的白鲸都是资源.
在本教程中, 我们的例子会围绕人们展开. 假设人们会使用VCARDS, 而VCARD将由RDF表示机制来描述. 我们最好把RDF考虑成由结点和箭头的形式构成的图. 一个简单的vcard在RDF中可能看起来是这样的:

资源John Smith在图中用椭圆表示, 并被一个统一资源定位符(URI) 所标识, 在本例中是"http://.../JohnSmith"). 如果你想要通过你的浏览器来访问这个资源的话,你很有可能会失败. 四月的愚人节笑话并不经得起考验, 相反如果你的浏览器把John Smith传递到你的桌面的话, 你才该感到惊讶. 如果你并不熟悉URI's的话, 你可以把它们想象成简单的陌生名字.
资源拥有属性(property). 在这些例子中, 我们对John Smith名片上出现的那些属性很感兴趣.图1只显示了一个属性, John Smith的全名. 属性是由标有属性名的箭头表示的. 属性的名字也是一个URI, 但是由于URI十分冗长笨重, 所以图中将它显示为XML qname的形式. 在':'之前的部分称为命名空间前缀并表示了一个命名空间. 在':'之后的部分称为局部名, 并表示在命名空间中的一个名字. 在写成RDF XML形式时, 属性常常以qname的形式表示, 这是一个在图形和文本中的简单的缩写方法. 然而, 严格地讲, 属性应该用URI来标识. 命名空间前缀:局部名的形式是一种命名空间连接局部名的URI缩写. 当浏览器访问时, 用并没有强制属性的URI必须指向一些具体的事物.
每个属性都有一个值. 在此例中, 值为一个文本(literal), 我们现在可以把它看成一个字符串.文本在图中显示为长方形.
Jena是一个Java API, 我们可以用它来创建和操纵诸如上述例图的RDF图. Jena设有表示图(graph), 资源(resource), 属性和文本(literal)的对象类. 表示资源, 属性和文本的接口分别称为Resource, Property, 和Literal. 在Jena中, 一个图(graph)被称为一个模型并被Model接口所表示.
创建上述例图或称为上述模型的代码很简单:
// some definitions
static String personURI    = "http://somewhere/JohnSmith";
static String fullName     = "John Smith";

// create an empty Model
Model model = ModelFactory.createDefaultModel();

// create the resource
Resource johnSmith = model.createResource(personURI);

// add the property
johnSmith.addProperty(VCARD.FN, fullName);

这些代码先定义了一些常量, 然后使用了ModelFactory类中的createDefaultMode()方法创建了一个空的基于内存存储的模型(Model 或 model). Jena还包含了Model接口的其他实现方式. 例如, 使用关系数据库的, 这些类型 Model接口也可以从ModelFactory中创建.
于是John Smith这个资源就被创建了, 并向其添加了一个属性. 此属性由一个"常" ("constant")类VCARD提供, 这个类保存了在VCARD模式(schema)中所有定义的表示对象. Jena也为其他一些著名的模式提供了常类的表示方法, 例如是RDF和RDF模式, Dublin 核心标准和DAML.
创建资源和添加属性的代码可以写成更紧凑的层叠形式:
Resource johnSmith =
        model.createResource(personURI)
             .addProperty(VCARD.FN, fullName);
这个例子的工作代码可以在Jena发布的材料的教程包中的Tutorial1中找到. 作为练习, 你自己可以获得此代码并修改其以创建一个简单VCARD.
现在让我们为vcard再增加一些更详细的内容, 以便探索更多的RDF和Jena的特性.
在第一个例子里, 属性值为一个文本. 然而RDF属性也可以采用其他的资源作为其属性值. 下面这个例子使用常用的RDF技术展示了如何表示John Smith名字的不同部分:

在这里我们增加了一个新的属性, vcard:N, 来表示John Smith名字的结构. 这个模型有几点有趣之处. 注意属性vcard:N使用一个资源作为起属性值. 同时注意代表复合名字的椭圆并没有URI标识. 它被认为是一个空白结点(blank Node).
创建此例的Jena代码也十分简单. 首先是一些声明和对空模型的创建.
// some definitions
String personURI    = "http://somewhere/JohnSmith";
String givenName    = "John";
String familyName   = "Smith";
String fullName     = givenName + " " + familyName;

// create an empty Model
Model model = ModelFactory.createDefaultModel();

// create the resource
//   and add the properties cascading style
Resource johnSmith
  = model.createResource(personURI)
         .addProperty(VCARD.FN, fullName)
         .addProperty(VCARD.N,
                      model.createResource()
                           .addProperty(VCARD.Given, givenName)
                           .addProperty(VCARD.Family, familyName));
此例的工作代码可以在Jena发布材料的教程包的Tutorial2中得到.
________________________________________
陈述
RDF模型中的每一个箭头表示为一个陈述(statement). 每一个陈述声明了关于某个资源的某个事实. 一个陈述有三部分组成.
主体, 也就是箭头的出发的资源.
谓词, 也就是标识箭头的属性.
客体, 也就是箭头所指向的那个资源或文本.
一个陈述有时也叫做一个三元组的原因就是它由三部分组成.
一个RDF模型(译者注: 指Jena中的接口Model)是由一组陈述所组成的. 在Tutorial2中, 每调用一次addProperty函数就会在模型中增加另一个陈述. (因为一个模型是由一组陈述组成的, 所以增加一个重复的陈述并不会产生任何意义.) Jena模型接口定义了一个listStatements()方法, 此方法会返回一个StmtIterator类型的变量. StmtItor是Java中Iterator的一个子类型, 这个StmtIterator变量重复迭代了该接口模型中的所有陈述. StmtIterator类型中有一个方法nextStatement(), 该方法会从iterator返回下一个陈述. (就和next()返回的一样, 但是已将其映射为Statement类型). 接口Statement提供了访问陈述中主体, 谓词和客体的方法.
现在我们会用使用那个接口来扩展Tutorial2, 使起列出所有的创建的陈述并将它们打印出来. 此例完整的代码可以在Tutorial3中找到.
// list the statements in the Model
StmtIterator iter = model.listStatements();

// print out the predicate, subject and object of each statement
while (iter.hasNext()) {
    Statement stmt      = iter.nextStatement();  // get next statement
    Resource  subject   = stmt.getSubject();     // get the subject
    Property  predicate = stmt.getPredicate();   // get the predicate
    RDFNode   object    = stmt.getObject();      // get the object

    System.out.print(subject.toString());
    System.out.print(" " + predicate.toString() + " ");
    if (object instanceof Resource) {
       System.out.print(object.toString());
    } else {
        // object is a literal
        System.out.print(" \"" + object.toString() + "\"");
    }

    System.out.println(" .");
}
因为一个陈述的客体可以是一个资源也可以是一个文本. getObject()方法会返回一个类型为RDFNode的客体, RDFNode是Resource和Literal类共同的超类. 为了确定本例中的客体确切的类型, 代码中使用 instanceof来确定其类型和相应的处理.
运行后, 此程序回产生与此相似的输出:
http://somewhere/JohnSmith http://www.w3.org/2001/vcard-rdf/3.0#N anon:14df86:ecc3dee17b:-7fff.
anon:14df86:ecc3dee17b:-7fff http://www.w3.org/2001/vcard-rdf/3.0#Family  "Smith".
anon:14df86:ecc3dee17b:-7fff http://www.w3.org/2001/vcard-rdf/3.0#Given  "John" .
http://somewhere/JohnSmith http://www.w3.org/2001/vcard-rdf/3.0#FN  "John Smith".

现在你明白了为什么模型构建会更加清晰. 如果你仔细观察, 就会发现上面每一行都由三个域组成, 这三个域分别代表了每一个陈述的主体, 谓词和客体. 在此模型中有四个箭头, 所以会有四个陈述. "anon:14df86:ecc3dee17b:-7fff"是有Jena产生的一个内部标识符, 它不是一个URI, 也不应该与URI混淆. 它只是Jena处理时使用的一个内部标号.
W3C的RDF核心工作小组定义了一个类似的表示符号称为N-三元组(N-Triples). 这个名字表示会使用"三元组符号". 在下一节中我们会看到Jena有一个内置的N-三元组写机制(writer).
________________________________________
写RDF
Jena设有读写XML形式的RDF方法. 这些方法可以被用来将一个RDF模型保存到文件并在日后重新将其读回.
Tutorial3创建了一个模型并将其以三元组的形式输出. Tutorial4对Tutorial3做了修改, 使其将此模型以RDF XML的形式输出到标准输出流中. 这个代码依然十分简单: model.write可以带一个OutputStream的参数.
// now write the model in XML form to a file
model.write(System.out);

应该有类似的输出:
<rdf:RDF
  xmlns:rdf='http://www.w3.org/1999/02/22-rdf-syntax-ns#'
  xmlns:vcard='http://www.w3.org/2001/vcard-rdf/3.0#'
>
  <rdf:Description rdf:about='http://somewhere/JohnSmith'>
    <vcard:FN>John Smith</vcard:FN>
    <vcard:N rdf:nodeID="A0"/>
  </rdf:Description>
  <rdf:Description rdf:nodeID="A0">
    <vcard:Given>John</vcard:Given>
    <vcard:Family>Smith</vcard:Family>
  </rdf:Description>
</rdf:RDF>

W3C的RDF规格说明书规定了如何用 XML的形式来表示RDF. RDF XML的语法十分复杂. 读者可以在RDF核心工作小组制定的RDF入门篇(primer)中找到更详细的指导. 但是不管怎么样, 让我们先迅速看一下应该如何解释上面的RDF XML输出
RDF常常嵌入在一个<rdf:RDF>元素中. 如果有其他的方法知道此XML是RDF的话,该元素是可以不写的. 然而我们常常会使用它. 在这个RDF元素中定义了两个在本文档中使用的命名空间. 接下来是一个<rdf:Description>元素, 此元素描述了URI为"http://somewhere/JohnSmith"的资源. 如果其中的rdf:about属性被省略的话, 这个元素就表示一个空白结点.
<vcard:FN>元素描述了此资源的一个属性. 属性的名字"FN"是属于vcard命名空间的. RDF会通过连接命名空间前缀的URI和名字局部名"FN"来形成该资源的URI "http://www.w3.org/2001/vcard-rdf/3.0#FN". 这个属性的值为文本"John Smith".
<vcard:N>元素是一个资源. 在此例中, 这个资源是用一个相对URI来表示的. RDF会通过连接这个相对URI和此文档的基准URI来把它转换为一个绝对URI.
但是, 在这个RDF XML输出中有一个错误, 它并没有准确地表示我们所创建的模型. 模型中的空白结点被分配了一个URI. 它不再是空白的了. RDF/XML语法并不能表示所有的RDF模型. 例如它不能表示一个同时是两个陈述的客体的空白结点. 我们用来写这个RDF/XML的'哑'writer方法并没有试图去正确的书写这个模型的子集, 虽然其原本可以被正确书写. 它给每一个空白结点一个URI, 使其不再空白.
Jena有一个扩展的接口, 它允许新的为不同的RDF串行化语言设计的writer可以被轻易地插入. 以上的调用会激发一个标准的'哑'writer方法. Jena也包含了一个更加复杂的RDF/XML writer, 它可以被用携带另一个参数的write()方法所调用.
// now write the model in XML form to a file
model.write(System.out, "RDF/XML-ABBREV");

此writer, 也就是所谓的PrettyWriter, 利用RDF/XML缩写语法把模型写地更为紧凑. 它也能保存尽可能保留空白结点. 然而, 它并不合适来输出大的模型. 因为它的性能不可能被人们所接受. 要输出大的文件和保留空白结点, 可以用N-三元组的形式输出:
// now write the model in XML form to a file
model.write(System.out, "N-TRIPLE");

这会产生类似于Tutorial3的输出, 此输出会遵循N-三元组的规格.

________________________________________
读RDF
Tutorial 5 演示了如何将用RDF XML记录的陈述读入一个模型. 在此例中, 我们提供了一个小型RDF/XML形式的vcard的数据库. 下面代码会将其读入和写出. 注意: 如果要运行这个小程序, 应该把输入文件放在你的classpath所指向的目录或jar中.

// create an empty model
Model model = ModelFactory.createDefaultModel();

// use the class loader to find the input file
InputStream in = Tutorial05.class
                               .getClassLoader()
                               .getResourceAsStream(inputFileName);
if (in == null) {
    throw new IllegalArgumentException(
                                 "File: " + inputFileName + " not found");
}

// read the RDF/XML file
model.read(new InputStreamReader(in), "");

// write it to standard out
model.write(System.out);
      
read()方法中的第二个参数是一个URI, 它是被用来解决相对URI的. 因为在测试文件中没有使用相对URI, 所以它允许被置为空值. 运行时, Tutorial5会产生类似如下的XML输出
<rdf:RDF
  xmlns:rdf='http://www.w3.org/1999/02/22-rdf-syntax-ns#'
  xmlns:vcard='http://www.w3.org/2001/vcard-rdf/3.0#'
>
  <rdf:Description rdf:nodeID="A0">
    <vcard:Family>Smith</vcard:Family>
    <vcard:Given>John</vcard:Given>
  </rdf:Description>
  <rdf:Description rdf:about='http://somewhere/JohnSmith/'>
    <vcard:FN>John Smith</vcard:FN>
    <vcard:N rdf:nodeID="A0"/>
  </rdf:Description>
  <rdf:Description rdf:about='http://somewhere/SarahJones/'>
    <vcard:FN>Sarah Jones</vcard:FN>
    <vcard:N rdf:nodeID="A1"/>
  </rdf:Description>
  <rdf:Description rdf:about='http://somewhere/MattJones/'>
    <vcard:FN>Matt Jones</vcard:FN>
    <vcard:N rdf:nodeID="A2"/>
  </rdf:Description>
  <rdf:Description rdf:nodeID="A3">
    <vcard:Family>Smith</vcard:Family>
    <vcard:Given>Rebecca</vcard:Given>
  </rdf:Description>
  <rdf:Description rdf:nodeID="A1">
    <vcard:Family>Jones</vcard:Family>
    <vcard:Given>Sarah</vcard:Given>
  </rdf:Description>
  <rdf:Description rdf:nodeID="A2">
    <vcard:Family>Jones</vcard:Family>
    <vcard:Given>Matthew</vcard:Given>
  </rdf:Description>
  <rdf:Description rdf:about='http://somewhere/RebeccaSmith/'>
    <vcard:FN>Becky Smith</vcard:FN>
    <vcard:N rdf:nodeID="A3"/>
  </rdf:Description>
</rdf:RDF>

________________________________________
Jena RDF 包
Jena是一个为语义网应用设计的一个Java API. 对应用开发者而言, 主要可用的RDF包是com.hp.hpl.jena.rdf.model. 因为API是以接口的方式定义的, 所以应用代码可以使用不同的实现机制而不用改变代码本身. 这个包包含了可以表示模型, 资源, 属性, 文本, 陈述和其他RDF关键概念的接口, 还有一个用来创建模型的ModelFactory. 所以如果要应用代码与实现类保持独立, 最好尽可能地使用接口, 而不要使用特定的实现类.
com.hp.hpl.jena.Tutorial包包含了本教程所有例子中使用到的工作源代码.
com.hp.hpl.jena.impl这些包包含了许多执行时所常用的执行类. 比如, 它们定义了诸如ResourseImpl, PropertyImpl和LiteralImpl的类, 这些类可以被不同的应用直接使用也可以被继承使用. 应用程序应该尽可能少地直接使用这些类. 例如, 与其使用ResouceImpl来创建一个新的实例, 更好的办法是使用任何正在使用的模型的createResource方法来完成. 那样的话, 如果模型的执行采用了一个优化的Resouce执行, 那么在这两种类型中不需要有任何的转换工作.

________________________________________
操纵模型
到目前为止, 本教程主要讲述的是如何创建, 读入和输出RDF模型. 现在是时候要讲述如何访问模型中的信息.
如果有了一个资源的URI, 那么就可以用Model.getResource(String uri)来从模型获取这个资源对象. 这个方法被定义来返回一个资源对象, 如果它确实存在于模型中, 否则的话就创建一个新的. 例如, 如何从模型中获取Adam Smith资源, 这个模型是Tutorial5中从文件读入的:
// retrieve the John Smith vcard resource from the model
Resource vcard = model.getResource(johnSmithURI);
  
Resouce接口定义了一系列用于访问某个资源的属性的方法. Resource.getProperty(Property p)方法访问了该资源的属性. 这个方法不允许通常的Java访问的转换, 因为所返回的对象是Statement, 而不是你所预计的Property. 返回整个陈述的好处是允许应用程序通过使用它的某个访问方法来访问该陈述的客体来访问这个属性值. 例如如何获取作为vcard:N属性值的资源:
// retrieve the value of the N property
Resource name = (Resource) vcard.getProperty(VCARD.N)
                                .getObject();

一般而言, 一个陈述的客体可以是一个资源或是一个文本. 所以此应用程序代码知道这个值一定是个资源, 就将类型资源映射到返回的对象上. Jena的目标之一是提供会返回值为特定类型的方法, 这样,应用程序就不必再做类型转换工作, 也不必再编译时做类型检查工作. 以上的代码片段也可以写成更方便的形式:
// retrieve the value of the FN property
Resource name = vcard.getProperty(VCARD.N)
                     .getResource();
类似地, 属性的文本值也可以被获取:
// retrieve the given name property
String fullName = vcard.getProperty(VCARD.FN)
                        .getString();

在这个例子中, 资源vcard只有一个vcard:FN属性和一个vcard:N属性. RDF允许资源有重复的属性, 例如Adam可能有超过一个的昵称. 让我们假设他有两个昵称:
// add two nickname properties to vcard
vcard.addProperty(VCARD.NICKNAME, "Smithy")
.addProperty(VCARD.NICKNAME, "Adman");

正如前面所提到的那样, Jena将RDF模型表示为一组陈述, 所以在模型中新增一个与原有陈述有着相同的主体,谓词和客体的陈述并不会后什么作用. Jena没有定义会返回模型中存在的两个昵称中的哪一个. Vcard.getProperty(VCARD.NICKNAME)调用的结果是不确定的. Jena会返回这些值中的某一个, 但是并不保证两次连续的调用会同一个值.
一个属性很有可能会出现多次, 而方法Resource.listProperty(Property p)可以用来返回一个iterator, 这个iterator会列出所有的值. 此方法所返回的iterator返回的对象的类型为Statement.我们可以像这样列出所有的昵称:
// set up the output
System.out.println("The nicknames of \""
                      + fullName + "\" are:");
// list the nicknames
StmtIterator iter = vcard.listProperties(VCARD.NICKNAME);
while (iter.hasNext()) {
    System.out.println("    " + iter.nextStatement()
                                    .getObject()
                                    .toString());
}

此代码可以在Tutorial6中找到, 运行后会产生如下输出:
The nicknames of "John Smith" are:
    Smithy
    Adman
一个资源的所有属性可以用不带参数的listStatement()方法列出.
________________________________________
查询模型
前一节讨论了如何通过一个有着已知URI的资源来操纵模型. 本节要讨论查询模型. 核心的Jena API只支持一些有限的查询原语. 对于更强大查询设备RDQL的介绍不在此文档中.
列出模型所有陈述的Model.listStatements()方法也许是最原始的查询模型方式. 然而并不推荐在大型的模型上使用这个方法. 类似的有Model.listSubjects(), 但其所返回的iterator会迭代所有含有属性的资源, 例如是一些陈述的主体.
Model.listSubjectsWithProperty(Property p, RDFNode o)方法所返回的iterator跌代了所有具有属性p且p属性的值为o的资源. 我们可能会预计使用rdf:type属性来搜索资源的类型属性以获得所有的vcard资源:
// retrieve all resource of type Vcard.
ResIterator iter = model.listSubjectsWithProperty(RDF.type, VCARD.Vcard);

然而, 不幸的是, 我们现在正在使用的vcard模式并没有为vcard定义类型. 然而, 如果我们假设只有类型为vcard的资源才会使用vcard:FN属性, 并且在我们的数据中, 所有此类资源都有这样一个属性, 那么我们就可以像这样找到所有的vcard:
// list vcards
ResIterator iter = model.listSubjectsWithProperty(VCARD.FN);
while (iter.hasNext()) {
    Resource r = iter.nextResource();
    ...
}

所有的这些查询方法不过是在原语查询方法model.listStatements(Select s)上稍做变化而已. 此方法会返回一个iterator, 该iterator会跌代模型中所有被s选中的陈述. 这个selector接口被设计成可扩展的, 但是目前, 它只有一个执行类,那就是com.hp.hpl.jena.rdf.model包中的SimpleSelector类. 在Jena中使用SimpleSelector是很少见的情况, 即当需要直接使用一个特定类而不是使用接口. SimpleSelector的构造函数带有三个参数:

Selector selector = new SimpleSelector(subject, predicate, object)

这个selector会选择所有主体与参数subject相配, 谓词与参数predicate相配, 且客体与参数object相配的陈述.
如果某个参数值为null, 则它表示与任何值均匹配; 否则的话, 它们就会匹配一样的资源或文本. (当两个资源有相同的URI或是同一个空白结点时, 这两个资源就是一样的; 当两个文本是一样的当且仅当它们的所有成分都是一样的.) 所以:
Selector selector = new SimpleSelector(null, null, null);
  
会选择模型中所有的陈述.
Selector selector = new SimpleSelector(null, VCARD.FN, null);
  
会选择所有谓词为VCARD.FN的陈述, 而对主体和客体没有要求. 作为一个特殊的缩写,
listStatements( S, P, O )
等同与
listStatements( new SimpleSelector( S, P, O ) )
下面的代码可以在Tutorial7中找到, 列举了数据库中所有vcard中的全名.
// select all the resources with a VCARD.FN property
ResIterator iter = model.listSubjectsWithProperty(VCARD.FN);
if (iter.hasNext()) {
    System.out.println("The database contains vcards for:");
    while (iter.hasNext()) {
        System.out.println("  " + iter.nextStatement()
                                      .getProperty(VCARD.FN)
                                      .getString());
    }
} else {
    System.out.println("No vcards were found in the database");
}
         
这个会产生类似如下的输出:
The database contains vcards for:
  Sarah Jones
  John Smith
  Matt Jones
  Becky Smith
  
你的下一个练习是修改此代码以使用SimpleSelector而不是使用listSubjectsWithProperty来达到相同的效果.
让我们看看如何对所选择的陈述实行更好的控制. SimpleSelector可以被继承, 它的select方法可以被修改来实现更好的过滤:
// select all the resources with a VCARD.FN property
// whose value ends with "Smith"
StmtIterator iter = model.listStatements(
    new SimpleSelector(null, VCARD.FN, (RDFNode) null) {
        public boolean selects(Statement s)
            {return s.getString().endsWith("Smith");}
});

这个示例使用了一个简洁的Java技术, 就是当创建此类的一个实例时重载一个内联的方法. 这里selects(…)方法会检查以保证全名以"Smith"做结尾. 重要的是要注意对主体, 谓语和客体的过滤是在调用selects(…)方法之前的执行的, 所以额外的测试只会被应用于匹配的陈述.
完整的代码可以在Tutorial8中找到, 并会产生如下的输出:
The database contains vcards for:
  John Smith
  Becky Smith
你也许会认为下面的代码:
// do all filtering in the selects method
StmtIterator iter = model.listStatements(
  new
      SimpleSelector(null, null, (RDFNode) null) {
          public boolean selects(Statement s) {
              return (subject == null   || s.getSubject().equals(subject))
                  && (predicate == null || s.getPredicate().equals(predicate))
                  && (object == null    || s.getObject().equals(object))
          }
     }
});
等同于:
StmtIterator iter =
  model.listStatements(new SimpleSelector(subject, predicate, object)

虽然在功能上它们可能是等同的, 但是第一种形式会列举出模型中所有的陈述, 然后再对它们进行逐一的测试. 而第二种形式则允许执行时建立索引来提供性能. 你可以在一个大模型中自己试试验证一下, 但是现在先为自己倒一杯咖啡休息一下吧.
________________________________________
对模型的操作
Jena提供了把模型当作一个集合整体来操纵的三种操作方法. 即三种常用的集合操作:并, 交和差.
两个模型的并操作就是把表示两个模型的陈述集的并操作. 这是RDF设计所支持的关键操作之一. 它此操作允许把分离的数据源合并到一起. 考虑下面两个模型:


当它们被合并时, 两个http://...JohnSmith会合并成一个, 重复的vcard:FN箭头会被丢弃, 此时就会产生:

让我们看一下这个代码的功能(完整的代码在Tutorial9中), 再看看会发生什么.
// read the RDF/XML files
model1.read(new InputStreamReader(in1), "");
model2.read(new InputStreamReader(in2), "");

// merge the Models
Model model = model1.union(model2);

// print the Model as RDF/XML
model.write(system.out, "RDF/XML-ABBREV");

petty writer会产生如下的输出:
<rdf:RDF
    xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
    xmlns:vcard="http://www.w3.org/2001/vcard-rdf/3.0#">
  <rdf:Description rdf:about="http://somewhere/JohnSmith/">
    <vcard:EMAIL>
      <vcard:internet>
        <rdf:value>John@somewhere.com</rdf:value>
      </vcard:internet>
    </vcard:EMAIL>
    <vcard:N rdf:parseType="Resource">
      <vcard:Given>John</vcard:Given>
      <vcard:Family>Smith</vcard:Family>
    </vcard:N>
    <vcard:FN>John Smith</vcard:FN>
  </rdf:Description>
</rdf:RDF>

即便你不熟悉RDF/XML的语法细节, 你仍然可以清楚的看见模型如同预期般被合并了. 我们可以在类似的方式下运算模型的交操作和差操作.
________________________________________
容器
RDF定义了一类特殊的资源来表示事物的集合. 这些资源称为容器. 一个容器的成员可以是资源也可以是文本. 有三类容器:
一个BAG是一个无序的集合.
一个ALT是一个用来表示备选项的无序的集合.
一个SEQ是一个有序的集合.
一个容器由一个资源表示. 该资源会有一个rdf:type属性, 属性值为rdf:Bag, 或rdf:Alt, 或是rdf:Seq, 再或是这些类型的子类型, 这取决于容器的类型. 容器的第一个成员是容器的rdf:_1的属性所对应的属性值; 第二个成员是容器的rdf:_2属性的值, 依此类推. 这些rdf:_nnn属性被称为序数属性.
例如, 一个含有Smith的vcard的简单bag容器的模型可能会看起来是这样的:

虽然bag容器的成员们被rdf:_1,rdf_2等等的属性所表示, 但是这些属性的顺序却并不重要. 即便我们将rdf:_1和rdf:_2的属性值交换, 但是交换后的模型仍然表示相同的信息.
Alt是设计用来表示被选项的. 例如, 我们假定有一个表示软件产品的资源. 它可能有一个属性指示从哪里可以获得次软件产品. 这个属性值可能是一个包含各种下载地址的Alt集合. Alt是无序的, 除了rdf:_1属性有着特殊的意义, 它表示默认的选项.
尽管我们可以用基本的资源和属性机制来处理容器, Jena为处理容器设计了显式的接口和执行类. 要避免在使用一个对象来操作容器的同时去使用低层方法来改变容器.
让我们修改Tutorial8以创建一个bag:
// create a bag
Bag smiths = model.createBag();

// select all the resources with a VCARD.FN property
// whose value ends with "Smith"
StmtIterator iter = model.listStatements(
    new SimpleSelector(null, VCARD.FN, (RDFNode) null) {
        public boolean selects(Statement s) {
                return s.getString().endsWith("Smith");
        }
    });
// add the Smith's to the bag
while (iter.hasNext()) {
    smiths.add(iter.next().getSubject());
}

如果我们将次模型输出可以看见它含有类似如下的成分:
<rdf:RDF
  xmlns:rdf='http://www.w3.org/1999/02/22-rdf-syntax-ns#'
  xmlns:vcard='http://www.w3.org/2001/vcard-rdf/3.0#'
>
...
  <rdf:Description rdf:nodeID="A3">
    <rdf:type rdf:resource='http://www.w3.org/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>

这表示了Bag资源.
容器接口提供了一个iterator来列举容器的内容:
// print out the members of the bag
NodeIterator 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 Smith
  Becky Smith

本例的可执行代码可以在Tutorial10中找到.
Jena类所提供的操纵容器的方法包括增加新成员, 在容器中间插入新成员和删除已有的成员. Jena容器类目前保证所使用的序数属性列表会从rdf:_1开始并且是相邻的. RDF核心工作小组放松了此项限制, 以允许有局部的容器表示. 所以这是Jena将来可能会修改的地方之一.

________________________________________
关于文本(Literals)和数据类型的更多探讨
RDF文本(literals)并不仅仅是简单的字符串而已. 文本可能有一个语言标签来指示该文本使用的语言. 有英语语言标签的文本"chat"会被认为与有着法语语言标签的文本"chat"是不同的. 这个奇怪的特性是原有RDF/XML语法产生的赝象(artefact).
另外, 事实上共有两种文本. 在一种里, 字符串成分只是简单的字符串. 而在另一种里, 字符串成分被预计为格式良好的XML片段. 当一个RDF模型被写成RDF/XML形式时, 一个特殊的使用parseType='Literal'的属性(attribute)构造会被使用来表示它.
在Jena中, 当一个文本被创建时, 这些属性就被设置了. 例如, 在Tutorial11中:
// create the resource
Resource r = model.createResource();

// add the property
r.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 Model
model.write(system.out);

会产生:

<rdf:RDF
  xmlns:rdf='http://www.w3.org/1999/02/22-rdf-syntax-ns#'
  xmlns:rdfs='http://www.w3.org/2000/01/rdf-schema#'
>
  <rdf:Description rdf:nodeID="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>

如果两个文本被认为是相同的, 它们一定都是XML的文本或都是简单的文本. 另外, 它们两个要么都没有语言标签, 要么有相同的语言标签. 对简单的文本而言, 两者的字符串一定是相同的. XML文本的等同有两点要求. 第一, 前面提到的要求必须被满足并且字符串必须相同. 第二, 如果它们字符串的cannonicalization一样的话, 它们就是一样的.(译者注: 找不到cannonicalization中文的解释.).
Jena接口也支持类型文字. 老式的对待类型文字的方法是把他们当成是字符串的缩写: 有类型的值会通过Java的常用方法转换为字符串, 并且这些字符串被存储在模型中. 例如, 可以试一试(注意:对于简单类型文字, 我们可以省略对model.createLiteral(…)的调用):
// create the resource
Resource r = model.createResource();

// add the property
r.addProperty(RDFS.label, "11")
.addProperty(RDFS.label, 11);

// write out the Model
model.write(system.out, "N-TRIPLE");

产生的输出如下:
_:A... <http://www.w3.org/2000/01/rdf-schema#label> "11" .

因为两个文本都是字符串"11", 所以只会有一个陈述被添加.
RDF核心工作小组定义了支持RDF数据类型的机制. Jena支持那些使用类型文字的机制; 但是本教程中不会对此讨论.
________________________________________
术语表
空白结点
表示一个资源, 但是并没有指示该资源的URI. 空白结点的作用如同第一逻辑中的存在符合变量.
Dublin 核心
一个关于网络资源的元数据标准. 更详细的信息可以在Dublin Core web site找到.
文本
一个可以作为属性值的字符串.
客体
三元组的一部分, 也就是陈述的值.
谓词
三元组的属性部分.
属性
属性(property)是资源的一个属性(attribute). 例如, DC.title是一个属性, RDF.type也是一个属性.
资源
某个实体. 它可以是一个网络资源, 例如一个网页; 它也可以是一个具体的物理对象, 例如一棵树或一辆车; 它也可以是一个抽象的概念, 例如国际象棋或足球. 资源由URI命名.
陈述
RDF模型中的一个箭头, 通常被解理解为一个事实
主体
RDF模型中箭头出发点的那个资源.
三元组
一个含有主体, 谓词和客体的结构. 是陈述的另一种称呼.
________________________________________
脚注
RDF资源的标签可以包括一个片段标签, 例如http://hostname/rdf/Tutorial/#ch-Introduction, 所以, 严格地讲, 一个RDF资源是由一个URI表示的.
文本可以表示为一个字符串, 也可以有一个可选的语言编码来表示该字符串的语言. 例如, 文本可能有一个表示英语的语言编码"en", 而文本"deux"可能有一个表示法语的语言编码"fr".
________________________________________
作者: Brian McBride
更新人: Daniel Boothby and Chris Dollin
$Id: index.html,v 1.11 2004/01/09 17:16:06 andy_seaborne Exp $
翻译者:april1019(这是我的翻译,希望对大家有帮助)


--  作者:admin
--  发布时间:7/22/2005 12:44:00 PM

--  
赞!!
--  作者:tiansword
--  发布时间:7/29/2005 11:58:00 AM

--  
thanks!
--  作者:webcoolie
--  发布时间:8/5/2005 4:34:00 PM

--  
太好了
--  作者:ppf4
--  发布时间:8/12/2005 6:24:00 PM

--  
Jana是HP公司的产品吗?在那可以下?
--  作者:jjojojj
--  发布时间:8/13/2005 11:09:00 AM

--  
在这里可以下啊: http://nchc.dl.sourceforge.net/sourceforge/jena/Jena-2.2.zip
--  作者:yulin
--  发布时间:12/1/2005 9:39:00 PM

--  
thanks

--  作者:kyocica
--  发布时间:12/2/2005 3:47:00 PM

--  
顶啊

--  作者:ztszhang
--  发布时间:12/2/2005 7:00:00 PM

--  
翻译得真好!
感谢!
--  作者:vole
--  发布时间:12/3/2005 1:50:00 PM

--  
赞一个
--  作者:yhl2016
--  发布时间:12/4/2005 3:56:00 PM

--  
谢谢!
--  作者:anew88
--  发布时间:12/5/2005 4:33:00 PM

--  
谢谢
--  作者:instillwater
--  发布时间:12/6/2005 11:18:00 AM

--  
赞!
--  作者:xirufeng
--  发布时间:12/12/2005 1:50:00 PM

--  
谢谢楼主
--  作者:xxiaoshilang
--  发布时间:12/13/2005 5:32:00 PM

--  
非常感谢楼主
--  作者:ld725725
--  发布时间:12/15/2005 9:37:00 AM

--  
多谢
--  作者:fanfanfly
--  发布时间:12/15/2005 10:16:00 PM

--  
thanks
--  作者:dbjaje
--  发布时间:12/16/2005 4:10:00 PM

--  
正在写论文,谢谢先,再看。
--  作者:xuqinan
--  发布时间:12/20/2005 10:36:00 AM

--  
顶!好
--  作者:xinxindong
--  发布时间:12/20/2005 7:41:00 PM

--  
太好了,谢谢!
--  作者:hzh_nc
--  发布时间:2/2/2006 9:04:00 AM

--  
谢谢! 好东西
--  作者:lbfeng
--  发布时间:2/8/2006 4:16:00 PM

--  
非常感谢!
--  作者:yimuyunlang
--  发布时间:3/30/2006 9:29:00 PM

--  
两个字:好人!比起那些只谈些概念的人不给实例的大虾好多了!很多大虾回答问题时不痛不痒,没有透明的说的让人一目了然的内容,说明大虾也只是忽悠理论,操作不行,自己肚中没有实例
--  作者:Ambrosia
--  发布时间:3/31/2006 9:48:00 AM

--  
zan
--  作者:krazy
--  发布时间:3/31/2006 4:58:00 PM

--  
谢谢共享
--  作者:sunww1999
--  发布时间:4/3/2006 11:45:00 AM

--  
太感谢了!!!
--  作者:wolf_xjf
--  发布时间:4/7/2006 1:45:00 PM

--  
谢谢!
--  作者:ruoxue
--  发布时间:4/12/2006 9:59:00 PM

--  
不错谢谢
--  作者:gwj_77_77_77
--  发布时间:4/18/2006 10:35:00 AM

--  
下了
多谢

--  作者:cao_shan
--  发布时间:4/18/2006 4:17:00 PM

--  
april1019,你辛苦了!谢谢!
--  作者:jsarrn
--  发布时间:7/21/2006 9:38:00 PM

--  
感谢!

--  作者:lixin_lichuan
--  发布时间:8/5/2006 2:25:00 PM

--  
谢谢!

--  作者:fromtoz
--  发布时间:8/6/2006 9:08:00 PM

--  
good
--  作者:sophia-rener
--  发布时间:8/7/2006 2:29:00 PM

--  
谢谢,正需要呢!
--  作者:paopao2008
--  发布时间:8/27/2006 5:02:00 PM

--  
真诚感谢中~
--  作者:windancer2003
--  发布时间:8/30/2006 12:22:00 AM

--  
非常感觉!
--  作者:lqm
--  发布时间:8/30/2006 5:04:00 PM

--  
好东东啊,我对RDF的入门理解,全部来自这篇文档啊!!!!
--  作者:skinner
--  发布时间:10/18/2006 2:00:00 AM

--  
感谢万分

--  作者:coco
--  发布时间:11/16/2006 9:04:00 PM

--  
太好了.支持啊
--  作者:zyq569
--  发布时间:11/16/2006 9:42:00 PM

--  
好文章,楼主好样的
--  作者:zyq569
--  发布时间:11/16/2006 9:44:00 PM

--  
好文章,楼主好样的
--  作者:ebookisok
--  发布时间:11/17/2006 11:17:00 AM

--  

--  作者:cao_shan
--  发布时间:11/17/2006 12:56:00 PM

--  
不错!
--  作者:Protege
--  发布时间:11/18/2006 6:34:00 PM

--  
第一次顶,谢谢了
--  作者:myxiangrong2000
--  发布时间:11/19/2006 9:43:00 AM

--  
ding
thanks!
--  作者:flyingFang
--  发布时间:11/21/2006 11:37:00 AM

--  
大家好,我看不到楼主文章中的图。请问是只有我看不到么?

这篇文章的原文,大家可以给个链接么,谢谢。看到图的话,可能更利于理解。

谢谢


--  作者:flyingFang
--  发布时间:11/21/2006 11:39:00 AM

--  
sorry。 刚刚自己找到原文了,可能大家都知道哈。我是新手,给同样是新手的同学,提供连接:

http://jena.sourceforge.net/tutorial/RDF_API/


--  作者:yongzhu
--  发布时间:11/21/2006 2:53:00 PM

--  
这是精华啊
--  作者:tacl202
--  发布时间:11/30/2006 8:55:00 AM

--  
thanks
--  作者:tangling
--  发布时间:3/22/2007 10:23:00 AM

--  
多谢!太棒了!
--  作者:eview
--  发布时间:3/25/2007 4:18:00 PM

--  
哈哈, 看完了英文, 中文有点不适应了
--  作者:ricky_lxl
--  发布时间:5/9/2007 11:22:00 PM

--  
感谢!!
--  作者:zj007zj007
--  发布时间:5/10/2007 9:11:00 AM

--  
谢谢,收下了。
--  作者:campushr
--  发布时间:5/11/2007 12:33:00 PM

--  
向楼主表示感谢
--  作者:bzbc
--  发布时间:9/28/2007 10:32:00 PM

--  Jena 简介
Jena 简介
http://www.ibm.com/developerworks/cn/java/j-jena/

RDF 越来越被认为是表示和处理半结构化数据的一种极好选择。本文中,Web 开发人员 Philip McCarthy 向您展示了如何使用 Jena Semantic Web Toolkit,以便在 Java 应用程序中使用 RDF 数据模型。

“资源描述框架(Resource Description Framework,RDF)”最近成为 W3C 推荐标准,与 XML 和 SOAP 等 Web 标准并排。RDF 可以应用于处理特殊输入数据(如 CRM)的领域,已经广泛用于社会网络和自助出版软件(如 LiveJournal 和 TypePad)。

Java 程序员将越来越多地得益于具有使用 RDF 模型的技能。在本文中,我将带您体验惠普实验室的开放源代码 Jena Semantic Web Framework(请参阅 参考资料)的一些功能。您将了解如何创建和填充 RDF 模型,如何将它们持久存储到数据库中,以及如何使用 RDQL 查询语言以程序方式查询这些模型。最后,我将说明如何使用 Jena 的推理能力从本体推断模型知识。

本文假设您已经就图形、三元组和模式等概念方面对 RDF 比较熟悉,并对 Java 编程有基本的了解。

创建简单的 RDF 模型

我们从基本操作开始:从头创建模型并向其添加 RDF 语句。本节,我将说明如何创建描述一组虚构家庭成员之间关系的模型,如图 1 中所示:

图 1. 虚拟家庭树

将使用来自“关系”词汇表(请参阅 参考资料)的属性 siblingOf 、 spouseOf 、 parentOf 和 childOf 来描述不同的关系类型。为简单起见,家庭成员用来自虚构名称空间的 URI( http://family/ )进行标识。词汇表 URI 通常以 Jena 代码形式使用,所以将它们声明为 Java 常量会非常有用,减少了错误输入。

Schemagen

当您通过 Jena 的 API 来使用模型时,为模型词汇表中的每个属性定义常量非常有用。如果有词汇表的 RDF、DAML 或 OWL 表示,Jena 的 Schemagen 工具可以自动生成这些常量,使您的工作更加容易。

Schemagen 在命令行中运行,使用的参数包括模式或本体文件的位置、要输出的类的名称和 Java 包。然后可以导出生成的 Java 类,其 Property 常量用于访问模型。

还可以使用 Ant 将 Schemagen 作为构建处理的一部分来运行,保持 Java 常量类与正在变化的词汇表保持同步。


Jena 的 ModelFactory 类是创建不同类型模型的首选方式。在这种情况下,您想要空的、内存模型,所以要调用的方法是 ModelFactory.createDefaultModel() 。这种方法返回 Model 实例,您将使用它创建表示家庭中每个成员的 Resource 。创建了资源后,可以编写关于这些资源的语句并添加到模型中。

在 Jena 中,语句的主题永远是 Resource ,谓词由 Property 表示,对象是另一个 Resource 或常量值。常量在 Jena 中通过 Literal 类型表示。所有这些类型共享公共接口 RDFNode 。将需要四个不同的 Property 实例表示家庭树中的关系。这些实例使用 Model.createProperty() 创建。

将语句添加到模型中的最简单方法是通过调用 Resource.addProperty() 。此方法以 Resource 作为主题在模型中创建语句。该方法使用两个参数,表示语句谓词的 Property 和语句的对象。 addProperty() 方法被过载:一个过载使用 RDFNode 作为对象,所以可以使用 Resource 或 Literal 。还有有益过载,它们使用由 Java 原语或 String 表示的常量。在示例中,语句的对象是表示其他家庭成员的 Resource 。

通过使用三元组的主题、谓词和对象调用 Model.createStatement() ,还可以直接在模型上创建语句。注意以此种方式创建 Statement 不将其添加到模型中。如果想将其添加到模型中,请使用创建的 Statement 调用 Model.add() ,如清单 1 所示:

清单 1. 创建模型来表示虚构的家庭
// URI declarations
            String familyUri = "http://family/";
            String relationshipUri = "http://purl.org/vocab/relationship/";
            // Create an empty Model
            Model model = ModelFactory.createDefaultModel();
            // Create a Resource for each family member, identified by their
            URI Resource adam = model.createResource(familyUri+"adam");
            Resource beth = model.createResource(familyUri+"beth");
            Resource chuck = model.createResource(familyUri+"chuck");
            Resource dotty = model.createResource(familyUri+"dotty");
            // and so on for other family members
            // Create properties for the different types of relationship to represent
            Property childOf = model.createProperty(relationshipUri,"childOf");
            Property parentOf = model.createProperty(relationshipUri,"parentOf");
            Property siblingOf = model.createProperty(relationshipUri,"siblingOf");
            Property spouseOf = model.createProperty(relationshipUri,"spouseOf");
            // Add properties to adam describing relationships to other family members
            adam.addProperty(siblingOf,beth);
            adam.addProperty(spouseOf,dotty);
            adam.addProperty(parentOf,edward);
            // Can also create statements directly .. .
            Statement statement = model.createStatement(adam,parentOf,fran);
            // but remember to add the created statement to the model
            model.add(statement);
            


整个代码示例 FamilyModel.java 还说明了语句批量如何一次添加到模型中,或者作为一个数组或者作为 java.util.List 。

构建了家庭模型后,我们看一下如何使用 Jena 的查询 API 从模型中提取信息。


回页首

查询 RDF 模型

程序化地查询 Jena 模型主要通过 list() 方法在 Model 和 Resource 接口中执行。可以使用这些方法获得满足特定条件的主题、对象和 Statement 。它们还返回 java.util.Iterator 的特殊化,其具有返回特定对象类型的其他方法。

我们返回 清单 1的家庭模型,看一下可以查询它的不同方法,如清单 2 所示:

清单 2. 查询家庭模型
// List everyone in the model who has a child:
            ResIterator parents = model.listSubjectsWithProperty(parentOf);
            // Because subjects of statements are Resources, the method returned a ResIterator
            while (parents.hasNext()) {
            // ResIterator has a typed nextResource() method
            Resource person = parents.nextResource();
            // Print the URI of the resource
            System.out.println(person.getURI()); }
            // Can also find all the parents by getting the objects of all "childOf" statements
            // Objects of statements could be Resources or literals, so the Iterator returned
            // contains RDFNodes
            NodeIterator moreParents = model.listObjectsOfProperty(childOf);
            // To find all the siblings of a specific person, the model itself can be queried
            NodeIterator siblings = model.listObjectsOfProperty(edward, siblingOf);
            // But it's more elegant to ask the Resource directly
            // This method yields an iterator over Statements
            StmtIterator moreSiblings = edward.listProperties(siblingOf);
            


最通用的查询方法是 Model.listStatements(Resource s, Property p, RDFNode o) ,下面说明的便利方法都是以其为基础。所有这些参数都可以保留为 null ,在这种情况下,它们作为通配符,与任何数据都匹配。清单 3 中显示了 Model.listStatements() 的一些使用示例:

清单 3. 使用选择器查询模型
// Find the exact statement "adam is a spouse of dotty"
            model.listStatements(adam,spouseOf,dotty);
            // Find all statements with adam as the subject and dotty as the object
            model.listStatements(adam,null,dotty);
            // Find any statements made about adam
            model.listStatements(adam,null,null);
            // Find any statement with the siblingOf property
            model.listStatements(null,siblingOf,null);
            


回页首

导入和持久化模型

不是所有的应用程序都从空模型开始。更常见的是,在开始时从现有数据填充模型。在这种情况下,使用内存模型的缺点是每次启动应用程序时都要从头重新填充模型。另外,每次关闭应用程序时,对内存模型进行的更改都将丢失。

一种解决方案是使用 Model.write() 序列化模型到文件系统,然后在开始时使用 Model.read() 将其取消序列化。不过,Jena 还提供了持久化模型,它们会被持续而透明地持久存储到后备存储器。Jena 可以在文件系统中或在关系数据库中持久化它的模型。当前支持的数据库引擎是 PostgreSQL、Oracle 和 MySQL。

WordNet

WordNet 是“英文语言的词汇数据库”。我使用的是 Sergey Melnik 和 Stefan Decker 的 RDF 表示。它具有四个单独的模型,本文示例中将使用其中三个模型。

WordNet-nouns 模型包含 WordNet 表示的所有“词汇概念”和用于表示每个概念的“单词形式”。例如,它包含由单词形式“domestic dog”、“dog”和“Canis familiaris”表示的词汇概念。

第二个模型是 WordNet-glossary。它提供模型中每个词汇概念的简短定义。“dog”的词汇概念具有词汇条目“a member of the genus Canis (probably descended from the common wolf) that has been domesticated by man since prehistoric times.”

WordNet-hyponyms 是第三个模型。它定义模型中概念的层次结构。概念“dog”是概念“canine”下位词,而“canine” 本身是概念“carnivore”的下位词。


为了说明如何导入和持久化模型,我将 WordNet 1.6 数据库的 RDF 表示导入到 MySQL 中。因为我使用的 WordNet 表示采用多个单独 RDF 文档的形式,将这些文档导入到一个 Jena 模型中会合并它们的语句。 图 2 说明了 Nouns 和 Glossary 模型合并后 WordNet 模型的片段的结构:

图 2. 合并的 WordNet nouns 和 glossary 模型的结构

创建数据库后台模型的第一步是说明 MySQL 驱动类,并创建 DBConnection 实例。 DBConnection 构造函数使用用户的 ID 和密码登录到数据库。它还使用包含 Jena 使用的 MySQL 数据库名称的数据库 URL 参数,格式为 "jdbc:mysql://localhost/dbname" 。Jena 可以在一个数据库内创建多个模型。 DBConnection 的最后一个参数是数据库类型,对于 MySQL,该参数为 "MySQL" 。

然后 DBConnection 实例可以与 Jena 的 ModelFactory 一起使用来创建数据库后台模型。

创建了模型后,可以从文件系统中读入 WordNet RDF 文档。不同的 Model.read() 方法可以从 Reader 、 InputStream 或 URL 填充模型。可以通过 Notation3 、N-Triples 或默认情况下通过 RDF/XML 语法解析模型。WordNet 作为 RDF/XML 进行序列化,所以不需要指定语法。读取模型时,可以提供基准 URI。基准 URI 用于将模型中的任何相对 URI 转换成绝对 URI。因为 WordNet 文档不包含任何相对 URI,所以此参数可以指定为 null 。

清单 4 显示了将 WordNet RDF/XML 文件导入到 MySQL 持久化模型的完整过程:

清单 4. 导入和持久化 WordNet 模型
// Instantiate the MySQL driver
            Class.forName("com.mysql.jdbc.Driver");
            // Create a database connection object
            DBConnection connection = new DBConnection(DB_URL, DB_USER, DB_PASSWORD, DB_TYPE);
            // Get a ModelMaker for database-backed models
            ModelMaker maker = ModelFactory.createModelRDBMaker(connection);
            // Create a new model named "wordnet."Setting the second parameter to "true" causes an
            // AlreadyExistsException to be thrown if the db already has a model with this name
            Model wordnetModel = maker.createModel("wordnet",true);
            // Start a database transaction.Without one, each statement will be auto-committed
            // as it is added, which slows down the model import significantly.
            model.begin();
            // For each wordnet model .. .
            InputStream in = this.getClass().getClassLoader().getResourceAsStream(filename);
            model.read(in,null);
            // Commit the database transaction model.
            commit();


由于已经填充了 wordnet 模型,以后可以通过调用 ModelMaker.openModel("wordnet",true); 来访问该模型。

仅使用 Jena 的 API 查询像 WordNet 这样巨大的模型将有一定的限制性,因为要执行的每类查询都将需要专门编写多行的代码。幸运的是,Jena 以 RDQL 形式提供了一种表达通用查询的机制。


回页首

RDF 数据查询语言(RDQL)

RDQL 是 RDF 的查询语言。虽然 RDQL 还不是正是的标准,但已由 RDF 框架广泛执行。RDQL 允许简明地表达复杂的查询,查询引擎执行访问数据模型的繁重工作。RDQL 的语法表面上类似 SQL 的语法,它的一些概念对已经使用过关系数据库查询的人来说将比较熟悉。在 Jena Web 站点中可以找到极好的 RDQL 指南,但几个简单的示例会对说明基础知识大有帮助。

使用 jena.rdfquery 工具可以在命令行上对 Jena 模型执行 RDQL 查询。RDFQuery 从文本文件中获取 RDQL 查询,然后对指定的模型运行该查询。对数据库后台模型运行查询需要相当多的参数。清单 5 中显示了运行下列示例需要的完整命令行:

清单 5. 从命令行运行 RDQL 查询
$java jena.rdfquery --data jdbc:mysql://localhost/jena --user dbuser --password dbpass
            --driver com.mysql.jdbc.Driver --dbType MySQL --dbName wordnet --query example_query.rdql
            


正如您看到的,这些参数中的大多数参数都提供了创建与 MySQL 的连接所需的详细信息。其中重要的部分是 --query example_query.rdql ,它是 RDQL 文件的位置。还要注意运行 jena.rdfquery 需要 Jena 的 lib 目录中的所有 JAR 文件。

清单 6 显示了您将检查的第一个查询:

清单 6. 查找“domestic dog”的 WordNet 词汇条目的 RDQL 查询
SELECT
            ?definition
            WHERE
            (?concept, <wn:wordForm>, "domestic dog"),
            (?concept, <wn:glossaryEntry>, ?definition)
            USING
            wn FOR <http://www.cogsci.princeton.edu/~wn/schema/>
            


SELECT 部分声明查询要输出的变量 — 在本例中,是名为 definition 的变量。 WHERE 子句引入第二个变量 concept 并定义与图形匹配的三元组。查询在具有 WHERE 子句中的所有三元组的图形中查找语句。所以,在英语中, WHERE 子句的意思为“查找具有 'domestic dog' 作为单词形式的概念,并查找这些概念的词汇条目”,如图 3 所示。 USING 子句提供一种便利,用于声明名称空间的前缀。

图 3. 清单 6 中的 WHERE 子句匹配的图形

运行查询的结果为:

definition
            ===============================================================================
            "a member of the genus Canis (probably descended from the common wolf) that has
            been domesticated by man since prehistoric times; occurs in many breeds; "the
            dog barked all night""
            


所以这种情况仅有一个结果。清单 7 中显示的下个查询的意思为“查找单词 'bear' 表示的概念,并查找这些概念的词汇条目”。

清单 7. 查找“bear”的 WordNet 词汇条目的 RDQL 查询
SELECT
            ?definition
            WHERE
            (?concept, <wn:wordForm>, "bear"),
            (?concept, <wn:glossaryEntry>, ?definition)
            USING
            wn FOR <http://www.cogsci.princeton.edu/~wn/schema/>
            


此查询返回 15 个结果,因为此单词形式表示多个不同的概念。结果为:

definition
            ===============================================================================
            "massive plantigrade carnivorous or omnivorous mammals with long shaggy coats
            and strong claws"
            "an investor with a pessimistic market outlook"
            "have on one's person; "He wore a red ribbon"; "bear a scar""
            "give birth (to a newborn); "My wife had twins yesterday!""
            


清单 8 中显示了另一个示例,查找其他两个单词的上位词(母词):

清单 8. 查找“panther”和“tiger”的 WordNet 上位词的 RDQL 查询
SELECT
            ?wordform, ?definition
            WHERE
            (?firstconcept, <wn:wordForm>, "panther"),
            (?secondconcept, <wn:wordForm>, "tiger"),
            (?firstconcept, <wn:hyponymOf>, ?hypernym),
            (?secondconcept, <wn:hyponymOf>, ?hypernym),
            (?hypernym, <wn:wordForm>, ?wordform),
            (?hypernym, <wn:glossaryEntry>, ?definition)
            USING
            wn FOR <http://www.cogsci.princeton.edu/~wn/schema/>
            


此处,查询的意思是“查找单词 'panther' 和 'tiger' 所指的概念;查找第三个概念,前两个概念是其下位词;查找第三个概念的可能的单词和词会条目”,如图 4 所示:

图 4. 清单 8 中 WHERE 子句匹配的图形

wordform 和 definition 都在 SELECT 子句中声明,所以它们都是输出。尽管词查询仅匹配了一个 WordNet 概念,查询的图形可以以两种方式匹配,因为该概念有两个不同的单词形式:

wordform  | definition
            =====================================================================================
            "big cat" | "any of several large cats typically able to roar and living in the wild"
            "cat"     | "any of several large cats typically able to roar and living in the wild"
            


回页首

使用 Jena 中的 RDQL

Jena 的 com.hp.hpl.jena.rdql 包包含在 Java 代码中使用 RDQL 所需的所有类和接口。要创建 RDQL 查询,将 RDQL 放入 String 中,并将其传送给 Query 的构造函数。通常直接设置模型用作查询的源,除非在 RDQL 中使用 FROM 子句指定了其他的源。一旦创建了 Query ,可以从它创建 QueryEngine ,然后执行查询。清单 9 中说明了此过程:

清单 9. 创建和运行 RDQL 查询
// Create a new query passing a String containing the RDQL to execute
            Query query = new Query(queryString);
            // Set the model to run the query against
            query.setSource(model);
            // Use the query to create a query engine
            QueryEngine qe = new QueryEngine(query);
            // Use the query engine to execute the query
            QueryResults results = qe.exec();
            


使用 Query 的一个非常有用的方法是在执行之前将它的一些变量设置为固定值。这种使用模式与 javax.sql.PreparedStatement 的相似。变量通过 ResultBinding 对象与值绑定,执行时该对象会传送给 QueryEngine 。可以将变量与 Jena Resource 或与常量值绑定。在将常量与变量绑定之前,通过调用 Model.createLiteral 将其打包。清单 10 说明了预先绑定方法:

清单 10. 将查询变量与值绑定
// Create a query that has variables x and y
            Query query = new Query(queryString);
            // A ResultBinding specifies mappings between query variables and values
            ResultBinding initialBinding = new ResultBinding() ;
            // Bind the query's first variable to a resource
            Resource someResource = getSomeResource();
            initialBinding.add("x", someResource);
            // Bind the query's second variable to a literal value
            RDFNode foo = model.createLiteral("bar");
            initialBinding.add("y", foo);
            // Execute the query with the specified values for x and y
            QueryEngine qe = new QueryEngine(query);
            QueryResults results = qe.exec(initialBinding);
            


QueryEngine.exec() 返回的 QueryResults 对象执行 java.util.Iterator 。 next() 方法返回 ResultBinding 对象。查询中使用的所有变量都可以凭名称通过 ResultBinding 获得,而不管它们是否是 SELECT 子句的一部分。清单 11 显示了如何进行此操作,仍使用 清单 6中的 RDQL 查询:

清单 11. 查找“domestic dog”的 WordNet 词汇条目的 RDQL 查询
SELECT
            ?definition
            WHERE
            (?concept, <wn:wordForm>, "domestic dog"),
            (?concept, <wn:glossaryEntry>, ?definition)
            USING
            wn FOR <http://www.cogsci.princeton.edu/~wn/schema/>";
            


运行此查询获得的 ResultBinding 如期望的那样包含常量词汇条目。另外,还可以访问变量 concept 。变量通过调用 ResultBinding.get() 凭名称获得。通过此方法返回的所有变量都可以转换成 RDFNode ,如果您想将这些变量绑定回更进一步的 RDQL 查询,这将非常有用。

这种情况下, concept 变量表示 RDF 资源,所以从 ResultBinding.get() 获得的 Object 可以转换成 Resource 。然后可以调用 Resource 的查询方法来进一步探查这部分模型,如清单 12 中所示:

清单 12. 使用查询结果
// Execute a query
            QueryResults results = qe.exec();
            // Loop over the results
            while (results.hasNext()) {
            ResultBinding binding = (ResultBinding)results.next();
            // Print the literal value of the "definition" variable
            RDFNode definition = (RDFNode) binding.get("definition");
            System.out.println(definition.toString());
            // Get the RDF resource used in the query
            Resource concept = (Resource)binding.get("concept");
            // Query the concept directly to find other wordforms it has
            List wordforms = concept.listObjectsOfProperty(wordForm);
            }
            


程序源码下载中包含的程序 FindHypernym.java(请参阅 参考资料)汇总了您这里研究的区域。它查找命令行上给定单词的上位词,清单 13 中显示了使用的查询:

清单 13. 查找概念的上位词的单词形式和词汇条目的 RDQL 查询
SELECT
            ?hypernym, ?definition
            WHERE
            (?firstconcept, <wn:wordForm>, ?hyponym),
            (?firstconcept, <wn:hyponymOf>, ?secondconcept),
            (?secondconcept, <wn:wordForm>, ?hypernym),
            (?secondconcept, <wn:glossaryEntry>, ?definition)
            USING
            wn FOR <http://www.cogsci.princeton.edu/~wn/schema/>
            


命令行上给出的单词与 hyponym 词语绑定,查询查找该单词表示的概念,查找第二个概念(第一个概念是它的下位词),然后输出第二个概念的单词形式和定义。清单 14 显示了输出:

清单 14. 运行示例 FindHypernym 程序
$ java FindHypernym "wisteria"
            Hypernyms found for 'wisteria':
            vine:weak-stemmed plant that derives support from climbing, twining,
            or creeping along a surface


回页首

使用 OWL 添加意义

您可能想知道为什么“wisteria”的上位词搜索仅返回它的直接上位词“vine”。如果从植物学观点,您可能还希望显示“traceophyte”也显示为上位词,以及“plant”。实际上,WordNet 模型表明“wisteria”是“vine”的下位词,“vine”是“traceophyte”的下位词。直观地,您知道“wisteria”因此是“traceophyte”的下位词,因为您知道“hyponym of”关系是 可传递的。所以您需要有一种方法将这种认识合并到 FindHypernym 程序中,从而产生了 OWL。

可传递关系

关系对于三个元素 a、 b和 c是可传递的,从而 a和 b之间及 b和 c之间存在关系意味着 a和 c之间存在关系。

可传递关系的一个示例是“大于”关系。如果 a大于 b, b大于 c,因而 a肯定大于 c。


Web Ontology Language 或 OWL 是 W3C 推荐标准,设计用来“明确表示词汇表中词语的意义以及那些词语之间的关系”。与 RDF Schema 一起,OWL 提供了一种正式地描述 RDF 模型的机制。除了定义资源可以属于的层次结构类,OWL 还允许表达资源的属性特征。例如,在 清单 1 中使用的 Relationship 词汇表中,可以使用 OWL 说明 childOf 属性与 parentOf 属性相反。另一个示例说明 WordNet 词汇表的 hyponymOf 属性是可传递的。

在 Jena 中,本体被看作一种特殊类型的 RDF 模型 OntModel 。此接口允许程序化地对本地进行操作,使用便利方法创建类、属性限制等等。备选方法将本体看作特殊 RDF 模型,仅添加定义其语义规则的语句。清单 15 中说明了这些技术。注意还可以将本体语句添加到现有数据模型中,或使用 Model.union() 将本体模型与数据模型合并。

清单 15. 创建 WordNet 的 OWL 本体模型
// Make a new model to act as an OWL ontology for WordNet
            OntModel wnOntology = ModelFactory.createOntologyModel();
            // Use OntModel's convenience method to describe
            // WordNet's hyponymOf property as transitive
            wnOntology.createTransitiveProperty(WordnetVocab.hyponymOf.getURI());
            // Alternatively, just add a statement to the underlying model to express that
            // hyponymOf is of type TransitiveProperty
            wnOntology.add(WordnetVocab.hyponymOf, RDF.type, OWL.TransitiveProperty);


回页首

使用 Jena 推理

给定了本体和模型后,Jena 的推理引擎可以派生模型未明确表达的其他语句。Jena 提供了多个 Reasoner 类型来使用不同类型的本体。因为要将 OWL 本体与 WordNet 模型一起使用,所以需要 OWLReasoner 。

下例显示了如何将 OWL WordNet 本体应用到 WordNet 模型自身以创建推理模型。这里我实际将使用 WordNet 模型的子集,仅包含下位词层次结构中“plant life”之下的那些名词。仅使用子集的原因是推理模型需要保存在内存中,WordNet 模型对于内存模型过大而不能实现。我用来从整个 WordNet 模型中提取 plants 模型的代码包含在文章来源中,名为 ExtractPlants.java(请参阅 参考资料)。

首先我从 ReasonerRegistry 中获得 OWLReasoner 。 ReasonerRegistry.getOWLReasoner() 在它的标准配置中返回 OWL reasoner,这对于此简单情况已经足够。下一步是将 reasoner 与 WordNet 本体绑定。此操作返回可以应用本体规则的 reasoner。然后,将使用绑定的 reasoner 从 WordNet 模型创建 InfModel 。

从原始数据和 OWL 本体创建了推理模型后,它就可以像任何其他 Model 实例一样进行处理。因此,如清单 16 所示,通过 FindHypernym.java 与正常 Jena 模型一起使用的 Java 代码和 RDQL 查询可以重新应用到推理模型,而不进行任何更改:

清单 16. 创建和查询推理模型
// Get a reference to the WordNet plants model
            ModelMaker maker = ModelFactory.createModelRDBMaker(connection);
            Model model = maker.openModel("wordnet-plants",true);
            // Create an OWL reasoner
            Reasoner owlReasoner = ReasonerRegistry.getOWLReasoner();
            // Bind the reasoner to the WordNet ontology model
            Reasoner wnReasoner = owlReasoner.bindSchema(wnOntology);
            // Use the reasoner to create an inference model
            InfModel infModel = ModelFactory.createInfModel(wnReasoner, model);
            // Set the inference model as the source of the
            query query.setSource(infModel);
            // Execute the query as normal
            QueryEngine qe = new QueryEngine(query);
            QueryResults results = qe.exec(initialBinding);
            


文章来源中有完整清单,名为 FindInferredHypernyms.java。清单 17 显示了当对推理模型查询“wisteria”的上位词时的结果:

清单 17. 运行示例 FindInferredHypernyms 程序
$ java FindInferredHypernyms wisteria
            Hypernyms found for 'wisteria':
            vine:weak-stemmed plant that derives support from climbing, twining, or creeping along a surface
            tracheophyte:green plant having a vascular system:ferns, gymnosperms, angiosperms
            vascular plant:green plant having a vascular system:ferns, gymnosperms, angiosperms
            plant life:a living organism lacking the power of locomotion
            flora:a living organism lacking the power of locomotion
            plant:a living organism lacking the power of locomotion
            


OWL 本体中包含的信息已经使 Jena 可以推断“wisteria”在模型中有上位词。


回页首

结束语

本文说明了 Jena Semantic Web Toolkit 的一些最重要的功能,并用示例说明了如何创建、导入和持久化 RDF 模型。您已经了解了查询模型的不同方法,并看到了如何使用 RDQL 简明地表达任意查询。另外,您还了解了如何使用 Jena 的推理引擎对基于本体的模型进行推理。

本文中的示例已经说明了将数据表示为 RDF 模型的一些效果,以及 RDQL 从这些模型中提取数据的灵活性。当在您自己的 Java 应用程序中使用 RDF 模型时,这里说明的基本方法将是非常有用的起点。

Jena 是综合的 RDF 工具集,它的功能远不止您这里了解的这些。Jena 项目的主页是开始学习其功能的好地方。


--  作者:ejun_2006
--  发布时间:10/11/2007 9:26:00 PM

--  
恩 是呀 不错多谢了!!!
--  作者:shaoguow
--  发布时间:10/12/2007 4:15:00 PM

--  
赞一个
--  作者:starrainlove
--  发布时间:10/13/2007 3:07:00 PM

--  
非常感谢哈
--  作者:todaysgp
--  发布时间:10/21/2007 11:48:00 PM

--  
我记下了!谢谢,洗完以后都提供这样的宝贵资料!
谢谢了!
--  作者:xiemengjun
--  发布时间:10/22/2007 1:40:00 AM

--  
taihaole
--  作者:clandy
--  发布时间:10/23/2007 2:04:00 AM

--  
好贴!学习ing
--  作者:30983638
--  发布时间:11/24/2007 3:25:00 PM

--  
翻译得不错

--  作者:gazi945
--  发布时间:11/25/2007 8:22:00 PM

--  
实在是好东西
--  作者:chairmanwei
--  发布时间:11/25/2007 9:13:00 PM

--  
赞楼主一个先~
不过这篇相对简单,不翻译无所谓,呵呵。
要是把推理的api翻译出来,那就好了,嘿嘿~~
楼主不是对你的工作不敬啊~
--  作者:supergg
--  发布时间:11/27/2007 10:24:00 AM

--  

--  作者:delin
--  发布时间:12/20/2007 6:22:00 PM

--  
赞一个!正在学习中!
--  作者:廋鹤一只
--  发布时间:12/23/2007 10:02:00 PM

--  
thanks
--  作者:myldb_sw
--  发布时间:1/14/2008 9:10:00 PM

--  
非常谢谢,正准备学习如何使用Jena
--  作者:guffey
--  发布时间:2/7/2008 9:08:00 PM

--  
55楼,现在的jena版本好象改用sparql了,这几个例子怎么用sparql改写一下呢?
--  作者:guffey
--  发布时间:2/7/2008 10:34:00 PM

--  
import java.util.ArrayList;
import java.util.List;

import com.hp.hpl.jena.db.*;
import com.hp.hpl.jena.graph.Node;
import com.hp.hpl.jena.graph.Triple;
import com.hp.hpl.jena.query.Query;
import com.hp.hpl.jena.query.QueryExecution;
import com.hp.hpl.jena.query.QueryExecutionFactory;
import com.hp.hpl.jena.query.QueryFactory;
import com.hp.hpl.jena.query.QuerySolutionMap;
import com.hp.hpl.jena.query.ResultSet;
import com.hp.hpl.jena.query.ResultSetFormatter;
import com.hp.hpl.jena.rdf.model.Model;
import com.hp.hpl.jena.rdf.model.RDFNode;
import com.hp.hpl.jena.sparql.algebra.Algebra;
import com.hp.hpl.jena.sparql.algebra.Op;
import com.hp.hpl.jena.sparql.algebra.op.OpBGP;
import com.hp.hpl.jena.sparql.core.BasicPattern;
import com.hp.hpl.jena.sparql.core.Var;
import com.hp.hpl.jena.sparql.engine.QueryIterator;
import com.hp.hpl.jena.sparql.engine.ResultSetStream;
import com.hp.hpl.jena.sparql.engine.binding.Binding;
import com.hp.hpl.jena.sparql.util.FmtUtils;
import com.hp.hpl.jena.sparql.util.IndentedWriter;

/**
* Executes an RDQL query to find the hypernym of a given word
*/
public class FindHypernym {

 /** MySQL driver classname */
 private static final String mysqlDriver = "com.mysql.jdbc.Driver";

 /** URL of database to use */
 private static final String DB_URL = "jdbc:mysql://localhost/jena";
 private static final String DB_TYPE = "MySQL";

 /** User credentials */
 private static final String DB_USER = "user";
 private static final String DB_PASSWORD = "password";

 /** Name of the Jena model to create */
 private static final String MODEL_NAME = "wordnet";

 static public final String NL = System.getProperty("line.separator");

 /**
  * Finds the hypernym of a word given on the commandline
  */
 private static void printUsage() {
  System.out.print("\nUsage:\n");
  System.out.print("\tFindHypernym <hyponym>\n\n");
  System.out.print("\thyponym\t- The word to find hypernyms for\n");
 }
 public static void doSearch(Model m,String s){
  String BASE = "http://www.cogsci.princeton.edu/~wn/schema/" ;
  //Var var_hyponym = Var.alloc("hyponym") ;
        Var var_firstconcept = Var.alloc("firstconcept") ;
        Var var_hypernym = Var.alloc("hypernym");
        Var var_secondconcept = Var.alloc("secondconcept");
        Var var_definition = Var.alloc("definition");
        
        //WorkingVar wvar_hyponym = new WorkingVar();
  //wvar_hyponym.setString(s);
  Var var_hyponym=Var.alloc("hyponym");
  System.out.println(var_hyponym.toString());
        BasicPattern bp = new BasicPattern() ;
        bp.add(new Triple(var_firstconcept, Node.createURI(BASE+"wordForm"), var_hyponym)) ;
        bp.add(new Triple(var_firstconcept, Node.createURI(BASE+"hyponymOf"), var_secondconcept)) ;
        bp.add(new Triple(var_secondconcept, Node.createURI(BASE+"wordForm"), var_hypernym)) ;
        bp.add(new Triple(var_secondconcept, Node.createURI(BASE+"glossaryEntry"), var_definition)) ;
        
        Op op = new OpBGP(bp) ;
        m.write(System.out, "TTL") ;
        System.out.println("--------------") ;
        System.out.print(op) ;
        System.out.println("--------------") ;

        // ---- Execute expression
        QueryIterator qIter = Algebra.exec(op, m.getGraph()) ;
        
        // -------- Either read the query iterator directly ...
        if ( false )
        {
            for ( ; qIter.hasNext() ; )
            {
                Binding b = qIter.nextBinding() ;
                Node n = b.get(var_hypernym) ;
                System.out.println(FmtUtils.stringForNode(n)) ;
                Node n1 = b.get(var_definition) ;
                System.out.println(FmtUtils.stringForNode(n1)) ;
                System.out.println(b) ;
            }
            qIter.close() ;
        }
        else
        {
            // -------- Or make ResultSet from it (but not both - reading an
            //          iterator consumes the current solution)
            List varNames = new ArrayList() ;
            varNames.add("hypernym") ;
            varNames.add("definition") ;
            ResultSet rs = new ResultSetStream(varNames, m, qIter);
            ResultSetFormatter.out(rs) ;
            qIter.close() ;
        }
        //System.exit(0) ;
 }
 public static void main(String args[]) {

  // Check that the user provided a single argument
  if (args.length != 1) {
   printUsage();
   System.exit(-1);
  }

  try {
   // Instantiate database driver
   Class.forName(mysqlDriver);
  } catch (ClassNotFoundException e) {
   System.err.println("MySQL driver class not found");
   System.exit(-1);
  }

  // Get a connection to the db
  DBConnection connection = new DBConnection(DB_URL, DB_USER,
    DB_PASSWORD, DB_TYPE);

  // Get hold of the existing wordnet model
  ModelRDB model = ModelRDB.open(connection, MODEL_NAME);
  
  String prolog = "PREFIX wn: <http://www.cogsci.princeton.edu/~wn/schema/>";
  // Create a query
  
  String queryString = prolog + NL + "SELECT ?hypernym ?definition "
    + "WHERE {?firstconcept wn:wordForm ?hyponym. "
    + "?firstconcept wn:hyponymOf ?secondconcept. "
    + "?secondconcept wn:wordForm ?hypernym. "
    + "?secondconcept wn:glossaryEntry ?definition.} ";
  
  Query query = QueryFactory.create(queryString);
  query.serialize(new IndentedWriter(System.out, true));
  System.out.println();
  
  RDFNode hyponym = model.createLiteral(args[0]);
  //store the node in a QuerySolutionMap
  QuerySolutionMap initialBindings = new QuerySolutionMap();
  initialBindings.add("hyponym", hyponym);
   
  QueryExecution qexec = QueryExecutionFactory.create(query, model,initialBindings);
  ResultSet results = qexec.execSelect();
  // Output the results
  System.out.println("Hypernyms found for '" + args[0] + "':");
  System.out.println();
  ResultSetFormatter.out(System.out, results, query);
  qexec.close();
  System.out.println("========================");
  //doSearch(model,args[0]);
  try {
   // Close the database connection
   connection.close();
  } catch (java.sql.SQLException e) {
  }
 }

 // results.close() ;
 /* Property formword=model.CreateResouces()
 /*
  * ResIterator iter = model.listSubjectsWithProperty(); if (iter.hasNext()) {
  * System.out.println("The database contains vcards for:"); while
  * (iter.hasNext()) { System.out.println(" " + iter.nextStatement()
  * .getProperty(VCARD.FN) .getString()); } } else { System.out.println("No
  * vcards were found in the database"); }
  */
}

运行结果:
  1 PREFIX  wn:   <http://www.cogsci.princeton.edu/~wn/schema/>
  2
  3 SELECT  ?hypernym ?definition
  4 WHERE
  5   { ?firstconcept
  6               wn:wordForm       ?hyponym ;
  7               wn:hyponymOf      ?secondconcept .
  8     ?secondconcept
  9               wn:wordForm       ?hypernym ;
10               wn:glossaryEntry  ?definition .
11   }

Hypernyms found for 'tiger':

--------------------------------------------------------------------------------------------
| hypernym     | definition                                                                |
============================================================================================
| "big cat"    | "any of several large cats typically able to roar and living in the wild" |
| "cat"        | "any of several large cats typically able to roar and living in the wild" |
| "soul"       | "a human being; \"there was too much for one person to do\""              |
| "human"      | "a human being; \"there was too much for one person to do\""              |
| "individual" | "a human being; \"there was too much for one person to do\""              |
| "mortal"     | "a human being; \"there was too much for one person to do\""              |
| "person"     | "a human being; \"there was too much for one person to do\""              |
| "somebody"   | "a human being; \"there was too much for one person to do\""              |
| "someone"    | "a human being; \"there was too much for one person to do\""              |
--------------------------------------------------------------------------------------------
嗯,好象是运行成功了,不明白preson,somebody怎么也是上位词了呢。
还有根据Algebra例子写的一个doSearch方法没完成,请高手指点一下,谢谢!


--  作者:xiawared
--  发布时间:4/7/2008 3:33:00 PM

--  
学习中。。。
--  作者:dasotkb
--  发布时间:4/23/2008 2:36:00 PM

--  
thanks ...... good poster ~
--  作者:hyue2009
--  发布时间:4/27/2008 1:39:00 AM

--  
谢谢!
--  作者:ganggao
--  发布时间:6/26/2008 12:16:00 PM

--  
真的很好,非常感谢!!!
--  作者:jiangyue0011
--  发布时间:7/1/2008 3:38:00 PM

--  
支持外文资料的本土化进程!呵呵!楼主很强!
--  作者:mrcold
--  发布时间:7/26/2008 10:19:00 AM

--  
不错 收藏了
--  作者:lassiefly
--  发布时间:10/28/2008 4:01:00 PM

--  
多谢!
--  作者:Humphrey
--  发布时间:10/29/2008 10:17:00 AM

--  
姐姐果然不一般,感谢您为我们初学者提供易于接受的中文译文!
--  作者:hanfei813
--  发布时间:2/23/2009 1:36:00 PM

--  
非常感谢

--  作者:lipeiqiang1997
--  发布时间:3/10/2009 6:06:00 PM

--  
你能不能把Jena API文档给我发一份。从网上下载不下来,我还是个新手,谢谢
--  作者:lipeiqiang1997
--  发布时间:3/10/2009 6:07:00 PM

--  
我的邮箱是lipeiqiang84@163.com
--  作者:lipeiqiang1997
--  发布时间:3/17/2009 4:29:00 PM

--  
我想问一下Jena Tutorial的代码从哪里找啊,怎么我在HP的网站上找不到相关的资料那?哪位手里有的话可不可以给我发一份啊,或者上传到这个网站上,我的邮箱是     lipeiqiang84@163.com              谢谢了!!!
--  作者:lipeiqiang1997
--  发布时间:3/17/2009 4:29:00 PM

--  
我想问一下Jena Tutorial的代码从哪里找啊,怎么我在HP的网站上找不到相关的资料那?哪位手里有的话可不可以给我发一份啊,或者上传到这个网站上,我的邮箱是     lipeiqiang84@163.com              谢谢了!!!
--  作者:zym88
--  发布时间:5/7/2009 11:04:00 AM

--  赞!
多谢!辛苦了!
--  作者:yinshiwei
--  发布时间:3/7/2011 5:41:00 PM

--  
太感谢了
--  作者:zhenzixiong
--  发布时间:3/22/2011 4:46:00 PM

--  
崇拜得不行了
--  作者:cpahuangyg
--  发布时间:9/26/2012 1:12:00 PM

--  
感谢!
--  作者:廖娟娟
--  发布时间:5/27/2015 9:07:00 PM

--  
谢谢分享
W 3 C h i n a ( since 2003 ) 旗 下 站 点
苏ICP备05006046号《全国人大常委会关于维护互联网安全的决定》《计算机信息网络国际联网安全保护管理办法》
22,201.170ms