本文将对jena 的使用进行简单介绍。部分内容参考了这里 http://jena.apache.org/tutorials/rdf_api.html 。
我们先看下面一个例子。这是一个 people 资源。RDF 中关于人的信息用vcard 来表示比较合适。关于 RDF 中 vCard 的更多内容参考http://www.w3.org/TR/vcard-rdf/。
这个例子中,资源 http://.../JohnSmith 表示一个人。这个人的全名是 John Smith,即 vcard:FN 属性的值是 John Smith。在 Jena 中,资源用 Resource 类来表示,其属性用 Property 类来表示。而整体模型用Model 类来表示,即上图就是一个Model。一个 Model 对象可以包含多个资源。
上面所描述的资源使用 jena 编程表示如下:
import com.hp.hpl.jena.rdf.model.Model;
import com.hp.hpl.jena.rdf.model.ModelFactory;
import com.hp.hpl.jena.rdf.model.Resource;
import com.hp.hpl.jena.vocabulary.VCARD;
public class Introduction {
static String personURI = "http://somewhere/JohnSmith";
static String fullName = "John Smith";
public static void main(String[] args){
// 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 类是一个Model 工厂,用于创建model 对象。我们可以使用 Model 的createResource 方法在model 中创建一个资源,并可以使用资源的 addProperty 方法添加属性。
Model 的每个箭头都是一个陈述(Statement)。Statement 由三部分组成,分别是主语、谓语和客体。
下图表示一个Model:
它的每一个箭头都代表一个Statement。如资源http://.../JohnSmith 有一个vCard:FN 属性,其值是文本"John Smith ”。这个资源还有一个 vCard:N 属性,这个属性的值是另一个无名资源。该无名资源有两个属性,分别是 vCard:Given 和 vCard:Family。其值分别是文本的"John" 和 "Smith"。
我们可以用Jena API 来解析这个RDF 的Statement:
import com.hp.hpl.jena.rdf.model.Model;
import com.hp.hpl.jena.rdf.model.ModelFactory;
import com.hp.hpl.jena.rdf.model.Property;
import com.hp.hpl.jena.rdf.model.RDFNode;
import com.hp.hpl.jena.rdf.model.Resource;
import com.hp.hpl.jena.rdf.model.Statement;
import com.hp.hpl.jena.rdf.model.StmtIterator;
import com.hp.hpl.jena.vocabulary.VCARD;
public class StatementDemo {
public static void main(String[] args){
//Introduction
String personURI = "http://somewhere/JohnSmith";
String givenName = "John";
String familyName = "Smith";
String fullName = givenName + " " + familyName;
Model model = ModelFactory.createDefaultModel();
Resource johnSmith = model.createResource(personURI);
johnSmith.addProperty(VCARD.FN, fullName);
johnSmith.addProperty(VCARD.N,
model.createResource()
.addProperty(VCARD.Given, givenName)
.addProperty(VCARD.Family, familyName));
//Statement
StmtIterator iter = model.listStatements();
while(iter.hasNext()){
Statement stmt = iter.nextStatement();
Resource subject = stmt.getSubject();
Property predicate = stmt.getPredicate();
RDFNode object = stmt.getObject();
System.out.print(subject.toString());
System.out.print(" "+predicate.toString());
if(object instanceof Resource){
System.out.print(object.toString());
}else{
System.out.print("\"" + object.toString() + "\"");
}
System.out.println(" .");
}
}
}
Model 类的listStatements 将返回一个 Statement 的Iterator。Statement 有的主语、谓语、客体分别用 getSubject、getPredicate、getObject 来返回。其类型分别是 Resource、Property和RDFNode。其中客体 object 类型可以是Resource 或者文本。
该程序的输出如下:
http://somewhere/JohnSmith http://www.w3.org/2001/vcard-rdf/3.0#N-1e19b4fe:13bd0803952:-7fff .
http://somewhere/JohnSmith http://www.w3.org/2001/vcard-rdf/3.0#FN"John Smith" .
-1e19b4fe:13bd0803952:-7fff http://www.w3.org/2001/vcard-rdf/3.0#Family"Smith" .
-1e19b4fe:13bd0803952:-7fff http://www.w3.org/2001/vcard-rdf/3.0#Given"John" .
这四条分别代表了四个Statement,也即上面图中的四个箭头。
需要注意的是,这里有一个资源我们没有指定资源名。
我们看下面的例子:
import com.hp.hpl.jena.rdf.model.Model;
import com.hp.hpl.jena.rdf.model.ModelFactory;
import com.hp.hpl.jena.rdf.model.Resource;
import com.hp.hpl.jena.vocabulary.VCARD;
public class RDFWriting {
public static void main(String[] args){
//Introduction
String personURI = "http://somewhere/JohnSmith";
String givenName = "John";
String familyName = "Smith";
String fullName = givenName + " " + familyName;
Model model = ModelFactory.createDefaultModel();
Resource johnSmith = model.createResource(personURI);
johnSmith.addProperty(VCARD.FN, fullName);
johnSmith.addProperty(VCARD.N,
model.createResource()
.addProperty(VCARD.Given, givenName)
.addProperty(VCARD.Family, familyName));
//Model write
model.write(System.out);
System.out.println();
model.write(System.out, "RDF/XML-ABBREV");
System.out.println();
model.write(System.out, "N-TRIPLE");
}
}
Model 同第三部分图示中所述一样。我们可以通过 Model 的write 方法将其model 中内容写入一个输出流。本例的输出为:
John Smith
Smith
John
Smith
John
John Smith
_:AX2dX498ae941X3aX13bd08e9fe5X3aXX2dX7fff .
"John Smith" .
_:AX2dX498ae941X3aX13bd08e9fe5X3aXX2dX7fff "Smith" .
_:AX2dX498ae941X3aX13bd08e9fe5X3aXX2dX7fff "John" .
可以看出,model.write(OutputStream),model.write(OutputStream, ”RDF/XML-ABBREV"),model.write(OutputStream, "N-TRIPLE") 分别输出了不同格式的内容。
我们有如下一个rdf 文件
resources.rdf:
Smith
John
John Smith
Sarah Jones
Matt Jones
Smith
Rebecca
Jones
Sarah
Jones
Matthew
Becky Smith
它包含有四个People 资源。下面的程序将读取该rdf 文件并再将内容输出:
import java.io.InputStream;
import com.hp.hpl.jena.rdf.model.Model;
import com.hp.hpl.jena.rdf.model.ModelFactory;
import com.hp.hpl.jena.util.FileManager;
public class RDFReading {
public static String inputFileName = "resources.rdf";
public static void main(String[] args){
Model model = ModelFactory.createDefaultModel();
// 使用 FileManager 查找文件
InputStream in = FileManager.get().open( inputFileName );
if (in == null) {
throw new IllegalArgumentException(
"File: " + inputFileName + " not found");
}
// 读取RDF/XML 文件
model.read(in, null);
model.write(System.out);
}
}
Model 的read 方法可以读取RDF 输入到model 中。第二个参数可以指定格式。
我们看下面这个例子:
import com.hp.hpl.jena.rdf.model.Model;
import com.hp.hpl.jena.rdf.model.ModelFactory;
import com.hp.hpl.jena.rdf.model.Property;
import com.hp.hpl.jena.rdf.model.Resource;
public class NSPrefix {
public static void main(String[] args){
Model m = ModelFactory.createDefaultModel();
String nsA = "http://somewhere/else#";
String nsB = "http://nowhere/else#";
//创建Resource 和 Property
Resource root = m.createResource( nsA + "root" );
Property P = m.createProperty( nsA + "P" );
Property Q = m.createProperty( nsB + "Q" );
Resource x = m.createResource( nsA + "x" );
Resource y = m.createResource( nsA + "y" );
Resource z = m.createResource( nsA + "z" );
//层叠增加三个Statement
m.add( root, P, x ).add( root, P, y ).add( y, Q, z );
System.out.println( "# -- no special prefixes defined" );
m.write( System.out );
System.out.println( "# -- nsA defined" );
//设置Namespace nsA 的前缀为“nsA”
m.setNsPrefix( "nsA", nsA );
m.write( System.out );
System.out.println( "# -- nsA and cat defined" );
//设置Namespace nsB 的前缀为“cat”
m.setNsPrefix( "cat", nsB );
m.write( System.out );
}
}
该程序首先调用 Model 的createProperty 和createResource 生成属性和资源。然后调用Model.add 想model 中增加3个Statement。add 的三个参数分别是三元组的主语、谓语和客体。想Model 中增加内容实际上就是增加三元组。
Model 的 setNsPrefix 函数用于设置名字空间前缀。该程序的输出如下:
# -- no special prefixes defined
# -- nsA defined
# -- nsA and cat defined
如果我们没有为RDF 指定namespace 前缀,则jena 会自动为其生成名为 j.0, j.1 的名字空间。
上面介绍了jena 用来创建、读、写 RDF Model,本部分将主要用来访问RDF Model 的信息,对Model 的内容进行操作。
看下面一个例子:
import com.hp.hpl.jena.rdf.model.Model;
import com.hp.hpl.jena.rdf.model.ModelFactory;
import com.hp.hpl.jena.rdf.model.Resource;
import com.hp.hpl.jena.rdf.model.StmtIterator;
import com.hp.hpl.jena.vocabulary.VCARD;
public class ModelAccess {
public static void main(String[] args){
String personURI = "http://somewhere/JohnSmith";
String givenName = "John";
String familyName = "Smith";
String fullName = givenName + " " + familyName;
Model model = ModelFactory.createDefaultModel();
Resource johnSmith = model.createResource(personURI);
johnSmith.addProperty(VCARD.FN, fullName);
johnSmith.addProperty(VCARD.N,
model.createResource()
.addProperty(VCARD.Given, givenName)
.addProperty(VCARD.Family, familyName));
// 从 Model 获取资源
Resource vcard = model.getResource(personURI);
/*
// 获取N 属性的值(用属性的 getObject()方法)
Resource name = (Resource) vcard.getProperty(VCARD.N)
.getObject();
*/
// 如果知道属性的值是资源,可以使用属性的getResource 方法
Resource name = vcard.getProperty(VCARD.N)
.getResource();
// 属性的值若是 literal,则使用 getString 方法
fullName = vcard.getProperty(VCARD.FN)
.getString();
// 增加两个 NICKNAME 属性
vcard.addProperty(VCARD.NICKNAME, "Smithy")
.addProperty(VCARD.NICKNAME, "Adman");
System.out.println("The nicknames of \""
+ fullName + "\" are:");
// 列出两个NICKNAME 属性,使用资源的 listProperties 方法
StmtIterator iter = vcard.listProperties(VCARD.NICKNAME);
while (iter.hasNext()) {
System.out.println(" " + iter.nextStatement()
.getObject()
.toString());
}
}
}
本例子中主要使用了以下内容
Jena 和核心 API 仅支持有限的查询操作。我们这里进行简单介绍。
上面所述的几种查询都是对 Model.listStatements(Selector s) 进行了一些包装得到的。如
下面分别使用两种方式查询具有 fullName 的资源。
1. 使用 Model.listSubjectsWithProperty 查询:
import java.io.InputStream;
import com.hp.hpl.jena.rdf.model.Model;
import com.hp.hpl.jena.rdf.model.ModelFactory;
import com.hp.hpl.jena.rdf.model.ResIterator;
import com.hp.hpl.jena.util.FileManager;
import com.hp.hpl.jena.vocabulary.VCARD;
public class RDFQuery {
public static String inputFileName = "resources.rdf";
public static void main(String[] args){
Model model = ModelFactory.createDefaultModel();
InputStream in = FileManager.get().open( inputFileName );
if (in == null) {
throw new IllegalArgumentException(
"File: " + inputFileName + " not found");
}
model.read(in, null);
//使用 listResourcesWithProperty
ResIterator iter = model.listResourcesWithProperty(VCARD.FN);
if(iter.hasNext()){
System.out.println("The database contains vcard for:");
while(iter.hasNext()){
System.out.println(" "+iter.nextResource().getProperty(VCARD.FN).getString());
}
}else{
System.out.println("No vcards were found in the database");
}
}
}
2. 使用 Selector 查询:
import java.io.InputStream;
import com.hp.hpl.jena.rdf.model.Model;
import com.hp.hpl.jena.rdf.model.ModelFactory;
import com.hp.hpl.jena.rdf.model.RDFNode;
import com.hp.hpl.jena.rdf.model.SimpleSelector;
import com.hp.hpl.jena.rdf.model.StmtIterator;
import com.hp.hpl.jena.util.FileManager;
import com.hp.hpl.jena.vocabulary.VCARD;
public class RDFQuery1 {
public static String inputFileName = "resources.rdf";
public static void main(String[] args){
Model model = ModelFactory.createDefaultModel();
InputStream in = FileManager.get().open( inputFileName );
if (in == null) {
throw new IllegalArgumentException(
"File: " + inputFileName + " not found");
}
model.read(in, null);
//使用 Selector
StmtIterator iter = model.listStatements(new SimpleSelector(null, VCARD.FN, (RDFNode)null));
if(iter.hasNext()){
System.out.println("The database contains vcard for:");
while(iter.hasNext()){
System.out.println(" "+iter.nextStatement().getString());
}
}else{
System.out.println("No vcards were found in the database");
}
}
}
本例中使用resources.rdf 资源。上面两例的输出均为:
The database contains vcard for:
Becky Smith
Matt Jones
Sarah Jones
John Smith
我们知道,对数据库的操作主要包括增、删、改、查等。对RDF 我们同样可以实现这几种操作。查询操作我们已经介绍过,本节将介绍RDF Model 的增删操作。我们可以对一个RDF 增加或者删除 Statement。由于 RDF Model完全是由 Statements 构成的,因此我们可以据此实现资源和属性等的增删。改动操作可以通过删除后再添加来实现。
看下面这个例子:
import com.hp.hpl.jena.rdf.model.Model;
import com.hp.hpl.jena.rdf.model.ModelFactory;
import com.hp.hpl.jena.rdf.model.RDFNode;
import com.hp.hpl.jena.rdf.model.Resource;
import com.hp.hpl.jena.vocabulary.VCARD;
public class AddDelete {
public static void main(String[] args){
String personURI = "http://somewhere/JohnSmith";
String givenName = "John";
String familyName = "Smith";
String fullName = givenName + " " + familyName;
Model model = ModelFactory.createDefaultModel();
Resource johnSmith = model.createResource(personURI);
johnSmith.addProperty(VCARD.FN, fullName);
johnSmith.addProperty(VCARD.N,
model.createResource()
.addProperty(VCARD.Given, givenName)
.addProperty(VCARD.Family, familyName));
System.out.println("原始内容:");
model.write(System.out);
// 删除 Statement
model.remove(model.listStatements(null, VCARD.N, (RDFNode)null));
model.removeAll(null, VCARD.Given, (RDFNode)null);
model.removeAll(null, VCARD.Family, (RDFNode)null);
System.out.println("\n删除后的内容:");
model.write(System.out);
//增加 Statement
model.add(johnSmith, VCARD.N, model.createResource()
.addProperty(VCARD.Given, givenName)
.addProperty(VCARD.Family, familyName));
System.out.println("\n重新增加后的内容:");
model.write(System.out);
}
}
在此例中,我们首先生成一个Model ,然后使用 Model.remove 方法删除几个statement 条目,然后使用Model.add 又增加了回来。
Model.remove 方法可以实现statement 的删除操作,Model.add 可以实现statement 的增加。
除了直接使用 Model 的方法外,对Model 中的Resource(资源)或Property(属性,实际上也继承自Resource)进行增删操作也可以达到更改 Model 的目的。
Model 的合并主要分为 交、并、补三种操作。
如下图所示:
这两个图分别代表一个Model。它们的名字相同,且具有相同的属性 vcard:FN ,值为John Smith。因此,我们对这两个Model 进行“并”(union)操作。所得到的Model 的图形表示如下:
其中重复的 vcard:FN 值只出现一个。
这三种操作的方法分别为:
本文对核心 Jena API 进行了比较全面的介绍。Jena API 是处理RDF 的一个Java 框架。RDF 是用来进行资源描述的。
本文中的所有例子都经过本人调试运行正常。
需要了解 Jena 的更多内容,请参考 http://jena.apache.org/index.html 。
需要了解 RDF 的更多内容,请参考 http://www.w3.org/RDF/ 。另外http://www.w3school.com.cn/rdf/index.asp 是一个简明介绍。