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