RDF Redland Perl Programming

The libRDF is implemented in C. To use it from Perl, there exists the RDF::Redland wrapper (installed in the Linux lab). At this level, the package allows to read (and parse) RDF documents, to store RDF statements into a RDF model and to store models themselves persistently in an embedded database.

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

      $ man RDF::Redland
    

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 is done: First, the database has to be named, together with some options. Then, inside this database we have to create the model, i.e. a workspace in which the RDF graph is to be stored:

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

my $model   = new RDF::Redland::Model ($storage, "")
      || 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 keep the whole database only in memory:

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

Cleaning up at the end

The current Perl wrapper around librdf has to be kicked explicitely 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. This could be connected with using Swig for creating the wrapper.

Reading Statements from an RDF file

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

my $parser = new RDF::Redland::Parser (undef, "application/rdf+xml")
	|| die "Failed to find parser\n";
    
The MIME type application/rdf+xml simply tells the parser which kind of documents it should expect (I think). Using the parser we can ask 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 tell the parser, where the RDF document is, so we created a URI object before. That 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 (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 statement. 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

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

my $serializer = new RDF::Redland::Serializer("rdfxml")
	|| 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://www.bond.edu.au/staff/rho/');
my $predicate = new RDF::Redland::URI ('http://james.bond.edu.au/courses/semweb/vocab#explaining');
my $object    = new RDF::Redland::URI ('http://librdf.org');

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

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:

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 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 query in that it performs the role of a template:

my $template = new RDF::Redland::Statement (
                      undef,
                      new RDF::Redland::URI ('http://james.bond.edu.au/courses/semweb/vocab#explaining'), 
                      undef);
    

The 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 implements a candidate RDF query language (the standard is developed at the moment). 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 $results = $model->query_execute ($q);
while (!$results->finished) {
   for(my $i=0; $i < $results->bindings_count; $i++) {
      print "  ",$results->binding_name($i),"=",$results->binding_value($i)->as_string,"\n";
   }
   $results->next_result;
}
$results = 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 abouve SELECT clause. Inside the result loop we look at each of these bindings.