Introduction to working on LOD with Perl

Kjetil Kjernsmo <>.

Presentation at the Linked Open Data Hackfest, Oslo, 2010-01-04.

To install:

wget --no-check-certificate -O - | perl - RDF::LinkedData

Creative Commons Attribution-Sharealike 3.0 Norway License.

Important modules

And many more of differing relevance and maturity.

Resource Description Framework (RDF)

  1. From two documents to a triple.
    • Subject - Predicate - Object
  2. Add semantics to the link.
  3. Generalize the URI to identify anything.
  4. The object can be a string (with language or datatype).
  5. A collection of triples can be named with a URI.


Node types:

Important nodes: RDF::Trine::Node::Literal, RDF::Trine::Node::Resource, e.g:

my $subject = RDF::Trine::Node::Resource->new('');
my $predicate = RDF::Trine::Node::Resource->new('');
my $object = RDF::Trine::Node::Literal->new('Boeing 787', 'en');

Other node types: RDF::Trine::Node::Literal::XML, RDF::Trine::Node::Blank, RDF::Trine::Node::Variable, RDF::Trine::Node::Nil



my $statement = RDF::Trine::Statement->new($subject, $predicate, $object);

There's also RDF::Trine::Statement::Quad.



my $store = RDF::Trine::Store->new_with_config({
                                  storetype => 'DBI',
                                  name      => 'mymodel',
                                  dsn       => 'DBI:mysql:database=rdf',
                                  username  => 'dahut',
                                  password  => 'Str0ngPa55w0RD'



Various classes, for filtering, for doing unions properly, etc.

Typically, you would do:

my $model = RDF::Trine::Model->new($store);

or for experiments or temporary stuff:

my $model = RDF::Trine::Model->temporary_model;

Then, you can add your statement:




RDF/XML, Turtle, NQuads, NTriples, RDF/JSON, RDFa, TriG

Also, interface to Redland C parsers.

my $parser = RDF::Trine::Parser->new( 'turtle' );
$parser->parse_file_into_model( $base_uri, 'data.ttl', $model );



Used to manipulate data or SPARQL results iteratively, typically:

my $iterator = RDF::Trine::Iterator::Graph->new( \@data );
while (my $statement = $iterator->next) {
       # do something with $statement



RDF/XML, Turtle, NQuads, NTriples, RDF/JSON

From RDF::LinkedData:

my ($type, $s) = RDF::Trine::Serializer->negotiate(
                                         'request_headers' => $self->headers_in,
                                         base => $self->base,
                                         namespaces => $self->namespaces);
my $iterator = $model->bounded_description($node);
$output = $s->serialize_iterator_to_string ( $iterator );


Leading complete SPARQL 1.1 implementation!

Has no store on its own, that's all in RDF::Trine::Store.

Has a long list of classes:

You're not very likely to use most of this!


use strict;
use warnings;
use RDF::Trine;
use RDF::Query;

my $model = RDF::Trine::Model->temporary_model;
my $parser = RDF::Trine::Parser->new( 'turtle' );

my $turtle =<<'EOD';
@prefix foaf: <> .
@prefix rel: <> .
@prefix bio: <> .
@prefix xsd: <> .

<> a foaf:Person ;
    foaf:name "Kjetil Kjernsmo" ;
    rel:parentOf <> .

<> a foaf:Person ;
    foaf:givenName "Synne" ;
    bio:event [ bio:date "2008-11-25"^^xsd:date ;
                        a bio:Birth ] .

$parser->parse_into_model( '', $turtle, $model );

my $sparql=<<EOQ;
PREFIX foaf: <>
PREFIX rel: <>
PREFIX xsd: <>
SELECT ?childname WHERE {
        ?parent a foaf:Person ;
                rel:parentOf ?child .
          ?child  foaf:givenName ?childname .

my $query = RDF::Query->new( $sparql );
my $iterator = $query->execute( $model );
while (my $row = $iterator->next) {
       print $row->{ 'childname' }->as_string;


Always start writing tests!

use Test::More tests => 3;
use Test::RDF;
is_valid_rdf('</foo> <> "This is a Another test"@en .'
, 'turtle', 'Valid turtle');
is_rdf('</foo> <> "This is a Another test"@en .', 'turtle', 
       '<?xml version="1.0" encoding="utf-8"?><rdf:RDF xmlns:rdf=""><rdf:Description rdf:about="/foo"><ns0:label xmlns:ns0="" xml:lang="en">This is a Another test</ns0:label></rdf:Description></rdf:RDF>', 
       'rdfxml', 'Compare RDF/XML and Turtle');

isomorph_graphs($model1, $model2, 'Compare two models');

Quick install of RDF::LinkedData

On Ubuntu Maverick / Debian Squeeze, do first:

apt-get install libtest-nowarnings-perl libwww-perl libtest-www-mechanize-perl \
libtry-tiny-perl libplack-perl libtext-table-perl libtext-csv-perl \ 
libunicode-string-perl liblist-moreutils-perl libdbd-sqlite3-perl \
libtest-json-perl libmath-combinatorics-perl libxml-namespacesupport-perl \
libxml-libxml-perl liberror-perl libdigest-sha1-perl libset-scalar-perl \
libjson-perl libmoosex-log-log4perl-perl libnamespace-autoclean-perl \

Then, to install the Linked Data module in your home directory, go:

wget --no-check-certificate -O - | perl - RDF::LinkedData

Configuring RDF::LinkedData

Set environment:

export PERL5LIB=$HOME/perl5/lib/perl5
export PATH=$PATH:$HOME/perl5/bin

Then go

cd ~/.cpanm/latest-build/RDF-LinkedData-0.14/

Edit rdf_linkeddata.json in there to read:

  "base"  : "http://localhost:3000/matrikkel/",
  "store" : {
       "storetype"  : "Hexastore",
       "sources" : [ { 
                     "file" : "matrikkel1.ttl",
                     "syntax" : "turtle" 
                   } ], 

Running RDF::LinkedData basic server

plackup script/linked_data.psgi --host localhost --port 3000     

Go to http://localhost:3000/matrikkel/6 in your browser!

You should get a file named "data" with some RDF. That's a fully working Linked Data server!