Tutorial: RDF Redland Perl Programming

(This is a reworked and updated version of an earlier version)

Update: You also may be interested in how to use OWL and reasoning with RDF::Redland.

The libRDF library is implemented in C (by Dave Beckett). To use it from Perl, there exists the RDF::Redland wrapper. The distribution consists of several packages which allow to

  • read (and parse) RDF documents and other popular formats for triple content;
  • to store RDF statements into an RDF model;
  • to store models themselves persistently in a hash (BDB) database or also into relational backends;
  • and to query such stores, lately also by supporting a considerable subset of SPARQL.

To learn about the details of the API, you may want to consult the manual page

$ man RDF::Redland

which refers to classes of objects such as RDF::Redland::Storage or RDF::Redland::Statement.

Initialization

Obviously a Perl program will need to import the package first:

use RDF::Redland;

After that, two different things have to be set up before real work can be done:

  • First, the database has to be named and configured, together with some options;
  • Then, inside this database we have to create the model itself, i.e. a workspace in which the RDF graph is to going be stored:

my $storage = new RDF::Redland::Storage (
                  "hashes",
                  "test",
                  "new='yes', hash-type='bdb', dir='/where/ever/'")
      or die "Failed to create RDF::Redland::Storage";

my $model   = new RDF::Redland::Model ($storage, "")
      or die "Failed to create RDF::Redland::Model for storage";

The first statement opens the database hashes and names the database on the file system test. This will be used for the files which are stored into the directory /where/ever/. The second statement opens the model inside the given store.

If you do not care to save your model to disk, then you can alternatively keep the whole database only in memory:

my $storage = new RDF::Redland::Storage ("hashes", "test",
                                         "hash-type='memory'");

Cleaning up at the end

With older versions of the distribution the Perl wrapper around libRDF has to be kicked explicitly to ensure that any changes you made to the model (and the storage) actually find their way onto the disk:

$model   = undef;
$storage = undef;

My assumption is that the above assignments with a new value (undef in this case) trigger inside libRDF the functionality to store away the existing data. Maybe this is not an issue with more recent versions.

Reading Statements from an RDF/XML file

To parse an RDF file, we first need to create a parser object which will have to do the actual work:

my $parser = new RDF::Redland::Parser (undef, "application/rdf+xml")
    or die "Failed to find parser\n";

The MIME type application/rdf+xml simply tells the parser which kind of documents it should expect.

Using the parser we can then ask it to drop all RDF statements it finds into our model:

my $uri = new RDF::Redland::URI ("file:test.rdf");
$parser->parse_into_model ($uri, $uri, $model);

Of course, we have to instruct the parser, where the RDF document is. So we created a URI object before. The fact, that we had to pass this URI twice into the method may look strange, but is a common programming pattern when dealing with XML or RDF information: The first parameter is the actual URI where to find the document, the second simply tells the parser to use the URI as base to make all relative URIs inside the document absolute ones.

You can also separate parsing and adding and make that explicit in a loop (some people like loops):

my $uri    = new RDF::Redland::URI ("file:test.rdf");
my $stream = $parser->parse_as_stream ($uri, $uri);
while (!$stream->end) {
   $model->add_statement ($stream->current);
   $stream->next;
}
$stream=undef;

The parser will so create a stream of statements. Each statement in the stream you can access via $stream->current. Now you can decide what to do with it; in the loop above we used the method add_statement to add the RDF triple into the model.

Outputting your model to RDF/XML

If you have already a model, then maybe you want to save its content as RDF/XML into a file:

my $serializer = new RDF::Redland::Serializer("rdfxml")
    or die "Failed to find serializer";

my $uri = new RDF::Redland::URI('file:test2.rdf');
$serializer->serialize_model_to_file ('test2.rdf', $uri, $model);
$serializer = undef;

Creating Statements manually

If you get your RDF content not from a file, but from somewhere else, then you need to create your statements yourself:

my $subject   = new RDF::Redland::URI ('http://rho.was.here/');
my $predicate = new RDF::Redland::URI ('http://www.tuwien.ac.at/semweb#explaining');
my $object    = new RDF::Redland::URI ('http://librdf.org');

my $statement = new RDF::Redland::Statement($subject, $predicate, $object);

(The URIs above I not only made up; they are also an extremely bad choice.)

If an object is supposed to contain a literal and is not a node identified with a URI, then you can create such a node easily by passing in the value, in the following case a string:

my $statement = new RDF::Redland::Statement(
                    $subject,
                    $predicate,
                    new RDF::Redland::Node("libRDF")));

Asserting and Retracting Statements

To add a statement to a model, you would use

$model->add_statement ($statement);

As building a statement first is slightly cumbersome, you can combine this into single step:

