Jena文档《An Introduction to RDF and the Jena RDF API》的译文
前言
本文是一篇對(duì)W3C的資源描述框架(RDF)和 Jena(一個(gè)Java的RDF API)的教程性介紹. 本文是為那些不熟悉RDF的, 以及那些通過(guò)建立原形可以達(dá)到最好學(xué)習(xí)效果的, 或是因?yàn)槠渌蛳M芸焖俨僮鱆ena的程序員而寫(xiě)的. 我們假設(shè)讀者在閱讀本文前已具有一定的XML和Java知識(shí).
如果讀者在沒(méi)有理解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)再試一試. 這樣一來(lái)就能達(dá)到理論實(shí)踐相結(jié)合的效果.數(shù)據(jù)模型本身十分簡(jiǎn)單,所以學(xué)習(xí)過(guò)程不會(huì)太長(zhǎng).
RDF具有XML的語(yǔ)法, 所以許多熟悉XML的人就會(huì)認(rèn)為以XML語(yǔ)法的形式來(lái)思考RDF. 然而, 這是不對(duì)的. RDF應(yīng)該以它數(shù)據(jù)模型的形式來(lái)被理解. RDF數(shù)據(jù)可是用XML來(lái)表示, 但是理解數(shù)據(jù)模型的重要性更在理解此語(yǔ)法重要性之上.
Jena API的一個(gè)運(yùn)行例子, 包括本教程中所有例子的工作源代碼都可以在http://www.hpl.hp.com/semweb/下載.
________________________________________
目錄
1. 導(dǎo)言
2. 陳述Statements
3. RDF寫(xiě)操作
4. RDF讀操作
5. Jena RDF 包
6. 操縱模型
7. 查詢(xún)模型
8. 對(duì)模型的操作?
9. 容器Containers
10. 關(guān)于Literals和數(shù)據(jù)類(lèi)型的更多探討
11. 術(shù)語(yǔ)表
________________________________________
導(dǎo)言
資源描述框架是(RDF)是描述資源的一項(xiàng)標(biāo)準(zhǔn)(在技術(shù)上是W3C的推薦標(biāo)準(zhǔn)). 什么是資源? 這實(shí)在是一個(gè)很難回答的問(wèn)題, 其精確的定義目前尚在爭(zhēng)論中. 出于我們的目的, 我們可以把資源想象成任何我們可以確定識(shí)別的東西. 在本教程中,讀者你本身就是一個(gè)資源, 而你的主頁(yè)也是一個(gè)資源, 數(shù)字1和故事中巨大的白鯨都是資源.
在本教程中, 我們的例子會(huì)圍繞人們展開(kāi). 假設(shè)人們會(huì)使用VCARDS, 而VCARD將由RDF表示機(jī)制來(lái)描述. 我們最好把RDF考慮成由結(jié)點(diǎn)和箭頭的形式構(gòu)成的圖. 一個(gè)簡(jiǎn)單的vcard在RDF中可能看起來(lái)是這樣的:
資源John Smith在圖中用橢圓表示, 并被一個(gè)統(tǒng)一資源定位符(URI) 所標(biāo)識(shí), 在本例中是"http://.../JohnSmith"). 如果你想要通過(guò)你的瀏覽器來(lái)訪(fǎng)問(wèn)這個(gè)資源的話(huà),你很有可能會(huì)失敗. 四月的愚人節(jié)笑話(huà)并不經(jīng)得起考驗(yàn), 相反如果你的瀏覽器把John Smith傳遞到你的桌面的話(huà), 你才該感到驚訝. 如果你并不熟悉URI's的話(huà), 你可以把它們想象成簡(jiǎn)單的陌生名字.
資源擁有屬性(property). 在這些例子中, 我們對(duì)John Smith名片上出現(xiàn)的那些屬性很感興趣.圖1只顯示了一個(gè)屬性, John Smith的全名. 屬性是由標(biāo)有屬性名的箭頭表示的. 屬性的名字也是一個(gè)URI, 但是由于URI十分冗長(zhǎng)笨重, 所以圖中將它顯示為XML qname的形式. 在':'之前的部分稱(chēng)為命名空間前綴并表示了一個(gè)命名空間. 在':'之后的部分稱(chēng)為局部名, 并表示在命名空間中的一個(gè)名字. 在寫(xiě)成RDF XML形式時(shí), 屬性常常以qname的形式表示, 這是一個(gè)在圖形和文本中的簡(jiǎn)單的縮寫(xiě)方法. 然而, 嚴(yán)格地講, 屬性應(yīng)該用URI來(lái)標(biāo)識(shí). 命名空間前綴:局部名的形式是一種命名空間連接局部名的URI縮寫(xiě). 當(dāng)瀏覽器訪(fǎng)問(wèn)時(shí), 用并沒(méi)有強(qiáng)制屬性的URI必須指向一些具體的事物.
每個(gè)屬性都有一個(gè)值. 在此例中, 值為一個(gè)文本(literal), 我們現(xiàn)在可以把它看成一個(gè)字符串.文本在圖中顯示為長(zhǎng)方形.
Jena是一個(gè)Java API, 我們可以用它來(lái)創(chuàng)建和操縱諸如上述例圖的RDF圖. Jena設(shè)有表示圖(graph), 資源(resource), 屬性和文本(literal)的對(duì)象類(lèi). 表示資源, 屬性和文本的接口分別稱(chēng)為Resource, Property, 和Literal. 在Jena中, 一個(gè)圖(graph)被稱(chēng)為一個(gè)模型并被Model接口所表示.
創(chuàng)建上述例圖或稱(chēng)為上述模型的代碼很簡(jiǎn)單:
// 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類(lèi)中的createDefaultMode()方法創(chuàng)建了一個(gè)空的基于內(nèi)存存儲(chǔ)的模型(Model 或 model). Jena還包含了Model接口的其他實(shí)現(xiàn)方式. 例如, 使用關(guān)系數(shù)據(jù)庫(kù)的, 這些類(lèi)型 Model接口也可以從ModelFactory中創(chuàng)建.
于是John Smith這個(gè)資源就被創(chuàng)建了, 并向其添加了一個(gè)屬性. 此屬性由一個(gè)"常" ("constant")類(lèi)VCARD提供, 這個(gè)類(lèi)保存了在VCARD模式(schema)中所有定義的表示對(duì)象. Jena也為其他一些著名的模式提供了常類(lèi)的表示方法, 例如是RDF和RDF模式, Dublin 核心標(biāo)準(zhǔn)和DAML.
創(chuàng)建資源和添加屬性的代碼可以寫(xiě)成更緊湊的層疊形式:
Resource johnSmith =
??????? model.createResource(personURI)
???????????? .addProperty(VCARD.FN, fullName);
這個(gè)例子的工作代碼可以在Jena發(fā)布的材料的教程包中的Tutorial1中找到. 作為練習(xí), 你自己可以獲得此代碼并修改其以創(chuàng)建一個(gè)簡(jiǎn)單VCARD.
現(xiàn)在讓我們?yōu)関card再增加一些更詳細(xì)的內(nèi)容, 以便探索更多的RDF和Jena的特性.
在第一個(gè)例子里, 屬性值為一個(gè)文本. 然而RDF屬性也可以采用其他的資源作為其屬性值. 下面這個(gè)例子使用常用的RDF技術(shù)展示了如何表示John Smith名字的不同部分:
在這里我們?cè)黾恿艘粋€(gè)新的屬性, vcard:N, 來(lái)表示John Smith名字的結(jié)構(gòu). 這個(gè)模型有幾點(diǎn)有趣之處. 注意屬性vcard:N使用一個(gè)資源作為起屬性值. 同時(shí)注意代表復(fù)合名字的橢圓并沒(méi)有URI標(biāo)識(shí). 它被認(rèn)為是一個(gè)空白結(jié)點(diǎn)(blank Node).
創(chuàng)建此例的Jena代碼也十分簡(jiǎn)單. 首先是一些聲明和對(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類(lèi)型的變量. StmtItor是Java中Iterator的一個(gè)子類(lèi)型, 這個(gè)StmtIterator變量重復(fù)迭代了該接口模型中的所有陳述. StmtIterator類(lèi)型中有一個(gè)方法nextStatement(), 該方法會(huì)從iterator返回下一個(gè)陳述. (就和next()返回的一樣, 但是已將其映射為Statement類(lèi)型). 接口Statement提供了訪(fǎng)問(wèn)陳述中主體, 謂詞和客體的方法.
現(xiàn)在我們會(huì)用使用那個(gè)接口來(lái)擴(kuò)展Tutorial2, 使起列出所有的創(chuàng)建的陳述并將它們打印出來(lái). 此例完整的代碼可以在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è)類(lèi)型為RDFNode的客體, RDFNode是Resource和Literal類(lèi)共同的超類(lèi). 為了確定本例中的客體確切的類(lèi)型, 代碼中使用 instanceof來(lái)確定其類(lèi)型和相應(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è)類(lèi)似的表示符號(hào)稱(chēng)為N-三元組(N-Triples). 這個(gè)名字表示會(huì)使用"三元組符號(hào)". 在下一節(jié)中我們會(huì)看到Jena有一個(gè)內(nèi)置的N-三元組寫(xiě)機(jī)制(writer).
________________________________________
寫(xiě)RDF
Jena設(shè)有讀寫(xiě)XML形式的RDF方法. 這些方法可以被用來(lái)將一個(gè)RDF模型保存到文件并在日后重新將其讀回.
Tutorial3創(chuàng)建了一個(gè)模型并將其以三元組的形式輸出. Tutorial4對(duì)Tutorial3做了修改, 使其將此模型以RDF XML的形式輸出到標(biāo)準(zhǔn)輸出流中. 這個(gè)代碼依然十分簡(jiǎn)單: model.write可以帶一個(gè)OutputStream的參數(shù).
// now write the model in XML form to a file
model.write(System.out);
應(yīng)該有類(lèi)似的輸出:
<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:about='http://somewhere/JohnSmith'>
??? <vcard:FN>John Smith</vcard:FN>
??? <vcard:N rdf:nodeID="A0"/>
? </rdf:Description>
? <rdf:Description rdf:nodeID="A0">
??? <vcard:Given>John</vcard:Given>
??? <vcard:Family>Smith</vcard:Family>
? </rdf:Description>
</rdf:RDF>
W3C的RDF規(guī)格說(shuō)明書(shū)規(guī)定了如何用 XML的形式來(lái)表示RDF. RDF XML的語(yǔ)法十分復(fù)雜. 讀者可以在RDF核心工作小組制定的RDF入門(mén)篇(primer)中找到更詳細(xì)的指導(dǎo). 但是不管怎么樣, 讓我們先迅速看一下應(yīng)該如何解釋上面的RDF XML輸出
RDF常常嵌入在一個(gè)<rdf:RDF>元素中. 如果有其他的方法知道此XML是RDF的話(huà),該元素是可以不寫(xiě)的. 然而我們常常會(huì)使用它. 在這個(gè)RDF元素中定義了兩個(gè)在本文檔中使用的命名空間. 接下來(lái)是一個(gè)<rdf:Description>元素, 此元素描述了URI為"http://somewhere/JohnSmith"的資源. 如果其中的rdf:about屬性被省略的話(huà), 這個(gè)元素就表示一個(gè)空白結(jié)點(diǎn).
<vcard:FN>元素描述了此資源的一個(gè)屬性. 屬性的名字"FN"是屬于vcard命名空間的. RDF會(huì)通過(guò)連接命名空間前綴的URI和名字局部名"FN"來(lái)形成該資源的URI "http://www.w3.org/2001/vcard-rdf/3.0#FN". 這個(gè)屬性的值為文本"John Smith".
<vcard:N>元素是一個(gè)資源. 在此例中, 這個(gè)資源是用一個(gè)相對(duì)URI來(lái)表示的. RDF會(huì)通過(guò)連接這個(gè)相對(duì)URI和此文檔的基準(zhǔn)URI來(lái)把它轉(zhuǎn)換為一個(gè)絕對(duì)URI.
但是, 在這個(gè)RDF XML輸出中有一個(gè)錯(cuò)誤, 它并沒(méi)有準(zhǔn)確地表示我們所創(chuàng)建的模型. 模型中的空白結(jié)點(diǎn)被分配了一個(gè)URI. 它不再是空白的了. RDF/XML語(yǔ)法并不能表示所有的RDF模型. 例如它不能表示一個(gè)同時(shí)是兩個(gè)陳述的客體的空白結(jié)點(diǎn). 我們用來(lái)寫(xiě)這個(gè)RDF/XML的'啞'writer方法并沒(méi)有試圖去正確的書(shū)寫(xiě)這個(gè)模型的子集, 雖然其原本可以被正確書(shū)寫(xiě). 它給每一個(gè)空白結(jié)點(diǎn)一個(gè)URI, 使其不再空白.
Jena有一個(gè)擴(kuò)展的接口, 它允許新的為不同的RDF串行化語(yǔ)言設(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縮寫(xiě)語(yǔ)法把模型寫(xiě)地更為緊湊. 它也能保存盡可能保留空白結(jié)點(diǎn). 然而, 它并不合適來(lái)輸出大的模型. 因?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)生類(lèi)似于Tutorial3的輸出, 此輸出會(huì)遵循N-三元組的規(guī)格.
________________________________________
讀RDF
Tutorial 5 演示了如何將用RDF XML記錄的陳述讀入一個(gè)模型. 在此例中, 我們提供了一個(gè)小型RDF/XML形式的vcard的數(shù)據(jù)庫(kù). 下面代碼會(huì)將其讀入和寫(xiě)出. 注意: 如果要運(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, 它是被用來(lái)解決相對(duì)URI的. 因?yàn)樵跍y(cè)試文件中沒(méi)有使用相對(duì)URI, 所以它允許被置為空值. 運(yùn)行時(shí), Tutorial5會(huì)產(chǎn)生類(lèi)似如下的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:Family>Smith</vcard:Family>
??? <vcard:Given>John</vcard:Given>
? </rdf:Description>
? <rdf:Description rdf:about='http://somewhere/JohnSmith/'>
??? <vcard:FN>John Smith</vcard:FN>
??? <vcard:N rdf:nodeID="A0"/>
? </rdf:Description>
? <rdf:Description rdf:about='http://somewhere/SarahJones/'>
??? <vcard:FN>Sarah Jones</vcard:FN>
??? <vcard:N rdf:nodeID="A1"/>
? </rdf:Description>
? <rdf:Description rdf:about='http://somewhere/MattJones/'>
??? <vcard:FN>Matt Jones</vcard:FN>
??? <vcard:N rdf:nodeID="A2"/>
? </rdf:Description>
? <rdf:Description rdf:nodeID="A3">
??? <vcard:Family>Smith</vcard:Family>
??? <vcard:Given>Rebecca</vcard:Given>
? </rdf:Description>
? <rdf:Description rdf:nodeID="A1">
??? <vcard:Family>Jones</vcard:Family>
??? <vcard:Given>Sarah</vcard:Given>
? </rdf:Description>
? <rdf:Description rdf:nodeID="A2">
??? <vcard:Family>Jones</vcard:Family>
??? <vcard:Given>Matthew</vcard:Given>
? </rdf:Description>
? <rdf:Description rdf:about='http://somewhere/RebeccaSmith/'>
??? <vcard:FN>Becky Smith</vcard:FN>
??? <vcard:N rdf:nodeID="A3"/>
? </rdf:Description>
</rdf:RDF>
________________________________________
Jena RDF 包
Jena是一個(gè)為語(yǔ)義網(wǎng)應(yīng)用設(shè)計(jì)的一個(gè)Java API. 對(duì)應(yīng)用開(kāi)發(fā)者而言, 主要可用的RDF包是com.hp.hpl.jena.rdf.model. 因?yàn)锳PI是以接口的方式定義的, 所以應(yīng)用代碼可以使用不同的實(shí)現(xiàn)機(jī)制而不用改變代碼本身. 這個(gè)包包含了可以表示模型, 資源, 屬性, 文本, 陳述和其他RDF關(guān)鍵概念的接口, 還有一個(gè)用來(lái)創(chuàng)建模型的ModelFactory. 所以如果要應(yīng)用代碼與實(shí)現(xiàn)類(lèi)保持獨(dú)立, 最好盡可能地使用接口, 而不要使用特定的實(shí)現(xiàn)類(lèi).
com.hp.hpl.jena.Tutorial包包含了本教程所有例子中使用到的工作源代碼.
com.hp.hpl.jena.impl這些包包含了許多執(zhí)行時(shí)所常用的執(zhí)行類(lèi). 比如, 它們定義了諸如ResourseImpl, PropertyImpl和LiteralImpl的類(lèi), 這些類(lèi)可以被不同的應(yīng)用直接使用也可以被繼承使用. 應(yīng)用程序應(yīng)該盡可能少地直接使用這些類(lèi). 例如, 與其使用ResouceImpl來(lái)創(chuàng)建一個(gè)新的實(shí)例, 更好的辦法是使用任何正在使用的模型的createResource方法來(lái)完成. 那樣的話(huà), 如果模型的執(zhí)行采用了一個(gè)優(yōu)化的Resouce執(zhí)行, 那么在這兩種類(lèi)型中不需要有任何的轉(zhuǎn)換工作.
________________________________________
操縱模型
到目前為止, 本教程主要講述的是如何創(chuàng)建, 讀入和輸出RDF模型. 現(xiàn)在是時(shí)候要講述如何訪(fǎng)問(wèn)模型中的信息.
如果有了一個(gè)資源的URI, 那么就可以用Model.getResource(String uri)來(lái)從模型獲取這個(gè)資源對(duì)象. 這個(gè)方法被定義來(lái)返回一個(gè)資源對(duì)象, 如果它確實(shí)存在于模型中, 否則的話(huà)就創(chuàng)建一個(gè)新的. 例如, 如何從模型中獲取Adam Smith資源, 這個(gè)模型是Tutorial5中從文件讀入的:
// retrieve the John Smith vcard resource from the model
Resource vcard = model.getResource(johnSmithURI);
??
Resouce接口定義了一系列用于訪(fǎng)問(wèn)某個(gè)資源的屬性的方法. Resource.getProperty(Property p)方法訪(fǎng)問(wèn)了該資源的屬性. 這個(gè)方法不允許通常的Java訪(fǎng)問(wèn)的轉(zhuǎn)換, 因?yàn)樗祷氐膶?duì)象是Statement, 而不是你所預(yù)計(jì)的Property. 返回整個(gè)陳述的好處是允許應(yīng)用程序通過(guò)使用它的某個(gè)訪(fǎng)問(wèn)方法來(lái)訪(fǎng)問(wèn)該陳述的客體來(lái)訪(fǎng)問(wèn)這個(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è)資源, 就將類(lèi)型資源映射到返回的對(duì)象上. Jena的目標(biāo)之一是提供會(huì)返回值為特定類(lèi)型的方法, 這樣,應(yīng)用程序就不必再做類(lèi)型轉(zhuǎn)換工作, 也不必再編譯時(shí)做類(lèi)型檢查工作. 以上的代碼片段也可以寫(xiě)成更方便的形式:
// retrieve the value of the FN property
Resource name = vcard.getProperty(VCARD.N)
???????????????????? .getResource();
類(lèi)似地, 屬性的文本值也可以被獲取:
// retrieve the given name property
String fullName = vcard.getProperty(VCARD.FN)
??????????????????????? .getString();
在這個(gè)例子中, 資源vcard只有一個(gè)vcard:FN屬性和一個(gè)vcard:N屬性. RDF允許資源有重復(fù)的屬性, 例如Adam可能有超過(guò)一個(gè)的昵稱(chēng). 讓我們假設(shè)他有兩個(gè)昵稱(chēng):
// add two nickname properties to vcard
vcard.addProperty(VCARD.NICKNAME, "Smithy")
.addProperty(VCARD.NICKNAME, "Adman");
正如前面所提到的那樣, Jena將RDF模型表示為一組陳述, 所以在模型中新增一個(gè)與原有陳述有著相同的主體,謂詞和客體的陳述并不會(huì)后什么作用. Jena沒(méi)有定義會(huì)返回模型中存在的兩個(gè)昵稱(chēng)中的哪一個(gè). Vcard.getProperty(VCARD.NICKNAME)調(diào)用的結(jié)果是不確定的. Jena會(huì)返回這些值中的某一個(gè), 但是并不保證兩次連續(xù)的調(diào)用會(huì)同一個(gè)值.
一個(gè)屬性很有可能會(huì)出現(xiàn)多次, 而方法Resource.listProperty(Property p)可以用來(lái)返回一個(gè)iterator, 這個(gè)iterator會(huì)列出所有的值. 此方法所返回的iterator返回的對(duì)象的類(lèi)型為Statement.我們可以像這樣列出所有的昵稱(chēng):
// 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()方法列出.
________________________________________
查詢(xún)模型
前一節(jié)討論了如何通過(guò)一個(gè)有著已知URI的資源來(lái)操縱模型. 本節(jié)要討論查詢(xún)模型. 核心的Jena API只支持一些有限的查詢(xún)?cè)Z(yǔ). 對(duì)于更強(qiáng)大查詢(xún)?cè)O(shè)備RDQL的介紹不在此文檔中.
列出模型所有陳述的Model.listStatements()方法也許是最原始的查詢(xún)模型方式. 然而并不推薦在大型的模型上使用這個(gè)方法. 類(lèi)似的有Model.listSubjects(), 但其所返回的iterator會(huì)迭代所有含有屬性的資源, 例如是一些陳述的主體.
Model.listSubjectsWithProperty(Property p, RDFNode o)方法所返回的iterator跌代了所有具有屬性p且p屬性的值為o的資源. 我們可能會(huì)預(yù)計(jì)使用rdf:type屬性來(lái)搜索資源的類(lèi)型屬性以獲得所有的vcard資源:
// retrieve all resource of type Vcard.
ResIterator iter = model.listSubjectsWithProperty(RDF.type, VCARD.Vcard);
然而, 不幸的是, 我們現(xiàn)在正在使用的vcard模式并沒(méi)有為vcard定義類(lèi)型. 然而, 如果我們假設(shè)只有類(lèi)型為vcard的資源才會(huì)使用vcard:FN屬性, 并且在我們的數(shù)據(jù)中, 所有此類(lèi)資源都有這樣一個(gè)屬性, 那么我們就可以像這樣找到所有的vcard:
// list vcards
ResIterator iter = model.listSubjectsWithProperty(VCARD.FN);
while (iter.hasNext()) {
??? Resource r = iter.nextResource();
??? ...
}
所有的這些查詢(xún)方法不過(guò)是在原語(yǔ)查詢(xún)方法model.listStatements(Select s)上稍做變化而已. 此方法會(huì)返回一個(gè)iterator, 該iterator會(huì)跌代模型中所有被s選中的陳述. 這個(gè)selector接口被設(shè)計(jì)成可擴(kuò)展的, 但是目前, 它只有一個(gè)執(zhí)行類(lèi),那就是com.hp.hpl.jena.rdf.model包中的SimpleSelector類(lèi). 在Jena中使用SimpleSelector是很少見(jiàn)的情況, 即當(dāng)需要直接使用一個(gè)特定類(lèi)而不是使用接口. 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à), 它們就會(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ì)主體和客體沒(méi)有要求. 作為一個(gè)特殊的縮寫(xiě),
listStatements( S, P, O )?
等同與
listStatements( new SimpleSelector( S, P, O ) )?
下面的代碼可以在Tutorial7中找到, 列舉了數(shù)據(jù)庫(kù)中所有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)生類(lèi)似如下的輸出:
The database contains vcards for:
? Sarah Jones
? John Smith
? Matt Jones
? Becky Smith
??
你的下一個(gè)練習(xí)是修改此代碼以使用SimpleSelector而不是使用listSubjectsWithProperty來(lái)達(dá)到相同的效果.
讓我們看看如何對(duì)所選擇的陳述實(shí)行更好的控制. SimpleSelector可以被繼承, 它的select方法可以被修改來(lái)實(shí)現(xiàn)更好的過(guò)濾:
// 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è)簡(jiǎn)潔的Java技術(shù), 就是當(dāng)創(chuàng)建此類(lèi)的一個(gè)實(shí)例時(shí)重載一個(gè)內(nèi)聯(lián)的方法. 這里selects(…)方法會(huì)檢查以保證全名以"Smith"做結(jié)尾. 重要的是要注意對(duì)主體, 謂語(yǔ)和客體的過(guò)濾是在調(diào)用selects(…)方法之前的執(zhí)行的, 所以額外的測(cè)試只會(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)行逐一的測(cè)試. 而第二種形式則允許執(zhí)行時(shí)建立索引來(lái)提供性能. 你可以在一個(gè)大模型中自己試試驗(yàn)證一下, 但是現(xiàn)在先為自己倒一杯咖啡休息一下吧.
________________________________________
對(duì)模型的操作
Jena提供了把模型當(dāng)作一個(gè)集合整體來(lái)操縱的三種操作方法. 即三種常用的集合操作:并, 交和差.
兩個(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)生如下的輸出:
<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:about="http://somewhere/JohnSmith/">
??? <vcard:EMAIL>
????? <vcard:internet>
??????? <rdf:value>John@somewhere.com</rdf:value>
????? </vcard:internet>
??? </vcard:EMAIL>
??? <vcard:N rdf:parseType="Resource">
????? <vcard:Given>John</vcard:Given>
????? <vcard:Family>Smith</vcard:Family>
??? </vcard:N>
??? <vcard:FN>John Smith</vcard:FN>
? </rdf:Description>
</rdf:RDF>
即便你不熟悉RDF/XML的語(yǔ)法細(xì)節(jié), 你仍然可以清楚的看見(jiàn)模型如同預(yù)期般被合并了. 我們可以在類(lèi)似的方式下運(yùn)算模型的交操作和差操作.
________________________________________
容器
RDF定義了一類(lèi)特殊的資源來(lái)表示事物的集合. 這些資源稱(chēng)為容器. 一個(gè)容器的成員可以是資源也可以是文本. 有三類(lèi)容器:?
一個(gè)BAG是一個(gè)無(wú)序的集合.
一個(gè)ALT是一個(gè)用來(lái)表示備選項(xiàng)的無(wú)序的集合.
一個(gè)SEQ是一個(gè)有序的集合.
一個(gè)容器由一個(gè)資源表示. 該資源會(huì)有一個(gè)rdf:type屬性, 屬性值為rdf:Bag, 或rdf:Alt, 或是rdf:Seq, 再或是這些類(lèi)型的子類(lèi)型, 這取決于容器的類(lèi)型. 容器的第一個(gè)成員是容器的rdf:_1的屬性所對(duì)應(yīng)的屬性值; 第二個(gè)成員是容器的rdf:_2屬性的值, 依此類(lèi)推. 這些rdf:_nnn屬性被稱(chēng)為序數(shù)屬性.
例如, 一個(gè)含有Smith的vcard的簡(jiǎn)單bag容器的模型可能會(huì)看起來(lái)是這樣的:
雖然bag容器的成員們被rdf:_1,rdf_2等等的屬性所表示, 但是這些屬性的順序卻并不重要. 即便我們將rdf:_1和rdf:_2的屬性值交換, 但是交換后的模型仍然表示相同的信息.
Alt是設(shè)計(jì)用來(lái)表示被選項(xiàng)的. 例如, 我們假定有一個(gè)表示軟件產(chǎn)品的資源. 它可能有一個(gè)屬性指示從哪里可以獲得次軟件產(chǎn)品. 這個(gè)屬性值可能是一個(gè)包含各種下載地址的Alt集合. Alt是無(wú)序的, 除了rdf:_1屬性有著特殊的意義, 它表示默認(rèn)的選項(xiàng).
盡管我們可以用基本的資源和屬性機(jī)制來(lái)處理容器, Jena為處理容器設(shè)計(jì)了顯式的接口和執(zhí)行類(lèi). 要避免在使用一個(gè)對(duì)象來(lái)操作容器的同時(shí)去使用低層方法來(lái)改變?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());
}
如果我們將次模型輸出可以看見(jiàn)它含有類(lèi)似如下的成分:
<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資源.
容器接口提供了一個(gè)iterator來(lái)列舉容器的內(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類(lèi)所提供的操縱容器的方法包括增加新成員, 在容器中間插入新成員和刪除已有的成員. Jena容器類(lèi)目前保證所使用的序數(shù)屬性列表會(huì)從rdf:_1開(kāi)始并且是相鄰的. RDF核心工作小組放松了此項(xiàng)限制, 以允許有局部的容器表示. 所以這是Jena將來(lái)可能會(huì)修改的地方之一.
________________________________________
關(guān)于文本(Literals)和數(shù)據(jù)類(lèi)型的更多探討
RDF文本(literals)并不僅僅是簡(jiǎn)單的字符串而已. 文本可能有一個(gè)語(yǔ)言標(biāo)簽來(lái)指示該文本使用的語(yǔ)言. 有英語(yǔ)語(yǔ)言標(biāo)簽的文本"chat"會(huì)被認(rèn)為與有著法語(yǔ)語(yǔ)言標(biāo)簽的文本"chat"是不同的. 這個(gè)奇怪的特性是原有RDF/XML語(yǔ)法產(chǎn)生的贗象(artefact).
另外, 事實(shí)上共有兩種文本. 在一種里, 字符串成分只是簡(jiǎn)單的字符串. 而在另一種里, 字符串成分被預(yù)計(jì)為格式良好的XML片段. 當(dāng)一個(gè)RDF模型被寫(xiě)成RDF/XML形式時(shí), 一個(gè)特殊的使用parseType='Literal'的屬性(attribute)構(gòu)造會(huì)被使用來(lái)表示它.
在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("<em>chat</em>", true));
// write out the Model
model.write(system.out);
會(huì)產(chǎn)生:
<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>
如果兩個(gè)文本被認(rèn)為是相同的, 它們一定都是XML的文本或都是簡(jiǎn)單的文本. 另外, 它們兩個(gè)要么都沒(méi)有語(yǔ)言標(biāo)簽, 要么有相同的語(yǔ)言標(biāo)簽. 對(duì)簡(jiǎn)單的文本而言, 兩者的字符串一定是相同的. XML文本的等同有兩點(diǎn)要求. 第一, 前面提到的要求必須被滿(mǎn)足并且字符串必須相同. 第二, 如果它們字符串的cannonicalization一樣的話(huà), 它們就是一樣的.(譯者注: 找不到cannonicalization中文的解釋.).
Jena接口也支持類(lèi)型文字. 老式的對(duì)待類(lèi)型文字的方法是把他們當(dāng)成是字符串的縮寫(xiě): 有類(lèi)型的值會(huì)通過(guò)Java的常用方法轉(zhuǎn)換為字符串, 并且這些字符串被存儲(chǔ)在模型中. 例如, 可以試一試(注意:對(duì)于簡(jiǎn)單類(lèi)型文字, 我們可以省略對(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");
產(chǎn)生的輸出如下:
_:A... <http://www.w3.org/2000/01/rdf-schema#label> "11" .
因?yàn)閮蓚€(gè)文本都是字符串"11", 所以只會(huì)有一個(gè)陳述被添加.
RDF核心工作小組定義了支持RDF數(shù)據(jù)類(lèi)型的機(jī)制. Jena支持那些使用類(lèi)型文字的機(jī)制; 但是本教程中不會(huì)對(duì)此討論.
________________________________________
術(shù)語(yǔ)表
空白結(jié)點(diǎn)?
表示一個(gè)資源, 但是并沒(méi)有指示該資源的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)頁(yè); 它也可以是一個(gè)具體的物理對(duì)象, 例如一棵樹(shù)或一輛車(chē); 它也可以是一個(gè)抽象的概念, 例如國(guó)際象棋或足球. 資源由URI命名.
陳述
RDF模型中的一個(gè)箭頭, 通常被解理解為一個(gè)事實(shí)
主體?
RDF模型中箭頭出發(fā)點(diǎn)的那個(gè)資源.
三元組?
一個(gè)含有主體, 謂詞和客體的結(jié)構(gòu). 是陳述的另一種稱(chēng)呼.
________________________________________
腳注
RDF資源的標(biāo)簽可以包括一個(gè)片段標(biāo)簽, 例如http://hostname/rdf/Tutorial/#ch-Introduction, 所以, 嚴(yán)格地講, 一個(gè)RDF資源是由一個(gè)URI表示的.
文本可以表示為一個(gè)字符串, 也可以有一個(gè)可選的語(yǔ)言編碼來(lái)表示該字符串的語(yǔ)言. 例如, 文本可能有一個(gè)表示英語(yǔ)的語(yǔ)言編碼"en", 而文本"deux"可能有一個(gè)表示法語(yǔ)的語(yǔ)言編碼"fr".
總結(jié)
以上是生活随笔為你收集整理的Jena文档《An Introduction to RDF and the Jena RDF API》的译文的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 巧妙设置yum软件库轻松解决软件包安装问
- 下一篇: 如何在Power 750上实现硬盘背板的