RDF和Jena RDF API入门(5)
容器
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:
// java code
// 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());
}
// 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());
}
# jruby code
smiths = model.createBag
iter = model.listStatements(
Class.new(Java::SimpleSelector) {
def selects s
java.lang.String.new(s.getString).endsWith 'Smith'
end
}.new(nil, VCARD::FN, nil))
while iter.hasNext
smiths.add iter.nextStatement.getSubject
end
smiths = model.createBag
iter = model.listStatements(
Class.new(Java::SimpleSelector) {
def selects s
java.lang.String.new(s.getString).endsWith 'Smith'
end
}.new(nil, VCARD::FN, nil))
while iter.hasNext
smiths.add iter.nextStatement.getSubject
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="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>
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来列举容器的内容:
// java code
// 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");
}
// 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");
}
# jruby code
iter2 = smiths.iterator
if iter2.hasNext
puts 'The bag contains:'
while iter2.hasNext
puts model\
.getResource(iter2.next.getString)\
.getRequiredProperty(VCARD::FN).getString
end
else
puts 'The bag is empty'
end
end
iter2 = smiths.iterator
if iter2.hasNext
puts 'The bag contains:'
while iter2.hasNext
puts model\
.getResource(iter2.next.getString)\
.getRequiredProperty(VCARD::FN).getString
end
else
puts 'The bag is empty'
end
end
并会产生如下的输出:
The bag contains:
John Smith
Becky Smith
这里发生了一些小小的问题。正常来说,jruby是不需要类型转换的。然而这里如果直接使用下面的代码:
# jruby code
puts iter2.next.getRequiredProperty(VCARD::FN).getString
puts iter2.next.getRequiredProperty(VCARD::FN).getString
却并不能正确的执行。具体的原因没有弄清楚,只是知道iter2.next返回的对象是com.hp.hpl.jena.rdf.model.impl.LiteralImpl类型的对象,而且并不包含对getRequiredProperty函数的相应。因为这个问题我也找了一些东西,可是没有得到明确的答案,因此使用了一个折衷的办法来实现功能。
本例的可执行代码可以在Tutorial10中找到。
Jena类所提供的操纵容器的方法包括增加新成员,在容器中间插入新成员和删除已有的成员。Jena容器类目前保证所使用的序数属性列表会从rdf:_1开始并且是相邻的。RDF核心工作小组放松了此项限制,以允许有局部的容器表示。所以这是Jena将来可能会修改的地方之一。
关于文本(Literals)和数据类型的更多探讨
RDF 文本(literals)并不仅仅是简单的字符串而已。文本可能有一个语言标签来指示该文本使用的语言。有英语语言标签的文本“chat”会被认为与有着法语语言标签的文本“chat”是不同的。这个奇怪的特性是原有RDF/XML语法产生的赝象(artefact)。
另外,事实上共有两种文本。在一种里,字符串成分只是简单的字符串。而在另一种里,字符串成分被预计为格式良好的XML片段。当一个RDF模型被写成 RDF/XML形式时,一个特殊的使用parseType='Literal'的属性(attribute)构造会被使用来表示它。
在jena中,当一个文本被创建时,这些属性就被设置了。例如,在Tutorial11中:
// java code
// 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);
// 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);
# 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'
model = Java::ModelFactory.createDefaultModel
r = model.createResource
r.addProperty(RDFS::label, model.createLiteral('chat', 'en'))\
.addProperty(RDFS::label, model.createLiteral('chat', 'fr'))\
.addProperty(RDFS::label, model.createLiteral('<em>chat</em>', true))
model.write Java::PrintWriter.new(java.lang.System.out)
puts
model = ModelFactory.createDefaultModel
r = model.createResource
r.addProperty(RDFS::label, '11')\
.addProperty(RDFS::label, 11)
model.write java.lang.System.out, 'N-TRIPLE'
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'
model = Java::ModelFactory.createDefaultModel
r = model.createResource
r.addProperty(RDFS::label, model.createLiteral('chat', 'en'))\
.addProperty(RDFS::label, model.createLiteral('chat', 'fr'))\
.addProperty(RDFS::label, model.createLiteral('<em>chat</em>', true))
model.write Java::PrintWriter.new(java.lang.System.out)
puts
model = ModelFactory.createDefaultModel
r = model.createResource
r.addProperty(RDFS::label, '11')\
.addProperty(RDFS::label, 11)
model.write java.lang.System.out, 'N-TRIPLE'
end
会产生:
<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>
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(…)的调用):
// java code
// 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");
// 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支持那些使用类型文字的机制;但是本教程中不会对此讨论。