python rdflibを使う
firefoxのaddonを書くとき、install.rdfを書くことになるのですが、そのチェックをrdflibでやってみることに。
addonのxpiに入れるinstall.rdfは以下のような感じです。
<?xml version="1.0"?> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:em="http://www.mozilla.org/2004/em-rdf#"> <rdf:Description rdf:about="urn:mozilla:install-manifest" em:id="${guid}" em:name="${name}" em:version="${version}" em:description="${description}" em:creator="${creator}" em:homepageURL="${home}" em:updateURL="${home}update.rdf" > <em:targetApplication rdf:nodeID="firefoxInfo" /> </rdf:Description> <rdf:Description rdf:nodeID="firefoxInfo" em:id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}" em:minVersion="3.0" em:maxVersion="3.*" /> </rdf:RDF>
読み込み&検索
前記index.rdfを読み込んで、情報を取り出すコードです。
import rdflib # load xml graph = rdflib.ConjunctiveGraph() graph.parse("install.rdf") # query em = rdflib.Namespace("http://www.mozilla.org/2004/em-rdf#") creators = graph.triples((None, em["creator"], None)) for target, prop, value in creators: print unicode(target) print unicode(value) pass # query: navigate app = [v for r, p, v in graph.triples((None, em.targetApplication, None))][0] max = [v for r, p, v in graph.triples((app, em.maxVersion, None))][0] print unicode(max) # query: sparql max = [max for max, in graph.query( """ select ?max where { ?a em:targetApplication ?b . ?b em:maxVersion ?max . }""", initNs={"em": em})][0] print unicode(max)
RDFのデータは、単純にはresource, property, valueのトリプレット(プログラミング風に書けば、resource[property] = value)をカラムとしたデータベースになってます。
ユーザーが操作するのは基本、Graphオブジェクトだけです。
検索は、triplesメソッドで、このトリプレットタプル(resource, property, value)のうち探したい部分をNoneにしたタプルを引数で渡すことで、Noneでない部分が一致したトリプレットタプルを返すgeneratorを返します。
namespaceはdict風に使うことも、property風に使うことも可能です。ただし、namespaceオブジェクトはunicodeクラスのサブクラスでもあるため、文字列のメソッド名(index、count, formatなど)とかぶる場合はproperty風には使えません。
parseメソッドでsparqlというSQL風言語でも問い合わせ可能です。その場合は、関連を手繰るような条件付けもできます。
作成&表示
前記index.rdfと同じものを作成するコードです。
import rdflib from rdflib import Literal as L from rdflib import RDF em = rdflib.Namespace("http://www.mozilla.org/2004/em-rdf#") # build new graph graph = rdflib.ConjunctiveGraph() graph.namespace_manager.bind("em", em) manifest = rdflib.URIRef("urn:mozilla:install-manifest") graph.add((manifest, em.id, L("${guid}"))) graph.add((manifest, em.name, L("${name}"))) graph.add((manifest, em.version, L("${version}"))) graph.add((manifest, em.description, L("${description}"))) graph.add((manifest, em.creator, L("${creator}"))) graph.add((manifest, em.homepageURL, L("${home}"))) graph.add((manifest, em.updateURL, L("${home}update.rdf"))) firefox = rdflib.BNode("firefoxInfo") graph.add((manifest, em.targetApplication, firefox)) graph.add((firefox, em.id, L("{ec8030f7-c20a-464f-9b0e-13a3a9e97384}"))) graph.add((firefox, em.minVersion, L("3.0"))) graph.add((firefox, em.maxVersion, L("3.*"))) # to xml print graph.serialize()
ちなみに、namespace_managerでプレフィックス指定を追加しなかったり、BNodeでid名を省略したりすると、適当な文字列が割り振られます。
BNodeはそのままではDescriptionになります。Seqなどは、BNodeに対して、RDF.typeにRDF.Seqを指定することになるようです。
# make seq seq = rdflib.BNode() graph.add((seq, RDF.type, RDF.Seq)) rdf = rdflib.Namespace("http://www.w3.org/1999/02/22-rdf-syntax-ns#") graph.add((seq, rdf._1, item1)) graph.add((seq, rdf._2, item2)) # get li items in seq: it cannot find by g.triples((seq, RDF.li, None)) items = [item for item in graph.seq(seq)]
なぜ、RDF.liでないかは、以下の文書を読めばわかるでしょう。
一旦RDFを手書きし、parseしてserializeしてみると、どういう構造かわかるかもしれません。