RDF和Jena RDF API入门(2)

 

陈述

RDF模型中的每一个箭头表示为一个陈述(statement)。每一个陈述声明了关于某个资源的某个事实。一个陈述由三部分组成。

主体,也就是箭头的出发的资源。
谓词,也就是标识箭头的属性。
客体,也就是箭头所指向的那个资源或文本。

一个陈述有时也叫做一个三元组的原因就是它由三部分组成。

一个RDF模型(译者注: 指Jena中的接口Model)是由一组陈述所组成的。在Tutorial2中,每调用一次addProperty函数就会在模型中增加另一个陈述。(因为一个模型是由一组陈述组成的,所以增加一个重复的陈述并不会产生任何意义。)Jena模型接口定义了一个 listStatements()方法,此方法会返回一个StmtIterator类型的变量。StmtItor是Java中Iterator的一个子类型,这个StmtIterator变量重复迭代了该接口模型中的所有陈述。StmtIterator类型中有一个方法nextStatement (),该方法会从iterator返回下一个陈述,(就和next()返回的一样,但是已将其映射为Statement类型)。接口 Statement提供了访问陈述中主体,谓词和客体的方法。

现在我们会用使用那个接口来扩展Tutorial2,使起列出所有的创建的陈述并将它们打印出来。此例完整的代码可以在Tutorial3中找到。

// java code
// 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(
" .");
}

#jruby code
require 'java'

module Java
    include_package 
'com.hp.hpl.jena.rdf.model'
    include_package 
'com.hp.hpl.jena.vocabulary'
   
    PersonURI 
= "http://somewhere/JohnSmith"
    GivenName 
= "John"
    FamilyName 
= "Smith"
    FullName 
= GivenName + " " + FamilyName
   
    model 
= Java::ModelFactory.createDefaultModel
   
    john_smith 
= \
        model
.createResource(PersonURI)\
            
.addProperty(VCARD::FN, FullName)\
            
.addProperty(VCARD::N, \
                model
.createResource()\
                
.addProperty(VCARD::Given, GivenName)\
                
.addProperty(VCARD::Family, FamilyName))
   
    iter 
= model.listStatements
   
    
while iter.hasNext
        stmt 
= iter.nextStatement
        subject 
= stmt.getSubject
        predicate 
= stmt.getPredicate
        
object = stmt.getObject
       
        
print subject.toString
        
print " " + predicate.toString + " "
        
if object.kind_of? Java::Resource
            
print object.toString
        
else
            
print "\"" + object.toString + "\""
        
end
        puts 
" ."
    
end
end

因为一个陈述的客体可以是一个资源也可以是一个文本。getObject()方法会返回一个类型为RDFNode的客体,RDFNode是Resource和Literal类共同的超类。为了确定本例中的客体确切的类型,代码中使用 instanceof(jruby中使用Object#kind_of?)来确定其类型和相应的处理。运行后,此程序回产生与此相似的输出:

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的参数。

// java code
// now write the model in XML form to a file
model.write(System.out);

#jruby code
require 'java'

module Java
    include_package 
'com.hp.hpl.jena.rdf.model'
    include_package 
'com.hp.hpl.jena.vocabulary'
   
    PersonURI 
= "http://somewhere/JohnSmith"
    GivenName 
= "John"
    FamilyName 
= "Smith"
    FullName 
= GivenName + " " + FamilyName
   
    model 
= Java::ModelFactory.createDefaultModel
   
    john_smith 
= \
        model
.createResource(PersonURI)\
            
.addProperty(VCARD::FN, FullName)\
            
.addProperty(VCARD::N, \
                model
.createResource()\
                
.addProperty(VCARD::Given, GivenName)\
                
.addProperty(VCARD::Family, FamilyName))
   
    model
.write(java.lang.System.out)
end

应该有类似的输出:

<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:N rdf:nodeID="A0"/>
    
<vcard:FN>John Smith</vcard:FN>
  
</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()方法所调用。

// java code
// now write the model in XML form to a file
model.write(System.out, "RDF/XML-ABBREV");

# jruby code
model.write(java.lang.System.out, 'RDF/XML-ABBREV')

此writer,也就是所谓的PrettyWriter,利用RDF/XML缩写语法把模型写地更为紧凑。它也能保存尽可能保留空白结点。然而,它并不合适来输出大的模型。因为它的性能不可能被人们所接受。要输出大的文件和保留空白结点,可以用N-三元组的形式输出:

// java code
// now write the model in XML form to a file
model.write(System.out, "N-TRIPLE");

# jruby code
model.write(java.lang.System.out, 'N-TRIPLE')

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

读RDF

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

文件“vc-db-1.rdf”可以在“$JenaInstallPath/doc/tutorial/RDF_API/data/”中找到。

// java code
// 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);

# jruby code
require 'java'

module Java
    include_package 
'com.hp.hpl.jena.rdf.model'
    include_package 
'com.hp.hpl.jena.vocabulary'
    include_package 
'com.hp.hpl.jena.util'
    include_package 
'java.io'
   
    InputFileName 
= 'vc-db-1.rdf'
   
    model 
= Java::ModelFactory.createDefaultModel
    input_stream 
= Java::FileManager.get.open InputFileName
    
if input_stream == nil
        raise ArgumentError
, "File:"  + InputFileName + "not found"
    
end
    model
.read input_stream, ''
    model
.write java.lang.System.out
end

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:Given>John</vCard:Given>
    
<vCard:Family>Smith</vCard:Family>
  
</rdf:Description>
  
<rdf:Description rdf:about="http://somewhere/RebeccaSmith/">
    
<vCard:N rdf:nodeID="A1"/>
    
<vCard:FN>Becky Smith</vCard:FN>
  
</rdf:Description>
  
<rdf:Description rdf:nodeID="A2">
    
<vCard:Given>Matthew</vCard:Given>
    
<vCard:Family>Jones</vCard:Family>
  
</rdf:Description>
  
<rdf:Description rdf:nodeID="A3">
    
<vCard:Given>Sarah</vCard:Given>
    
<vCard:Family>Jones</vCard:Family>
  
</rdf:Description>
  
<rdf:Description rdf:about="http://somewhere/MattJones/">
    
<vCard:N rdf:nodeID="A2"/>
    
<vCard:FN>Matt Jones</vCard:FN>
  
</rdf:Description>
  
<rdf:Description rdf:nodeID="A1">
    
<vCard:Given>Rebecca</vCard:Given>
    
<vCard:Family>Smith</vCard:Family>
  
</rdf:Description>
  
<rdf:Description rdf:about="http://somewhere/SarahJones/">
    
<vCard:N rdf:nodeID="A3"/>
    
<vCard:FN>Sarah Jones</vCard:FN>
  
</rdf:Description>
  
<rdf:Description rdf:about="http://somewhere/JohnSmith/">
    
<vCard:N rdf:nodeID="A0"/>
    
<vCard:FN>John Smith</vCard:FN>
  
</rdf:Description>
</rdf:RDF>

posted on 2008-10-08 22:21  Code书生  阅读(2043)  评论(1编辑  收藏  举报

导航