RDFデータをRubyで扱うためのライブラリとして,RDF.rbとそのプラグイン群がある.このライブラリで空白ノードを扱う際に注意する点について記載する.
結論
- RDF.rbで空白ノードを扱う場合,TurtleやRDF/XMLでシリアライズしない方がよい.
- 同一のノードIDを与えたはずの空白ノードが,別々のノードとして扱われる
- その際,ノードIDが正しく記述されない
- N-Triplesでのシリアライズには問題が無いので,一度N-Triplesでファイル出力してから他のツール(Raptorなど)でTurtleやRDF/XMLへ変換するのがよい.
- ファイル出力をせず,文字列から直接Raptorを使用したシリアライズをしたい場合は,rdf-raptorを用いるとよい
- rdf-raptorをrdf-turtleやrdf-rdfxmlと同時にrequireした場合,先に読み込んだ方の処理系が優先される
空白ノードの表現
RDF.rbでは,以下のクラスによってRDFグラフのノードやエッジを表現する
- RDF::URI
- URIを持つリソースやプロパティは,RDF::URIクラスのインスタンスとして表現する
- RDF::Node
- URIを持たないリソースは,RDF::Nodeクラスのインスタンスとして表現する
- RDF::Literal
- リテラルは,RDF::Literalクラスのインスタンスとして表現する
空白ノードは,上記のうちRDF::Nodeクラスのインスタンスとして表現する.Rubyのコードで記述すると以下のようになる.
require 'rdf'
# ノードIDが未定の場合
blank_node = RDF::Node.new
# ノードIDが決まっている場合(例: ノードID=1234)
blank_node = RDF::Node.new('1234')
ノードIDを指定して空白ノードを作成すると,_:1234
のようなノードが作成される.ノードIDを指定しない場合,_:g70161525762180
のようなノードが作成される.
空白ノードをRDF::Statementの主語や目的語にした場合は,以下のように出力される.
#<RDF::Statement:0x3ff686826614(<http://example.com/resource/1> <http://purl.org/dc/elements/1.1/creator> _:1234 .)>
空白ノードが_:1234
となっているのが確認できる.
グラフ中の空白ノード
RDF.rbでは,RDF::Graphクラスのインスタンスとして空のRDFグラフを作成できる.空のグラフに空白ノードを含むRDFトリプルを追加するコードは以下のようになる.
require 'rdf'
require 'rdf/vocab'
# 空のグラフの作成
graph = RDF::Graph.new
# 空白ノードを含むトリプルの作成
statement1 = [
RDF::URI.new('http://example.com/resource/1'),
RDF::Vocab::DC11.creator,
RDF::Node.new('1234'),
]
statement2 = [
RDF::Node.new('1234'),
RDF::Vocab::FOAF.name,
RDF::Literal.new('名無しさん'),
]
# トリプルをグラフに追加
graph << statement1
graph << statement2
# グラフの中身の確認
graph.each_statement do |statement|
p statement
end
コードの実行結果は以下のようになる.
#<RDF::Statement:0x3fd53504c384(<http://example.com/resource/1> <http://purl.org/dc/elements/1.1/creator> _:1234 .)>
#<RDF::Statement:0x3fd535045c50(_:1234 <http://xmlns.com/foaf/0.1/name> "名無しさん" .)>
この出力結果から,_:1234
という空白ノードを含む2つのトリプルがグラフに格納されていることがわかる.
シリアライズ後の空白ノード
RDF.rbでは,RDF/XMLやTurtleに対応するためのプラグインとして以下の2種類が挙げられる
- Pure Rubyなプラグイン
- rdf-turtle,rdf-rdfxmlなど
- Raptor (C言語で記述された外部ライブラリ) を用いるプラグイン
- rdf-raptor
rdf-turtleとrdf-rdfxmlを使用した場合
空白ノードを含むRDFグラフをN-Triples,Turtle,RDF/XMLで出力するコードが以下である.
require 'rdf'
require 'rdf/vocab'
require 'rdf/turtle'
require 'rdf/rdfxml'
graph = RDF::Graph.new
statement1 = [
RDF::URI.new('http://example.com/resource/1'),
RDF::Vocab::DC11.creator,
RDF::Node.new('1234'),
]
statement2 = [
RDF::Node.new('1234'),
RDF::Vocab::FOAF.name,
RDF::Literal.new('名無しさん'),
]
graph << statement1
graph << statement2
puts "\n\n==== N-Triples ====\n\n"
puts graph.dump(:ntriples)
puts "\n\n==== Turtle ====\n\n"
puts graph.dump(:turtle)
puts "\n\n==== RDF/XML ====\n\n"
puts graph.dump(:rdfxml)
このコードを実行すると,以下のように表示される.
==== N-Triples ====
<http://example.com/resource/1> <http://purl.org/dc/elements/1.1/creator> _:1234 .
_:1234 <http://xmlns.com/foaf/0.1/name> "名無しさん" .
==== Turtle ====
<http://example.com/resource/1> <http://purl.org/dc/elements/1.1/creator> [] .
[ <http://xmlns.com/foaf/0.1/name> "名無しさん"] .
==== RDF/XML ====
<?xml version='1.0' encoding='utf-8' ?>
<rdf:RDF xmlns:ns0='http://purl.org/dc/elements/1.1/' xmlns:ns1='http://xmlns.com/foaf/0.1/' xmlns:rdf='http://www.w3.org/1999/02/22-rdf-syntax-ns#'>
<rdf:Description rdf:about='http://example.com/resource/1'>
<ns0:creator rdf:nodeID='1234' />
</rdf:Description>
<rdf:Description>
<ns1:name>名無しさん</ns1:name>
</rdf:Description>
</rdf:RDF>
N-Triplesは_:1234
という空白ノードが正しく出力されているが,Turtleでは1つ目のトリプルの目的語と2つ目のトリプルの主語が別々の空白ノードとして扱われている.RDF/XMLも同様で,1つ目のトリプルの目的語はrdf:nodeID='1234'
として正しく出力されているが,2つ目のトリプルにノードIDが与えられていない.
rdf-raptorを使用した場合
空白ノードを含むRDFグラフをN-Triples,Turtle,RDF/XMLで出力するコードが以下である.
require 'rdf'
require 'rdf/vocab'
require 'rdf/raptor'
graph = RDF::Graph.new
statement1 = [
RDF::URI.new('http://example.com/resource/1'),
RDF::Vocab::DC11.creator,
RDF::Node.new('1234'),
]
statement2 = [
RDF::Node.new('1234'),
RDF::Vocab::FOAF.name,
RDF::Literal.new('名無しさん'),
]
graph << statement1
graph << statement2
puts "\n\n==== N-Triples ====\n\n"
puts graph.dump(:ntriples)
puts "\n\n==== Turtle ====\n\n"
puts graph.dump(:turtle)
puts "\n\n==== RDF/XML ====\n\n"
puts graph.dump(:rdfxml)
require以外は,rdf-turtleやrdf-rdfxmlを使用した場合と全く同じである.ちなみに,rdf-turtleやrdf-rdfxmlをrdf-raptorと一緒にrequireした場合は,先に読み込まれた方がシリアライズに使用される.
このコードを実行すると,以下のように表示される.
なお,事前にraptorのバージョン2.0.15をインストールしている.
==== N-Triples ====
<http://example.com/resource/1> <http://purl.org/dc/elements/1.1/creator> _:1234 .
_:1234 <http://xmlns.com/foaf/0.1/name> "\u540D\u7121\u3057\u3055\u3093" .
==== Turtle ====
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
<http://example.com/resource/1>
<http://purl.org/dc/elements/1.1/creator> [
<http://xmlns.com/foaf/0.1/name> "名無しさん"
] .
==== RDF/XML ====
<?xml version="1.0" encoding="utf-8"?>
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
<rdf:Description rdf:about="http://example.com/resource/1">
<ns0:creator xmlns:ns0="http://purl.org/dc/elements/1.1/" rdf:nodeID="1234"/>
</rdf:Description>
<rdf:Description rdf:nodeID="1234">
<ns0:name xmlns:ns0="http://xmlns.com/foaf/0.1/">名無しさん</ns0:name>
</rdf:Description>
</rdf:RDF>
rdf-turtleやrdf-rdfxmlを使用した場合とは異なり,同一の空白ノードが正しく処理されていることがわかる.
以上から,RDF.rbで空白ノードを含むTurtleファイルやRDF/XMLファイルを出力したい場合,Raptorをインストールできる環境ならばrdf-raptorを使うとよい.Raptorをインストールできないなら,一度N-Triplesで出力してから他のツールでTurtleやRDF/XMLに変換するのがよい.