mysql jena rdf_RDF和Jena RDF API简介
這是官方文章《An Introduction to RDF and the Jena RDF API》的譯文。原文是在刺猬的溫馴這里看到的。其中的圖片沒法顯示了,還有一段丟失了。于是我在此也補(bǔ)充翻譯一下。^_^
前言
本文是一篇對(duì)W3C的資源描述框架(RDF)和 Jena(一個(gè)Java的RDF API)的教程性介紹. 本文是為那些不熟悉RDF的, 以及那些通過建立原形可以達(dá)到最好學(xué)習(xí)效果的, 或是因?yàn)槠渌蛳M芸焖俨僮鱆ena的程序員而寫的. 我們假設(shè)讀者在閱讀本文前已具有一定的XML和Java知識(shí).
如果讀者在沒有理解RDF數(shù)據(jù)模型的基礎(chǔ)上就迅速進(jìn)入操作階段,往往會(huì)導(dǎo)致失敗和失望. 然而,如果光學(xué)習(xí)數(shù)據(jù)模型又是十分枯燥乏味的, 并常常會(huì)導(dǎo)致曲折的形而上學(xué)的難題. 更好的學(xué)習(xí)辦法是在理解數(shù)據(jù)模型的同時(shí)練習(xí)操作它. 可以先學(xué)習(xí)一點(diǎn)數(shù)據(jù)模型再動(dòng)手試一試.然后在學(xué)習(xí)一點(diǎn)再試一試. 這樣一來就能達(dá)到理論實(shí)踐相結(jié)合的效果.數(shù)據(jù)模型本身十分簡單,所以學(xué)習(xí)過程不會(huì)太長.
RDF具有XML的語法, 所以許多熟悉XML的人就會(huì)認(rèn)為以XML語法的形式來思考RDF. 然而, 這是不對(duì)的. RDF應(yīng)該以它數(shù)據(jù)模型的形式來被理解. RDF數(shù)據(jù)可是用XML來表示, 但是理解數(shù)據(jù)模型的重要性更在理解此語法重要性之上.
Jena API的一個(gè)運(yùn)行例子, 包括本教程中所有例子的工作源代碼都可以在http://www.hpl.hp.com/semweb/下載.
________________________________________
目錄
1. 導(dǎo)言
2. 陳述Statements
3. RDF寫操作
4. RDF讀操作
5. Jena RDF 包
6. 操縱模型
7. 查詢模型
8. 對(duì)模型的操作
9. 容器Containers
10. 關(guān)于Literals和數(shù)據(jù)類型的更多探討
11. 術(shù)語表
________________________________________
導(dǎo)言
資源描述框架是(RDF)是描述資源的一項(xiàng)標(biāo)準(zhǔn)(在技術(shù)上是W3C的推薦標(biāo)準(zhǔn)). 什么是資源? 這實(shí)在是一個(gè)很難回答的問題, 其精確的定義目前尚在爭論中. 出于我們的目的, 我們可以把資源想象成任何我們可以確定識(shí)別的東西. 在本教程中,讀者你本身就是一個(gè)資源, 而你的主頁也是一個(gè)資源, 數(shù)字1和故事中巨大的白鯨都是資源.
在本教程中, 我們的例子會(huì)圍繞人們展開. 假設(shè)人們會(huì)使用VCARDS, 而VCARD將由RDF表示機(jī)制來描述. 我們最好把RDF考慮成由結(jié)點(diǎn)和箭頭的形式構(gòu)成的圖. 一個(gè)簡單的vcard在RDF中可能看起來是這樣的:
資源John Smith在圖中用橢圓表示, 并被一個(gè)統(tǒng)一資源定位符(URI) 所標(biāo)識(shí), 在本例中是"http://.../JohnSmith"). 如果你想要通過你的瀏覽器來訪問這個(gè)資源的話,你很有可能會(huì)失敗. 四月的愚人節(jié)笑話并不經(jīng)得起考驗(yàn), 相反如果你的瀏覽器把John Smith傳遞到你的桌面的話, 你才該感到驚訝. 如果你并不熟悉URI's的話, 你可以把它們想象成簡單的陌生名字.
資源擁有屬性(property). 在這些例子中, 我們對(duì)John Smith名片上出現(xiàn)的那些屬性很感興趣.圖1只顯示了一個(gè)屬性, John Smith的全名. 屬性是由標(biāo)有屬性名的箭頭表示的. 屬性的名字也是一個(gè)URI, 但是由于URI十分冗長笨重, 所以圖中將它顯示為XML qname的形式. 在':'之前的部分稱為命名空間前綴并表示了一個(gè)命名空間. 在':'之后的部分稱為局部名, 并表示在命名空間中的一個(gè)名字. 在寫成RDF XML形式時(shí), 屬性常常以qname的形式表示, 這是一個(gè)在圖形和文本中的簡單的縮寫方法. 然而, 嚴(yán)格地講, 屬性應(yīng)該用URI來標(biāo)識(shí). 命名空間前綴:局部名的形式是一種命名空間連接局部名的URI縮寫. 當(dāng)瀏覽器訪問時(shí), 用并沒有強(qiáng)制屬性的URI必須指向一些具體的事物.
每個(gè)屬性都有一個(gè)值. 在此例中, 值為一個(gè)文本(literal), 我們現(xiàn)在可以把它看成一個(gè)字符串.文本在圖中顯示為長方形.
Jena是一個(gè)Java API, 我們可以用它來創(chuàng)建和操縱諸如上述例圖的RDF圖. Jena設(shè)有表示圖(graph), 資源(resource), 屬性和文本(literal)的對(duì)象類. 表示資源, 屬性和文本的接口分別稱為Resource, Property, 和Literal. 在Jena中, 一個(gè)圖(graph)被稱為一個(gè)模型并被Model接口所表示.
創(chuàng)建上述例圖或稱為上述模型的代碼很簡單:
// 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()方法創(chuàng)建了一個(gè)空的基于內(nèi)存存儲(chǔ)的模型(Model 或 model). Jena還包含了Model接口的其他實(shí)現(xiàn)方式. 例如, 使用關(guān)系數(shù)據(jù)庫的, 這些類型 Model接口也可以從ModelFactory中創(chuàng)建.
于是John Smith這個(gè)資源就被創(chuàng)建了, 并向其添加了一個(gè)屬性. 此屬性由一個(gè)"常" ("constant")類VCARD提供, 這個(gè)類保存了在VCARD模式(schema)中所有定義的表示對(duì)象. Jena也為其他一些著名的模式提供了常類的表示方法, 例如是RDF和RDF模式, Dublin 核心標(biāo)準(zhǔn)和DAML.
創(chuàng)建資源和添加屬性的代碼可以寫成更緊湊的層疊形式:
Resource johnSmith =
model.createResource(personURI)
.addProperty(VCARD.FN, fullName);
這個(gè)例子的工作代碼可以在Jena發(fā)布的材料的教程包中的Tutorial1中找到. 作為練習(xí), 你自己可以獲得此代碼并修改其以創(chuàng)建一個(gè)簡單VCARD.
現(xiàn)在讓我們?yōu)関card再增加一些更詳細(xì)的內(nèi)容, 以便探索更多的RDF和Jena的特性.
在第一個(gè)例子里, 屬性值為一個(gè)文本. 然而RDF屬性也可以采用其他的資源作為其屬性值. 下面這個(gè)例子使用常用的RDF技術(shù)展示了如何表示John Smith名字的不同部分:
在這里我們?cè)黾恿艘粋€(gè)新的屬性, vcard:N, 來表示John Smith名字的結(jié)構(gòu). 這個(gè)模型有幾點(diǎn)有趣之處. 注意屬性vcard:N使用一個(gè)資源作為起屬性值. 同時(shí)注意代表復(fù)合名字的橢圓并沒有URI標(biāo)識(shí). 它被認(rèn)為是一個(gè)空白結(jié)點(diǎn)(blank Node).
創(chuàng)建此例的Jena代碼也十分簡單. 首先是一些聲明和對(duì)空模型的創(chuàng)建.
// 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發(fā)布材料的教程包的Tutorial2中得到.
________________________________________
陳述
RDF模型中的每一個(gè)箭頭表示為一個(gè)陳述(statement). 每一個(gè)陳述聲明了關(guān)于某個(gè)資源的某個(gè)事實(shí). 一個(gè)陳述有三部分組成.
主體, 也就是箭頭的出發(fā)的資源.
謂詞, 也就是標(biāo)識(shí)箭頭的屬性.
客體, 也就是箭頭所指向的那個(gè)資源或文本.
一個(gè)陳述有時(shí)也叫做一個(gè)三元組的原因就是它由三部分組成.
一個(gè)RDF模型(譯者注: 指Jena中的接口Model)是由一組陳述所組成的. 在Tutorial2中, 每調(diào)用一次addProperty函數(shù)就會(huì)在模型中增加另一個(gè)陳述. (因?yàn)橐粋€(gè)模型是由一組陳述組成的, 所以增加一個(gè)重復(fù)的陳述并不會(huì)產(chǎn)生任何意義.) Jena模型接口定義了一個(gè)listStatements()方法, 此方法會(huì)返回一個(gè)StmtIterator類型的變量. StmtItor是Java中Iterator的一個(gè)子類型, 這個(gè)StmtIterator變量重復(fù)迭代了該接口模型中的所有陳述. StmtIterator類型中有一個(gè)方法nextStatement(), 該方法會(huì)從iterator返回下一個(gè)陳述. (就和next()返回的一樣, 但是已將其映射為Statement類型). 接口Statement提供了訪問陳述中主體, 謂詞和客體的方法.
現(xiàn)在我們會(huì)用使用那個(gè)接口來擴(kuò)展Tutorial2, 使起列出所有的創(chuàng)建的陳述并將它們打印出來. 此例完整的代碼可以在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(" .");
}
因?yàn)橐粋€(gè)陳述的客體可以是一個(gè)資源也可以是一個(gè)文本. getObject()方法會(huì)返回一個(gè)類型為RDFNode的客體, RDFNode是Resource和Literal類共同的超類. 為了確定本例中的客體確切的類型, 代碼中使用 instanceof來確定其類型和相應(yīng)的處理.
運(yùn)行后, 此程序回產(chǎn)生與此相似的輸出:
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".
現(xiàn)在你明白了為什么模型構(gòu)建會(huì)更加清晰. 如果你仔細(xì)觀察, 就會(huì)發(fā)現(xiàn)上面每一行都由三個(gè)域組成, 這三個(gè)域分別代表了每一個(gè)陳述的主體, 謂詞和客體. 在此模型中有四個(gè)箭頭, 所以會(huì)有四個(gè)陳述. "anon:14df86:ecc3dee17b:-7fff"是有Jena產(chǎn)生的一個(gè)內(nèi)部標(biāo)識(shí)符, 它不是一個(gè)URI, 也不應(yīng)該與URI混淆. 它只是Jena處理時(shí)使用的一個(gè)內(nèi)部標(biāo)號(hào).
W3C的RDF核心工作小組定義了一個(gè)類似的表示符號(hào)稱為N-三元組(N-Triples). 這個(gè)名字表示會(huì)使用"三元組符號(hào)". 在下一節(jié)中我們會(huì)看到Jena有一個(gè)內(nèi)置的N-三元組寫機(jī)制(writer).
________________________________________
寫RDF
Jena設(shè)有讀寫XML形式的RDF方法. 這些方法可以被用來將一個(gè)RDF模型保存到文件并在日后重新將其讀回.
Tutorial3創(chuàng)建了一個(gè)模型并將其以三元組的形式輸出. Tutorial4對(duì)Tutorial3做了修改, 使其將此模型以RDF XML的形式輸出到標(biāo)準(zhǔn)輸出流中. 這個(gè)代碼依然十分簡單: model.write可以帶一個(gè)OutputStream的參數(shù).
// now write the model in XML form to a file
model.write(System.out);
應(yīng)該有類似的輸出:
xmlns:rdf='http://www.w3.org/1999/02/22-rdf-syntax-ns#'
xmlns:vcard='http://www.w3.org/2001/vcard-rdf/3.0#'
>
Jena有一個(gè)擴(kuò)展的接口, 它允許新的為不同的RDF串行化語言設(shè)計(jì)的writer可以被輕易地插入. 以上的調(diào)用會(huì)激發(fā)一個(gè)標(biāo)準(zhǔn)的'啞'writer方法. Jena也包含了一個(gè)更加復(fù)雜的RDF/XML writer, 它可以被用攜帶另一個(gè)參數(shù)的write()方法所調(diào)用.
// now write the model in XML form to a file
model.write(System.out, "RDF/XML-ABBREV");
此writer, 也就是所謂的PrettyWriter, 利用RDF/XML縮寫語法把模型寫地更為緊湊. 它也能保存盡可能保留空白結(jié)點(diǎn). 然而, 它并不合適來輸出大的模型. 因?yàn)樗男阅懿豢赡鼙蝗藗兯邮? 要輸出大的文件和保留空白結(jié)點(diǎn), 可以用N-三元組的形式輸出:
// now write the model in XML form to a file
model.write(System.out, "N-TRIPLE");
這會(huì)產(chǎn)生類似于Tutorial3的輸出, 此輸出會(huì)遵循N-三元組的規(guī)格.
________________________________________
讀RDF
Tutorial 5 演示了如何將用RDF XML記錄的陳述讀入一個(gè)模型. 在此例中, 我們提供了一個(gè)小型RDF/XML形式的vcard的數(shù)據(jù)庫. 下面代碼會(huì)將其讀入和寫出. 注意: 如果要運(yùn)行這個(gè)小程序, 應(yīng)該把輸入文件放在你的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()方法中的第二個(gè)參數(shù)是一個(gè)URI, 它是被用來解決相對(duì)URI的. 因?yàn)樵跍y試文件中沒有使用相對(duì)URI, 所以它允許被置為空值. 運(yùn)行時(shí), Tutorial5會(huì)產(chǎn)生類似如下的XML輸出
xmlns:rdf='http://www.w3.org/1999/02/22-rdf-syntax-ns#'
xmlns:vcard='http://www.w3.org/2001/vcard-rdf/3.0#'
>
Smith
John
Sarah Jones
Becky Smith
________________________________________
前綴的操作
顯示前綴定義
在前面的章節(jié)中,我們可以看到在輸出XML中聲明了命名空間前綴vcard并且用這個(gè)前綴表示資源的URI縮寫。. 雖然RDF只使用URI的全長拼寫,而不是用它的縮寫形式。JENA卻使用前綴對(duì)應(yīng)(映射)的方式,提供了在輸出時(shí)操作命名空間的寫法。興趣個(gè)簡單的例子。
Model m = ModelFactory.createDefaultModel();
String nsA = "http://somewhere/else#";
String nsB = "http://nowhere/else#";
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" );
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" );
m.setNsPrefix( "nsA", nsA );
m.write( System.out );
System.out.println( "# -- nsA and cat defined" );
m.setNsPrefix( "cat", nsB );
m.write( System.out );
從這段代碼運(yùn)行的結(jié)果產(chǎn)生的三段RDF/XML,由于對(duì)應(yīng)的前綴應(yīng)對(duì)(映射)不同產(chǎn)生的RDF/XML也不同。默認(rèn)的形式顯示在第一段,沒有任何前綴的形式(其實(shí)是系統(tǒng)內(nèi)容的前綴,只是名字不好看)。
# -- no special prefixes defined
xmlns:j.0="http://nowhere/else#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:j.1="http://somewhere/else#" >
我們可以看到這個(gè)RDF命名空間是自動(dòng)聲明的, 因數(shù)它是標(biāo)簽和必需的。?屬性P和Q的使用也需要聲音XML命名空間, 但在這個(gè)例子中,沒有顯示的為模型給定它們的前綴。它們就選擇系統(tǒng)內(nèi)部生成的(虛擬的)命名空間:?j.0?和?j.1。
方法setNsPrefix(String prefix, String URI)聲明了命名空間URI可以由prefix縮寫。JENA需要為它指定的這個(gè)prefix符合XML命名空間名字的規(guī)范,并且URI以非名稱字符結(jié)束。在輸出RDF/XML時(shí),就會(huì)使用這些聲明的前綴來縮寫RDF/XML中的命名空間:
# -- nsA defined
xmlns:j.0="http://nowhere/else#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:nsA="http://somewhere/else#" >
這里可以看到nsA已經(jīng)被用在屬性標(biāo)簽中了,但是別的命名空間依然使用系統(tǒng)構(gòu)建的。在JENA代碼中,只是用到這個(gè)方法,并不需要?jiǎng)e的任何操作了:
# -- nsA and cat defined
xmlns:cat="http://nowhere/else#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:nsA="http://somewhere/else#" >
兩個(gè)前綴都使用到輸出中的URI縮寫中了,沒有使用任何的系統(tǒng)內(nèi)部的前綴。
隱式定義前綴
除了調(diào)用setNsPrefix方法聲明前綴,其它JENA也會(huì)記住你在調(diào)用model.read()方法時(shí)讀入RDF使用的前綴。
把前面代碼生成的RDF/XML(有前綴的)粘貼到一個(gè)文件中,命名為file:/tmp/fragment.rdf。然后運(yùn)行以下代碼。
Model m2 = ModelFactory.createDefaultModel();
m2.read( "file:/tmp/fragment.rdf" );
m2.write( System.out );
你將會(huì)看到從入庫的文件中使用的前綴也會(huì)出現(xiàn)在輸出文件各。所有的前綴都被聲明在了輸出文件上,除非那個(gè)前綴沒有被使用(聲明了但未使用)。如果有某些前綴不想用,不希望出現(xiàn)在輸出中,可以調(diào)用removeNsPrefix(String prefix)方法取消使用。
N-Triples沒有關(guān)于縮寫URI的方式,不需要在意前綴和在輸出上使用前綴。JENA也支持N3,沒有前綴縮寫,在輸入中記錄,在輸出中使用(這句不知道怎么翻譯了,原文:Since NTriples doesn't have any short way of writing URIs, it takes no notice of prefixes on output and doesn't provide any on input. The notation?N3, also supported by Jena, does have short prefixed names, and records them on input and uses them on output.)。
JENA還有對(duì)模型中的前綴映射有更進(jìn)一步的操作,比如把現(xiàn)有的些映射關(guān)系封裝成一個(gè)java的Map對(duì)象(接口),或者一次性添加一批映射。關(guān)于PrefixMapping的更多細(xì)節(jié)請(qǐng)參閱相關(guān)文檔。
________________________________________
Jena RDF 包
Jena是一個(gè)為語義網(wǎng)應(yīng)用設(shè)計(jì)的一個(gè)Java API. 對(duì)應(yīng)用開發(fā)者而言, 主要可用的RDF包是com.hp.hpl.jena.rdf.model. 因?yàn)锳PI是以接口的方式定義的, 所以應(yīng)用代碼可以使用不同的實(shí)現(xiàn)機(jī)制而不用改變代碼本身. 這個(gè)包包含了可以表示模型, 資源, 屬性, 文本, 陳述和其他RDF關(guān)鍵概念的接口, 還有一個(gè)用來創(chuàng)建模型的ModelFactory. 所以如果要應(yīng)用代碼與實(shí)現(xiàn)類保持獨(dú)立, 最好盡可能地使用接口, 而不要使用特定的實(shí)現(xiàn)類.
com.hp.hpl.jena.Tutorial包包含了本教程所有例子中使用到的工作源代碼.
com.hp.hpl.jena.impl這些包包含了許多執(zhí)行時(shí)所常用的執(zhí)行類. 比如, 它們定義了諸如ResourseImpl, PropertyImpl和LiteralImpl的類, 這些類可以被不同的應(yīng)用直接使用也可以被繼承使用. 應(yīng)用程序應(yīng)該盡可能少地直接使用這些類. 例如, 與其使用ResouceImpl來創(chuàng)建一個(gè)新的實(shí)例, 更好的辦法是使用任何正在使用的模型的createResource方法來完成. 那樣的話, 如果模型的執(zhí)行采用了一個(gè)優(yōu)化的Resouce執(zhí)行, 那么在這兩種類型中不需要有任何的轉(zhuǎn)換工作.
________________________________________
操縱模型
到目前為止, 本教程主要講述的是如何創(chuàng)建, 讀入和輸出RDF模型. 現(xiàn)在是時(shí)候要講述如何訪問模型中的信息.
如果有了一個(gè)資源的URI, 那么就可以用Model.getResource(String uri)來從模型獲取這個(gè)資源對(duì)象. 這個(gè)方法被定義來返回一個(gè)資源對(duì)象, 如果它確實(shí)存在于模型中, 否則的話就創(chuàng)建一個(gè)新的. 例如, 如何從模型中獲取Adam Smith資源, 這個(gè)模型是Tutorial5中從文件讀入的:
// retrieve the John Smith vcard resource from the model
Resource vcard = model.getResource(johnSmithURI);
Resouce接口定義了一系列用于訪問某個(gè)資源的屬性的方法. Resource.getProperty(Property p)方法訪問了該資源的屬性. 這個(gè)方法不允許通常的Java訪問的轉(zhuǎn)換, 因?yàn)樗祷氐膶?duì)象是Statement, 而不是你所預(yù)計(jì)的Property. 返回整個(gè)陳述的好處是允許應(yīng)用程序通過使用它的某個(gè)訪問方法來訪問該陳述的客體來訪問這個(gè)屬性值. 例如如何獲取作為vcard:N屬性值的資源:
// retrieve the value of the N property
Resource name = (Resource) vcard.getProperty(VCARD.N)
.getObject();
一般而言, 一個(gè)陳述的客體可以是一個(gè)資源或是一個(gè)文本. 所以此應(yīng)用程序代碼知道這個(gè)值一定是個(gè)資源, 就將類型資源映射到返回的對(duì)象上. Jena的目標(biāo)之一是提供會(huì)返回值為特定類型的方法, 這樣,應(yīng)用程序就不必再做類型轉(zhuǎn)換工作, 也不必再編譯時(shí)做類型檢查工作. 以上的代碼片段也可以寫成更方便的形式:
// 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();
在這個(gè)例子中, 資源vcard只有一個(gè)vcard:FN屬性和一個(gè)vcard:N屬性. RDF允許資源有重復(fù)的屬性, 例如Adam可能有超過一個(gè)的昵稱. 讓我們假設(shè)他有兩個(gè)昵稱:
// add two nickname properties to vcard
vcard.addProperty(VCARD.NICKNAME, "Smithy")
.addProperty(VCARD.NICKNAME, "Adman");
正如前面所提到的那樣, Jena將RDF模型表示為一組陳述, 所以在模型中新增一個(gè)與原有陳述有著相同的主體,謂詞和客體的陳述并不會(huì)后什么作用. Jena沒有定義會(huì)返回模型中存在的兩個(gè)昵稱中的哪一個(gè). Vcard.getProperty(VCARD.NICKNAME)調(diào)用的結(jié)果是不確定的. Jena會(huì)返回這些值中的某一個(gè), 但是并不保證兩次連續(xù)的調(diào)用會(huì)同一個(gè)值.
一個(gè)屬性很有可能會(huì)出現(xiàn)多次, 而方法Resource.listProperty(Property p)可以用來返回一個(gè)iterator, 這個(gè)iterator會(huì)列出所有的值. 此方法所返回的iterator返回的對(duì)象的類型為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中找到, 運(yùn)行后會(huì)產(chǎn)生如下輸出:
The nicknames of "John Smith" are:
Smithy
Adman
一個(gè)資源的所有屬性可以用不帶參數(shù)的listStatement()方法列出.
________________________________________
查詢模型
前一節(jié)討論了如何通過一個(gè)有著已知URI的資源來操縱模型. 本節(jié)要討論查詢模型. 核心的Jena API只支持一些有限的查詢?cè)Z. 對(duì)于更強(qiáng)大查詢?cè)O(shè)備RDQL的介紹不在此文檔中.
列出模型所有陳述的Model.listStatements()方法也許是最原始的查詢模型方式. 然而并不推薦在大型的模型上使用這個(gè)方法. 類似的有Model.listSubjects(), 但其所返回的iterator會(huì)迭代所有含有屬性的資源, 例如是一些陳述的主體.
Model.listSubjectsWithProperty(Property p, RDFNode o)方法所返回的iterator跌代了所有具有屬性p且p屬性的值為o的資源. 我們可能會(huì)預(yù)計(jì)使用rdf:type屬性來搜索資源的類型屬性以獲得所有的vcard資源:
// retrieve all resource of type Vcard.
ResIterator iter = model.listSubjectsWithProperty(RDF.type, VCARD.Vcard);
然而, 不幸的是, 我們現(xiàn)在正在使用的vcard模式并沒有為vcard定義類型. 然而, 如果我們假設(shè)只有類型為vcard的資源才會(huì)使用vcard:FN屬性, 并且在我們的數(shù)據(jù)中, 所有此類資源都有這樣一個(gè)屬性, 那么我們就可以像這樣找到所有的vcard:
// list vcards
ResIterator iter = model.listSubjectsWithProperty(VCARD.FN);
while (iter.hasNext()) {
Resource r = iter.nextResource();
...
}
所有的這些查詢方法不過是在原語查詢方法model.listStatements(Select s)上稍做變化而已. 此方法會(huì)返回一個(gè)iterator, 該iterator會(huì)跌代模型中所有被s選中的陳述. 這個(gè)selector接口被設(shè)計(jì)成可擴(kuò)展的, 但是目前, 它只有一個(gè)執(zhí)行類,那就是com.hp.hpl.jena.rdf.model包中的SimpleSelector類. 在Jena中使用SimpleSelector是很少見的情況, 即當(dāng)需要直接使用一個(gè)特定類而不是使用接口. SimpleSelector的構(gòu)造函數(shù)帶有三個(gè)參數(shù):
Selector selector = new SimpleSelector(subject, predicate, object)
這個(gè)selector會(huì)選擇所有主體與參數(shù)subject相配, 謂詞與參數(shù)predicate相配, 且客體與參數(shù)object相配的陳述.
如果某個(gè)參數(shù)值為null, 則它表示與任何值均匹配; 否則的話, 它們就會(huì)匹配一樣的資源或文本. (當(dāng)兩個(gè)資源有相同的URI或是同一個(gè)空白結(jié)點(diǎn)時(shí), 這兩個(gè)資源就是一樣的; 當(dāng)兩個(gè)文本是一樣的當(dāng)且僅當(dāng)它們的所有成分都是一樣的.) 所以:
Selector selector = new SimpleSelector(null, null, null);
會(huì)選擇模型中所有的陳述.
Selector selector = new SimpleSelector(null, VCARD.FN, null);
會(huì)選擇所有謂詞為VCARD.FN的陳述, 而對(duì)主體和客體沒有要求. 作為一個(gè)特殊的縮寫,
listStatements( S, P, O )
等同與
listStatements( new SimpleSelector( S, P, O ) )
下面的代碼可以在Tutorial7中找到, 列舉了數(shù)據(jù)庫中所有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");
}
這個(gè)會(huì)產(chǎn)生類似如下的輸出:
The database contains vcards for:
Sarah Jones
John Smith
Matt Jones
Becky Smith
你的下一個(gè)練習(xí)是修改此代碼以使用SimpleSelector而不是使用listSubjectsWithProperty來達(dá)到相同的效果.
讓我們看看如何對(duì)所選擇的陳述實(shí)行更好的控制. SimpleSelector可以被繼承, 它的select方法可以被修改來實(shí)現(xiàn)更好的過濾:
// 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");}
});
這個(gè)示例使用了一個(gè)簡潔的Java技術(shù), 就是當(dāng)創(chuàng)建此類的一個(gè)實(shí)例時(shí)重載一個(gè)內(nèi)聯(lián)的方法. 這里selects(…)方法會(huì)檢查以保證全名以"Smith"做結(jié)尾. 重要的是要注意對(duì)主體, 謂語和客體的過濾是在調(diào)用selects(…)方法之前的執(zhí)行的, 所以額外的測試只會(huì)被應(yīng)用于匹配的陳述.
完整的代碼可以在Tutorial8中找到, 并會(huì)產(chǎn)生如下的輸出:
The database contains vcards for:
John Smith
Becky Smith
你也許會(huì)認(rèn)為下面的代碼:
// 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)
雖然在功能上它們可能是等同的, 但是第一種形式會(huì)列舉出模型中所有的陳述, 然后再對(duì)它們進(jìn)行逐一的測試. 而第二種形式則允許執(zhí)行時(shí)建立索引來提供性能. 你可以在一個(gè)大模型中自己試試驗(yàn)證一下, 但是現(xiàn)在先為自己倒一杯咖啡休息一下吧.
________________________________________
對(duì)模型的操作
Jena提供了把模型當(dāng)作一個(gè)集合整體來操縱的三種操作方法. 即三種常用的集合操作:并, 交和差.
兩個(gè)模型的并操作就是把表示兩個(gè)模型的陳述集的并操作. 這是RDF設(shè)計(jì)所支持的關(guān)鍵操作之一. 它此操作允許把分離的數(shù)據(jù)源合并到一起. 考慮下面兩個(gè)模型:
和?
當(dāng)它們被合并時(shí), 兩個(gè)http://...JohnSmith會(huì)合并成一個(gè), 重復(fù)的vcard:FN箭頭會(huì)被丟棄, 此時(shí)就會(huì)產(chǎn)生:
讓我們看一下這個(gè)代碼的功能(完整的代碼在Tutorial9中), 再看看會(huì)發(fā)生什么.
// 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會(huì)產(chǎn)生如下的輸出:
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:vcard="http://www.w3.org/2001/vcard-rdf/3.0#">
John@somewhere.com
John
Smith
John Smith
即便你不熟悉RDF/XML的語法細(xì)節(jié), 你仍然可以清楚的看見模型如同預(yù)期般被合并了. 我們可以在類似的方式下運(yùn)算模型的交操作和差操作.
________________________________________
容器
RDF定義了一類特殊的資源來表示事物的集合. 這些資源稱為容器. 一個(gè)容器的成員可以是資源也可以是文本. 有三類容器:
一個(gè)BAG是一個(gè)無序的集合.
一個(gè)ALT是一個(gè)用來表示備選項(xiàng)的無序的集合.
一個(gè)SEQ是一個(gè)有序的集合.
一個(gè)容器由一個(gè)資源表示. 該資源會(huì)有一個(gè)rdf:type屬性, 屬性值為rdf:Bag, 或rdf:Alt, 或是rdf:Seq, 再或是這些類型的子類型, 這取決于容器的類型. 容器的第一個(gè)成員是容器的rdf:_1的屬性所對(duì)應(yīng)的屬性值; 第二個(gè)成員是容器的rdf:_2屬性的值, 依此類推. 這些rdf:_nnn屬性被稱為序數(shù)屬性.
例如, 一個(gè)含有Smith的vcard的簡單bag容器的模型可能會(huì)看起來是這樣的:
雖然bag容器的成員們被rdf:_1,rdf_2等等的屬性所表示, 但是這些屬性的順序卻并不重要. 即便我們將rdf:_1和rdf:_2的屬性值交換, 但是交換后的模型仍然表示相同的信息.
Alt是設(shè)計(jì)用來表示被選項(xiàng)的. 例如, 我們假定有一個(gè)表示軟件產(chǎn)品的資源. 它可能有一個(gè)屬性指示從哪里可以獲得次軟件產(chǎn)品. 這個(gè)屬性值可能是一個(gè)包含各種下載地址的Alt集合. Alt是無序的, 除了rdf:_1屬性有著特殊的意義, 它表示默認(rèn)的選項(xiàng).
盡管我們可以用基本的資源和屬性機(jī)制來處理容器, Jena為處理容器設(shè)計(jì)了顯式的接口和執(zhí)行類. 要避免在使用一個(gè)對(duì)象來操作容器的同時(shí)去使用低層方法來改變?nèi)萜?
讓我們修改Tutorial8以創(chuàng)建一個(gè)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());
}
如果我們將次模型輸出可以看見它含有類似如下的成分:
xmlns:rdf='http://www.w3.org/1999/02/22-rdf-syntax-ns#'
xmlns:vcard='http://www.w3.org/2001/vcard-rdf/3.0#'
>
...
這表示了Bag資源.
容器接口提供了一個(gè)iterator來列舉容器的內(nèi)容:
// 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");
}
并會(huì)產(chǎn)生如下的輸出:
The bag contains:
John Smith
Becky Smith
本例的可執(zhí)行代碼可以在Tutorial10中找到.
Jena類所提供的操縱容器的方法包括增加新成員, 在容器中間插入新成員和刪除已有的成員. Jena容器類目前保證所使用的序數(shù)屬性列表會(huì)從rdf:_1開始并且是相鄰的. RDF核心工作小組放松了此項(xiàng)限制, 以允許有局部的容器表示. 所以這是Jena將來可能會(huì)修改的地方之一.
________________________________________
關(guān)于文本(Literals)和數(shù)據(jù)類型的更多探討
RDF文本(literals)并不僅僅是簡單的字符串而已. 文本可能有一個(gè)語言標(biāo)簽來指示該文本使用的語言. 有英語語言標(biāo)簽的文本"chat"會(huì)被認(rèn)為與有著法語語言標(biāo)簽的文本"chat"是不同的. 這個(gè)奇怪的特性是原有RDF/XML語法產(chǎn)生的贗象(artefact).
另外, 事實(shí)上共有兩種文本. 在一種里, 字符串成分只是簡單的字符串. 而在另一種里, 字符串成分被預(yù)計(jì)為格式良好的XML片段. 當(dāng)一個(gè)RDF模型被寫成RDF/XML形式時(shí), 一個(gè)特殊的使用parseType='Literal'的屬性(attribute)構(gòu)造會(huì)被使用來表示它.
在Jena中, 當(dāng)一個(gè)文本被創(chuàng)建時(shí), 這些屬性就被設(shè)置了. 例如, 在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("chat", true));
// write out the Model
model.write(system.out);
會(huì)產(chǎn)生:
xmlns:rdf='http://www.w3.org/1999/02/22-rdf-syntax-ns#'
xmlns:rdfs='http://www.w3.org/2000/01/rdf-schema#'
>
chat
chat
chat
如果兩個(gè)文本被認(rèn)為是相同的, 它們一定都是XML的文本或都是簡單的文本. 另外, 它們兩個(gè)要么都沒有語言標(biāo)簽, 要么有相同的語言標(biāo)簽. 對(duì)簡單的文本而言, 兩者的字符串一定是相同的. XML文本的等同有兩點(diǎn)要求. 第一, 前面提到的要求必須被滿足并且字符串必須相同. 第二, 如果它們字符串的cannonicalization一樣的話, 它們就是一樣的.(譯者注: 找不到cannonicalization中文的解釋.).
Jena接口也支持類型文字. 老式的對(duì)待類型文字的方法是把他們當(dāng)成是字符串的縮寫: 有類型的值會(huì)通過Java的常用方法轉(zhuǎn)換為字符串, 并且這些字符串被存儲(chǔ)在模型中. 例如, 可以試一試(注意:對(duì)于簡單類型文字, 我們可以省略對(duì)model.createLiteral(…)的調(diào)用):
// 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");
因?yàn)閮蓚€(gè)文本都是字符串"11", 所以只會(huì)有一個(gè)陳述被添加.
RDF核心工作小組定義了支持RDF數(shù)據(jù)類型的機(jī)制. Jena支持那些使用類型文字的機(jī)制; 但是本教程中不會(huì)對(duì)此討論.
________________________________________
術(shù)語表
空白結(jié)點(diǎn)
表示一個(gè)資源, 但是并沒有指示該資源的URI. 空白結(jié)點(diǎn)的作用如同第一邏輯中的存在符合變量.
Dublin 核心
一個(gè)關(guān)于網(wǎng)絡(luò)資源的元數(shù)據(jù)標(biāo)準(zhǔn). 更詳細(xì)的信息可以在Dublin Core web site找到.
文本
一個(gè)可以作為屬性值的字符串.
客體
三元組的一部分, 也就是陳述的值.
謂詞
三元組的屬性部分.
屬性
屬性(property)是資源的一個(gè)屬性(attribute). 例如, DC.title是一個(gè)屬性, RDF.type也是一個(gè)屬性.
資源
某個(gè)實(shí)體. 它可以是一個(gè)網(wǎng)絡(luò)資源, 例如一個(gè)網(wǎng)頁; 它也可以是一個(gè)具體的物理對(duì)象, 例如一棵樹或一輛車; 它也可以是一個(gè)抽象的概念, 例如國際象棋或足球. 資源由URI命名.
陳述
RDF模型中的一個(gè)箭頭, 通常被解理解為一個(gè)事實(shí)
主體
RDF模型中箭頭出發(fā)點(diǎn)的那個(gè)資源.
三元組
一個(gè)含有主體, 謂詞和客體的結(jié)構(gòu). 是陳述的另一種稱呼.
________________________________________
腳注
RDF資源的標(biāo)簽可以包括一個(gè)片段標(biāo)簽, 例如http://hostname/rdf/Tutorial/#ch-Introduction, 所以, 嚴(yán)格地講, 一個(gè)RDF資源是由一個(gè)URI表示的.
文本可以表示為一個(gè)字符串, 也可以有一個(gè)可選的語言編碼來表示該字符串的語言. 例如, 文本可能有一個(gè)表示英語的語言編碼"en", 而文本"deux"可能有一個(gè)表示法語的語言編碼"fr".
總結(jié)
以上是生活随笔為你收集整理的mysql jena rdf_RDF和Jena RDF API简介的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Go实战 | 一文带你搞懂从单队列到优先
- 下一篇: 遍历操作__getitem__