$model->add ($subject, $predicate, $object);

Note, that if such a statement already exists in the model, then there will actually be no change.

To get rid of a particular statement, you have to pass such a statement to the model

$model->remove_statement ($statement);

Browsing through the model

There are several methods to browse through the statements in a model. If you only want to list all of them, then you can create a statement stream from the model:

my $stream = $model->as_stream;
while (!$stream->end) {
   print "Statement: ",$stream->current->as_string,"\n";
   $stream->next;
}
$stream = undef;

If you you know what to look for, then you can set up a statement as a simple pattern in that it performs the role of a template:

my $template = new RDF::Redland::Statement (
    undef,
    new RDF::Redland::URI ('http://www.tuwien.ac.at/semweb#explaining'),
    undef);

Those parts which are defined (the predicate in the above case), are fixed, all undefined parts are variable. The method find_statements tries to identify all statements in the model which match this template:

my $stream = $model->find_statements ($template);
while (!$stream->end) {
   my $statement = $stream->current;
   print "Matching Statement: ", $statement->as_string,"\n";
   $stream->next;
}
$stream   = undef;
$template = undef;

Querying the model

libRDF also implemented long ago a candidate RDF query language RDQL while the future standard, SPARQL, was still a moving target. Such a query looks like an SQL statement:

my $q = new RDF::Redland::Query
        ("SELECT ?a ?c WHERE (?a dc:title ?c)
          USING dc FOR <http://purl.org/dc/elements/1.1/>"
);

Without going too much into this language here, it should be obvious that we are looking for all triples which have the form "something dc:title something-else", whereby the namespace prefix is explained in the USING clause.

Equipped with this query, we can execute it against the model:

my $res = $q->execute ($model);

while(!$res->finished) {
    for(my $i=0; $i < $res->bindings_count; $i++) {
        my $val = $res->binding_value($i);
        next unless defined $val; # optionals
        print "  ",$res->binding_name($i),"=",$val->as_string,"\n";
    }
    print "}\n";
    $res->next_result;
}
$res = undef;

This time we do not iterate over a stream of statements. The results we have asked for are actually only those nodes bound to the variables ?a and ?c as defined in the above SELECT clause. Inside the result loop we look at each of these bindings.

More recent versions of libRDF support a subset of SPARQL. Conveniently, the management code for querying is the same. The only thing which changes is that the query itself follows SPARQL syntax and that we have to tell the constructor just that:

my $q   = new RDF::Redland::Query("
    PREFIX dc: <http://purl.org/dc/elements/1.1/>
    SELECT ?a ?c
    WHERE
        {
         ?a dc:title ?c
         }"
,
    undef, undef, 'sparql');

Posted In

Very helpful stuff. Thanks

Very helpful stuff. Thanks for writing this up. Now if I could only find help with Redland::Storage and mysql ;)

Anonymous | Fri, 10/26/2007 - 15:28

It took me about an hour to

It took me about an hour to get it all but I finally did. Thanks, very useful!

sears parts (not verified) | Mon, 06/23/2008 - 16:13

good stuff

Yes I agree this helped me both enter and query data in Redland. Very clear and well done. _accurate_. I wish I could determine how to add another USING line to the query, but Im sure I will figure it out.

Anonymous (not verified) | Wed, 11/12/2008 - 21:08

Wow that was very concise

Wow that was very concise took me a while to get through it all but very useful thanks !

Jenny (not verified) | Tue, 12/02/2008 - 20:56

Redland Ruby Bindings

Thank you for this tutorial. I would like to point out that it is also valid for Ruby (you just have to change the syntax a bit) as there are Ruby bindings for Redland available as well. But as you said yourself (in another post) compiling these bindings can be tricky, esp. under Windows (using Cygwin).

Nevertheless it is possible to compile them even using MSVC6, so they would work with the Ruby 1-Click Installer for Windows. See my post about the problems to overcome. The compiled Redland libraries together with Ruby bindings can be downloaded from there as well.

And yes, I was despairing over this when I tried it the first time...

Regards

Melting Snowman (not verified) | Sat, 02/07/2009 - 15:11

Re: Redland Ruby Bindings

... compiling these bindings can be tricky, esp. under Windows (using Cygwin) ...

Yes, I failed at that time. Good to know that someone has tried harder. Thx for that info!

rho | Sat, 02/07/2009 - 18:32

Redland tutorial for c++ programming language

Hi,
You did an extraordinary work help for perl. Could you just do it for c++ users too.

Rama (not verified) | Fri, 02/19/2010 - 18:06

Re: Could you just do it for c++ users too

Sure, just leave your wishlist here, and I unshift it onto my TODO list.

rho | Fri, 02/19/2010 - 22:08