RDF - Object Mapping for RDF in .net

MakoLab open-source component

Collections

RDF supports a number of ways to represent a collection of child elements:

  • predicate with multiple objects
    • unordered
    • unique elements
  • RDF List (rdf:List)
    • ordered
    • allows duplicates
  • RDF Containers (rdf:Seq, rdf:Bag, rdf:Alt)
    • all three syntactically equal
    • rdf:Seq ordered by convention
    • allow duplicates

Multiple objects

The simplest way to associate one resource with multiple other resource is to simply use them as objects of a common property. Romantic Web calls that a simple collection. Such collection is a semantically an unordered bag. In NTriples it simply means to repeat the subject and predicate for each child element:

1
2
3
4
5
<tim-berners-lee> dbpedia-owl:award dbpedia:Order_of_the_British_Empire .
<tim-berners-lee> dbpedia-owl:award dbpedia:Royal_Designers_for_Industry .
<tim-berners-lee> dbpedia-owl:award dbpedia:Order_of_Merit .
<tim-berners-lee> dbpedia-owl:award dbpedia:Fellow_of_the_Royal_Society .
<tim-berners-lee> dbpedia-owl:award dbpedia:Royal_Academy_of_Engineering .

Turtle introduces a shorthand syntax, where comma is used to separate values and the subject/predicate aren’t repeated:

1
2
3
4
5
<tim-berners-lee> dbpedia-owl:award dbpedia:Order_of_the_British_Empire ,
                                    dbpedia:Royal_Designers_for_Industry ,
                                    dbpedia:Order_of_Merit ,
                                    dbpedia:Fellow_of_the_Royal_Society ,
                                    dbpedia:Royal_Academy_of_Engineering .

This is sufficient to represent .NET’s equivalent of IEnumerable or ICollection and is the default structure when mapping entity types. It can also be stated explicitly in mapping code.

1
2
3
4
5
6
7
8
using RomanticWeb.Mapping.Attributes;
using RomanticWeb.Mapping.Model;

public interface IPerson : IEntity
{
    [Property("dbpedia-owl", "award", StoreAs = StoreAs.SimpleCollection)]
    ICollection<Uri> Awards { get; set; }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
using RomanticWeb.Mapping.Fluent;

public class PersonMap : EntityMap<IPerson>
{
    public PersonMap()
    {
        Collection(p => p.Awards)
            .Term.Is("dbpedia-owl", "award"))
            .StoreAs.SimpleCollection();
    }
}

RDF collections

RDF collections are linked lists and thus they introduce intermediate list nodes, which connect subsequent elements. For this reason rdf:Lists have limited use but they are the common means to represent stable ordered sets. List nodes are connected using the rdf:rest predicate and rdf:first predicate points to the current list element. Each list node is usually a blank node, though it is not necessary. Here’s an example in NTriples:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
<tim-berners-lee> dbpedia-owl:award _:listNode1
_:listNode1 rdf:first dbpedia:Order_of_the_British_Empire .
_:listNode1 rdf:rest _:listNode2 .
_:listNode2 rdf:first dbpedia:Royal_Designers_for_Industry .
_:listNode2 rdf:rest _:listNode3 .
_:listNode3 rdf:first dbpedia:Order_of_Merit .
_:listNode3 rdf:rest _:listNode4 .
_:listNode4 rdf:first dbpedia:Fellow_of_the_Royal_Society .
_:listNode4 rdf:rest _:listNode5 .
_:listNode5 rdf:first dbpedia:Royal_Academy_of_Engineering .
_:listNode5 rdf:rest rdf:nil .

Again Turtle offers a more keyboard-friendly syntax for lists, which uses ( )

1
2
3
4
5
6
7
8
<tim-berners-lee> dbpedia-owl:award
(
    dbpedia:Order_of_the_British_Empire
    dbpedia:Royal_Designers_for_Industry
    dbpedia:Order_of_Merit
    dbpedia:Fellow_of_the_Royal_Society
    dbpedia:Royal_Academy_of_Engineering
) .

Mapping a .NET collection to an RDF collection is done similarly as with simple collection.

1
2
3
4
5
6
7
8
using RomanticWeb.Mapping.Attributes;
using RomanticWeb.Mapping.Model;

public interface IPerson : IEntity
{
    [Property("dbpedia-owl", "award", StoreAs = StoreAs.RdfList)]
    ICollection<Uri> Awards { get; set; }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
using RomanticWeb.Mapping.Fluent;

public class PersonMap : EntityMap<IPerson>
{
    public PersonMap()
    {
        Collection(p => p.Awards)
            .Term.Is("dbpedia-owl", "award"))
            .StoreAs.RdfList();
    }
}

RDF containers

RDF containers are currently not supported.

Dictionaries

RDF doesn’t include an established way to represent dictionaries as we know from .NET (aka hash maps). Romantic Web represents them as blank nodes, each having a key and value property. It is thus a simple collection of dictionary nodes.

1
2
3
4
5
6
<app> ex:settings [
                      ex:key "host", ex:value "127.6.6.6"
                  ],
                  [
                      ex:key "port", ex:value "1337"
                  ]

Because there is no guideline of what the ex:key and ex:value should be, Romantic Web allows users to map them to any properties desired.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
using RomanticWeb.Mapping.Attributes;
using RomanticWeb.Mapping.Model;

public interface IApp : IEntity
{
    [Dictionary("ex", "settings")]
    [Key("ex", "key")]
    [Value("ex", "value")]
    IDictionary<string, string> Settings { get; set; }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
using RomanticWeb.Mapping.Fluent;

public class AppMap : EntityMap<IApp>
{
    public AppMap()
    {
        Dictionary(p => p.Settings)
            .Term.Is("ex", "setting"))
            .KeyPredicate.Is("ex", "key"))
            .ValuePredicate.Is("ex", "value"));
    }
}