RDF Tree: Developer-friendly graph data

I have now written a follow-up to this post here

I want to make RDF data more developer-friendly. When you show a typical developer RDF, where they have previously been used to simple JSON or XML structures, they find the format confusing, and hard to code with. This is primarily because the data is a graph, and graphs don’t fit well with the tree structures of JSON and XML.

I have seen this problem tackled through the use of libraries that can parse and interpret the graph data, and present an easier interface to the developer. Whilst these have been useful, I still think there are some fundamental problems. JSON-LD also offers a solution to this problem, but is not sufficiently lightweight for environments where data structures change and develop regularly. I compare my approach with JSON-LD at the end of the post.

The problems described below are based on a particular problem I am trying to solve: I want to build developer-friendly APIs over data modelled as RDF, and obtained using SPARQL CONSTRUCT queries.

The problems fall into two categories of API call:

  1. Give me information about X (one thing)
  2. Give me information about all X1, X2, etc.. where all meets a certain condition (list)

Problems with a graph representing one thing

Problem: I don’t know what X is

If I call an API to find out about X, and get back the following RDF:

<uri:x> foaf:name "Bob Smith" .

Then it’s obvious what X is.

If I get this RDF back:

<uri:maybeX> foaf:name "Bob Smith" .
<uri:alsoMaybeX> foaf:name "Jane Smith" .
<uri:maybeX> foaf:knows <uri:alsoMaybeX> .

In this example, I have no idea what X is. This problem is usually solved using a heuristic, such as “the only resource of type T”. But these heuristics can be brittle, and changes to API data could easily break the heuristic.

I proposal to use a tree ontology to indicate the intention overall subject of the tree:

tree:tree tree:root <uri:x> .

Problems with a graph representing a list

Problem: ordering cannot easily expressed

If I call an API to get a list of things, then there is no commonly understood way to indicate the order of those things. The problem with general approaches to ordering in RDF, is that they don’t clearly indicate that the list specifically refers to the ordering of the API response. An API response is typically a dynamic ordering based on the current data, rather than an intrinsic ordering such as the ordering of events. An example would be “Page 4 of all Team GB athletes, ordered by surname, then first name”. In this example, the ordering is dynamic, and only known after the query data has been returned.

I propose to use a tree ontology to indicate a list of nodes in the graph that represent the API results. For example:

tree:tree tree:page "4"^^xsd:int .
tree:tree tree:first <uri:a> .
<uri:a> tree:next <uri:b> .
<uri:b> tree:next <uri:c> .
<uri:a> foaf:name "Person 1 in list" .
<uri:b> foaf:name "Person 2 in list" .
<uri:c> foaf:name "Person 3 in list" .

The additional tree ontology data above can be easily inserted into API responses by adding additional triples to the top of a SPARQL CONSTRUCT query.

Using RDF Tree ontology to build developer friendly data

Using the information from the tree ontology, it is possible to build developer friendly serialisations of the RDF data that are more directly usable for typical use-cases such as building tabular HTML or other visual representations of the data.

Building the RDF Tree from an RDF graph

The RDF tree is built by starting at the root (or multiple roots for lists) and traversing the graph until a rule causes the traversal to halt. The graph is traversed, breadth-first, following predicates in both directions. I used the following three halting rules, which seems to make effective trees for the use-cases I have looked at:

  1. If a parent node is present in the tree with the same resource as one traversed to, halt the traversal
  2. Do not follow the rdf:type property in the inverse direction
  3. If a list item resource is defined with the same resource as one traversed to, halt the traversal after one additional level in the tree

The following two examples show JSON RDF Tree serialisations of Olympics data, applying the rules above. The original Turtle data is shown, along with the resultant, proposed RDF Tree serialisation. The ‘^’ symbol is used to represent triples followed in the inverse direction of the predicate.

One athlete as RDF Turtle

@prefix par:     <http://purl.org/vocab/participation/schema#> .
@prefix rdfs:    <http://www.w3.org/2000/01/rdf-schema#> .
@prefix geo:     <http://www.bbc.co.uk/ontologies/geopolitical/> .
@prefix foaf:    <http://xmlns.com/foaf/0.1/> .
@prefix owl:     <http://www.w3.org/2002/07/owl#> .
@prefix domain:  <http://www.bbc.co.uk/ontologies/domain/> .
@prefix oly:     <http://www.bbc.co.uk/ontologies/2012olympics/> .
@prefix xsd:     <http://www.w3.org/2001/XMLSchema#> .
@prefix rdf:     <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix sport: <http://www.bbc.co.uk/ontologies/sport/> .
@prefix tree:  <http://purl.org/rdf-tree/> .

tree:tree tree:start <http://www.bbc.co.uk/things/4e40ce40-b632-4a42-98d7-cf97067f7bf9#id> .

<http://www.bbc.co.uk/things/7ef7ffdf-f101-4470-adc0-38a5abac9122#id> a sport:CompetitiveSportingOrganisation ;
      oly:territory <http://www.bbc.co.uk/things/territories/gb#id> ;
      domain:document <http://www.bbc.co.uk/sport/olympics/2012/countries/great-britain> ;
        domain:shortName "Great Britain & N. Ireland"^^xsd:string ;
      domain:name "Team GB"^^xsd:string .

<http://www.bbc.co.uk/things/4e40ce40-b632-4a42-98d7-cf97067f7bf9#id> a sport:Person ;
      par:role_at <http://www.bbc.co.uk/things/7ef7ffdf-f101-4470-adc0-38a5abac9122#id> ;
      oly:dateOfBirth "1976-10-24"^^xsd:date ;
      oly:gender "M"^^xsd:string ;
      oly:height "172.0"^^xsd:float ;
      oly:weight "72.0"^^xsd:float ;
      domain:name "Ben Ainslie"^^xsd:string ;
      sport:competesIn <http://www.bbc.co.uk/things/2012/sam002#id>, <http://www.bbc.co.uk/things/2012/sam005#id> ;
      sport:discipline <http://www.bbc.co.uk/things/d65c5dce-f5e4-4340-931b-16ca1848d092#id> ;
      domain:document <http://www.bbc.co.uk/sport/olympics/2012/athletes/4e40ce40-b632-4a42-98d7-cf97067f7bf9>, <http://www.facebook.com/pages/Ben-Ainslie/108182689201922> ;
      foaf:familyName "Ainslie"^^xsd:string ;
      foaf:givenName "Ben"^^xsd:string .

<http://www.facebook.com/pages/Ben-Ainslie/108182689201922> a domain:Document ; 
   domain:documentType <http://www.bbc.co.uk/things/document-types/external> , <http://www.bbc.co.uk/things/document-types/facebook> .

<http://www.bbc.co.uk/sport/olympics/2012/athletes/4e40ce40-b632-4a42-98d7-cf97067f7bf9> a domain:Document ;
   domain:domain <http://www.bbc.co.uk/things/domains/olympics2012> ;
   domain:documentType <http://www.bbc.co.uk/things/document-types/bbc-document> .

<http://www.bbc.co.uk/things/2012/sam002#id> a sport:MedalCompetition ;
        domain:name "Sailing - Men's Finn"^^xsd:string ;
        domain:shortName "Men's Finn"^^xsd:string ;
      domain:externalId <urn:ioc2012:SAM002000> ;
      domain:document <http://www.bbc.co.uk/sport/olympics/2012/sports/sailing/events/mens-finn> .

<http://www.bbc.co.uk/things/2012/sam005#id> a sport:MedalCompetition ;
      domain:name "Sailing - Men's 470"^^xsd:string ;
        domain:shortName "Men's 470"^^xsd:string ;
      domain:externalId <urn:ioc2012:SAM005000> ;
        domain:document <http://www.bbc.co.uk/sport/olympics/2012/sports/sailing/events/mens-470> .

<http://www.bbc.co.uk/things/d65c5dce-f5e4-4340-931b-16ca1848d092#id> a sport:SportsDiscipline ;
      domain:document <http://www.bbc.co.uk/sport/olympics/2012/sports/sailing> ;
      domain:name "Sailing"^^xsd:string .

<http://www.bbc.co.uk/things/territories/gb#id> a geo:Territory ;
      domain:name "the United Kingdom of Great Britain and Northern Ireland"^^xsd:string ;
      geo:isInGroup <http://www.bbc.co.uk/things/81b14df8-f9d2-4dff-a676-43a1a9a5c0a5#id> .

<http://www.bbc.co.uk/things/81b14df8-f9d2-4dff-a676-43a1a9a5c0a5#id> a geo:Group ;
      domain:name "Europe"^^xsd:string ;
      geo:groupType <http://www.bbc.co.uk/things/group-types/bbc-news-geo-regions> .

One athlete RDF Tree JSON

{
  "rdf:about": "http://www.bbc.co.uk/things/4e40ce40-b632-4a42-98d7-cf97067f7bf9#id",
  "domain:name": "Ben Ainslie",
  "foaf:familyName": "Ainslie",
  "foaf:givenName": "Ben",
  "oly:dateOfBirth": "1976-10-24",
  "oly:gender": "M",
  "oly:height": "172.0",
  "oly:weight": "72.0",
  "domain:document": [
    {
      "rdf:about": "http://www.facebook.com/pages/Ben-Ainslie/108182689201922",
      "domain:documentType": [
        {
          "rdf:about": "http://www.bbc.co.uk/things/document-types/facebook"
        },
        {
          "rdf:about": "http://www.bbc.co.uk/things/document-types/external"
        }
      ],
      "rdf:type": {
        "rdf:about": "http://www.bbc.co.uk/ontologies/domain/Document"
      }
    },
    {
      "rdf:about": "http://www.bbc.co.uk/sport/olympics/2012/athletes/4e40ce40-b632-4a42-98d7-cf97067f7bf9",
      "domain:documentType": {
        "rdf:about": "http://www.bbc.co.uk/things/document-types/bbc-document"
      },
      "domain:domain": {
        "rdf:about": "http://www.bbc.co.uk/things/domains/olympics2012"
      },
      "rdf:type": {
        "rdf:about": "http://www.bbc.co.uk/ontologies/domain/Document"
      }
    }
  ],
  "par:role_at": {
    "rdf:about": "http://www.bbc.co.uk/things/7ef7ffdf-f101-4470-adc0-38a5abac9122#id",
    "domain:name": "Team GB",
    "domain:shortName": "Great Britain \u0026 N. Ireland",
    "domain:document": {
      "rdf:about": "http://www.bbc.co.uk/sport/olympics/2012/countries/great-britain"
    },
    "oly:territory": {
      "rdf:about": "http://www.bbc.co.uk/things/territories/gb#id",
      "domain:name": "the United Kingdom of Great Britain and Northern Ireland",
      "geo:isInGroup": {
        "rdf:about": "http://www.bbc.co.uk/things/81b14df8-f9d2-4dff-a676-43a1a9a5c0a5#id",
        "domain:name": "Europe",
        "geo:groupType": {
          "rdf:about": "http://www.bbc.co.uk/things/group-types/bbc-news-geo-regions"
        },
        "rdf:type": {
          "rdf:about": "http://www.bbc.co.uk/ontologies/geopolitical/Group"
        }
      },
      "rdf:type": {
        "rdf:about": "http://www.bbc.co.uk/ontologies/geopolitical/Territory"
      }
    },
    "rdf:type": {
      "rdf:about": "http://www.bbc.co.uk/ontologies/sport/CompetitiveSportingOrganisation"
    }
  },
  "sport:competesIn": [
    {
      "rdf:about": "http://www.bbc.co.uk/things/2012/sam002#id",
      "domain:name": "Sailing - Men\u0027s Finn",
      "domain:shortName": "Men\u0027s Finn",
      "domain:document": {
        "rdf:about": "http://www.bbc.co.uk/sport/olympics/2012/sports/sailing/events/mens-finn"
      },
      "domain:externalId": {
        "rdf:about": "urn:ioc2012:SAM002000"
      },
      "rdf:type": {
        "rdf:about": "http://www.bbc.co.uk/ontologies/sport/MedalCompetition"
      }
    },
    {
      "rdf:about": "http://www.bbc.co.uk/things/2012/sam005#id",
      "domain:name": "Sailing - Men\u0027s 470",
      "domain:shortName": "Men\u0027s 470",
      "domain:document": {
        "rdf:about": "http://www.bbc.co.uk/sport/olympics/2012/sports/sailing/events/mens-470"
      },
      "domain:externalId": {
        "rdf:about": "urn:ioc2012:SAM005000"
      },
      "rdf:type": {
        "rdf:about": "http://www.bbc.co.uk/ontologies/sport/MedalCompetition"
      }
    }
  ],
  "sport:discipline": {
    "rdf:about": "http://www.bbc.co.uk/things/d65c5dce-f5e4-4340-931b-16ca1848d092#id",
    "domain:name": "Sailing",
    "domain:document": {
      "rdf:about": "http://www.bbc.co.uk/sport/olympics/2012/sports/sailing"
    },
    "rdf:type": {
      "rdf:about": "http://www.bbc.co.uk/ontologies/sport/SportsDiscipline"
    }
  },
  "rdf:type": {
    "rdf:about": "http://www.bbc.co.uk/ontologies/sport/Person"
  }
}

List of three athletes as RDF Turtle

@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix owl: <http://www.w3.org/2002/07/owl#> .
@prefix par: <http://purl.org/vocab/participation/schema#> .
@prefix foaf: <http://xmlns.com/foaf/0.1/> .
@prefix domain: <http://www.bbc.co.uk/ontologies/domain/> .
@prefix sport: <http://www.bbc.co.uk/ontologies/sport/> .
@prefix oly: <http://www.bbc.co.uk/ontologies/2012olympics/> .
@prefix geo-pol: <http://www.bbc.co.uk/ontologies/geopolitical/> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
@prefix tree:  <http://purl.org/rdf-tree/> .

tree:tree tree:first <http://www.bbc.co.uk/things/4e40ce40-b632-4a42-98d7-cf97067f7bf9#id> .
<http://www.bbc.co.uk/things/4e40ce40-b632-4a42-98d7-cf97067f7bf9#id> tree:next <http://www.bbc.co.uk/things/f2798806-4e54-47ff-a1ec-32beefde2058#id> .
<http://www.bbc.co.uk/things/f2798806-4e54-47ff-a1ec-32beefde2058#id> tree:next <http://www.bbc.co.uk/things/82f5db84-0591-49ee-b6f4-a1d26e9381fb#id> .

<http://www.bbc.co.uk/things/4e40ce40-b632-4a42-98d7-cf97067f7bf9#id> a sport:Person ;
    oly:isMedallistFor <http://www.bbc.co.uk/things/3143ecc6-ffa4-4446-9d13-ee60801c4881#id> ;
    domain:name "Ben Ainslie"^^xsd:string ;
    domain:canonicalName "Ben Ainslie"^^xsd:string ;
    foaf:givenName "Ben"^^xsd:string ;
    foaf:familyName "Ainslie"^^xsd:string ;
    oly:gender "M"^^xsd:string ;
    sport:discipline <http://www.bbc.co.uk/things/d65c5dce-f5e4-4340-931b-16ca1848d092#id> ;
    domain:document <http://www.bbc.co.uk/sport/olympics/2012/athletes/4e40ce40-b632-4a42-98d7-cf97067f7bf9> .

<http://www.bbc.co.uk/things/d65c5dce-f5e4-4340-931b-16ca1848d092#id> a sport:SportsDiscipline ;
  domain:name "Sailing"^^xsd:string ;
  domain:document <http://www.bbc.co.uk/sport/olympics/2012/sports/sailing> .

_:node166n6c6vlx37 a sport:CompetesForRole ;
    par:holder <http://www.bbc.co.uk/things/4e40ce40-b632-4a42-98d7-cf97067f7bf9#id> ;
    par:role_at <http://www.bbc.co.uk/things/7ef7ffdf-f101-4470-adc0-38a5abac9122#id> .

<http://www.bbc.co.uk/things/7ef7ffdf-f101-4470-adc0-38a5abac9122#id> a sport:CompetitiveSportingOrganisation ;
  domain:name "Team GB"^^xsd:string ;
  domain:shortName "Great Britain & N. Ireland"^^xsd:string ;
  domain:document <http://www.bbc.co.uk/sport/olympics/2012/countries/great-britain> .

<http://www.bbc.co.uk/things/8d6ae957-d338-442b-99d7-190f20b78dd4#id> oly:oneToWatch <http://www.bbc.co.uk/things/4e40ce40-b632-4a42-98d7-cf97067f7bf9#id> .

<http://www.bbc.co.uk/things/82f5db84-0591-49ee-b6f4-a1d26e9381fb#id> a sport:Person ;
  domain:name "Usain Bolt"^^xsd:string ;
  domain:canonicalName "Usain Bolt"^^xsd:string ;
    foaf:givenName "Usain"^^xsd:string ;
    foaf:familyName "Bolt"^^xsd:string ;
  oly:gender "M"^^xsd:string ;
  oly:worldOlympicDream "true"^^xsd:boolean ;
  sport:discipline <http://www.bbc.co.uk/things/b3a086df-ab42-2b44-be8b-76b600bfcdce#id> ;
  domain:document <http://www.bbc.co.uk/sport/olympics/2012/athletes/82f5db84-0591-49ee-b6f4-a1d26e9381fb> .

<http://www.bbc.co.uk/things/b3a086df-ab42-2b44-be8b-76b600bfcdce#id> a sport:SportsDiscipline ;
    domain:name "Athletics"^^xsd:string ;
  domain:document <http://www.bbc.co.uk/sport/olympics/2012/sports/athletics> .

_:node166n6c6vlx113 a sport:CompetesForRole ;
    par:holder <http://www.bbc.co.uk/things/82f5db84-0591-49ee-b6f4-a1d26e9381fb#id> ;
    par:role_at <http://www.bbc.co.uk/things/76369f3b-65a0-4e69-8c52-859adfdefa49#id> .

<http://www.bbc.co.uk/things/76369f3b-65a0-4e69-8c52-859adfdefa49#id> a sport:CompetitiveSportingOrganisation ;
    domain:name "Jamaica"^^xsd:string ;
  domain:shortName "Jamaica"^^xsd:string ;
  domain:externalId <urn:ioc2012:jam> ;
  domain:document <http://www.bbc.co.uk/sport/olympics/2012/countries/jamaica> .

<http://www.bbc.co.uk/things/f2798806-4e54-47ff-a1ec-32beefde2058#id> a sport:Person ;
  oly:isMedallistFor <http://www.bbc.co.uk/things/3143ecc6-ffa4-4446-9d13-ee60801c4881#id> ;
  domain:name "Majlinda Kelmendi"^^xsd:string ;
  domain:canonicalName "Majlinda Kelmendi"^^xsd:string ;
    foaf:givenName "Majlinda"^^xsd:string ;
    foaf:familyName "Kelmendi"^^xsd:string ;
  oly:gender "W"^^xsd:string ;
  sport:discipline <http://www.bbc.co.uk/things/654f550c-0c2d-2341-a8f5-66e3a9ba28ba#id> ;
  domain:document <http://www.bbc.co.uk/sport/olympics/2012/athletes/f2798806-4e54-47ff-a1ec-32beefde2058> .

<http://www.bbc.co.uk/things/654f550c-0c2d-2341-a8f5-66e3a9ba28ba#id> a sport:SportsDiscipline ;
    domain:name "Judo"^^xsd:string ;
  domain:document <http://www.bbc.co.uk/sport/olympics/2012/sports/judo> .

_:node166n6c6vlx445 a sport:CompetesForRole ;
    par:holder <http://www.bbc.co.uk/things/f2798806-4e54-47ff-a1ec-32beefde2058#id> ;
    par:role_at <http://www.bbc.co.uk/things/7ef7ffdf-f101-4470-adc0-38a5abac9122#id> .

List of three athletes RDF Tree JSON

Note the repeating country nodes: these are useful for tabular representations.

[
  {
    "rdf:about": "http://www.bbc.co.uk/things/4e40ce40-b632-4a42-98d7-cf97067f7bf9#id",
    "domain:canonicalName": "Ben Ainslie",
    "domain:name": "Ben Ainslie",
    "foaf:familyName": "Ainslie",
    "foaf:givenName": "Ben",
    "oly:gender": "M",
    "^oly:oneToWatch": {
      "rdf:about": "http://www.bbc.co.uk/things/8d6ae957-d338-442b-99d7-190f20b78dd4#id"
    },
    "^par:holder": {
      "par:role_at": {
        "rdf:about": "http://www.bbc.co.uk/things/7ef7ffdf-f101-4470-adc0-38a5abac9122#id",
        "domain:name": "Team GB",
        "domain:shortName": "Great Britain \u0026 N. Ireland",
        "^par:role_at": {
          "par:holder": {
            "rdf:about": "http://www.bbc.co.uk/things/f2798806-4e54-47ff-a1ec-32beefde2058#id"
          },
          "rdf:type": {
            "rdf:about": "http://www.bbc.co.uk/ontologies/sport/CompetesForRole"
          }
        },
        "domain:document": {
          "rdf:about": "http://www.bbc.co.uk/sport/olympics/2012/countries/great-britain"
        },
        "rdf:type": {
          "rdf:about": "http://www.bbc.co.uk/ontologies/sport/CompetitiveSportingOrganisation"
        }
      },
      "rdf:type": {
        "rdf:about": "http://www.bbc.co.uk/ontologies/sport/CompetesForRole"
      }
    },
    "domain:document": {
      "rdf:about": "http://www.bbc.co.uk/sport/olympics/2012/athletes/4e40ce40-b632-4a42-98d7-cf97067f7bf9"
    },
    "oly:isMedallistFor": {
      "rdf:about": "http://www.bbc.co.uk/things/3143ecc6-ffa4-4446-9d13-ee60801c4881#id",
      "^oly:isMedallistFor": {
        "rdf:about": "http://www.bbc.co.uk/things/f2798806-4e54-47ff-a1ec-32beefde2058#id"
      }
    },
    "sport:discipline": {
      "rdf:about": "http://www.bbc.co.uk/things/d65c5dce-f5e4-4340-931b-16ca1848d092#id",
      "domain:name": "Sailing",
      "domain:document": {
        "rdf:about": "http://www.bbc.co.uk/sport/olympics/2012/sports/sailing"
      },
      "rdf:type": {
        "rdf:about": "http://www.bbc.co.uk/ontologies/sport/SportsDiscipline"
      }
    },
    "rdf:type": {
      "rdf:about": "http://www.bbc.co.uk/ontologies/sport/Person"
    }
  },
  {
    "rdf:about": "http://www.bbc.co.uk/things/f2798806-4e54-47ff-a1ec-32beefde2058#id",
    "domain:canonicalName": "Majlinda Kelmendi",
    "domain:name": "Majlinda Kelmendi",
    "foaf:familyName": "Kelmendi",
    "foaf:givenName": "Majlinda",
    "oly:gender": "W",
    "^par:holder": {
      "par:role_at": {
        "rdf:about": "http://www.bbc.co.uk/things/7ef7ffdf-f101-4470-adc0-38a5abac9122#id",
        "domain:name": "Team GB",
        "domain:shortName": "Great Britain \u0026 N. Ireland",
        "^par:role_at": {
          "par:holder": {
            "rdf:about": "http://www.bbc.co.uk/things/4e40ce40-b632-4a42-98d7-cf97067f7bf9#id"
          },
          "rdf:type": {
            "rdf:about": "http://www.bbc.co.uk/ontologies/sport/CompetesForRole"
          }
        },
        "domain:document": {
          "rdf:about": "http://www.bbc.co.uk/sport/olympics/2012/countries/great-britain"
        },
        "rdf:type": {
          "rdf:about": "http://www.bbc.co.uk/ontologies/sport/CompetitiveSportingOrganisation"
        }
      },
      "rdf:type": {
        "rdf:about": "http://www.bbc.co.uk/ontologies/sport/CompetesForRole"
      }
    },
    "domain:document": {
      "rdf:about": "http://www.bbc.co.uk/sport/olympics/2012/athletes/f2798806-4e54-47ff-a1ec-32beefde2058"
    },
    "oly:isMedallistFor": {
      "rdf:about": "http://www.bbc.co.uk/things/3143ecc6-ffa4-4446-9d13-ee60801c4881#id",
      "^oly:isMedallistFor": {
        "rdf:about": "http://www.bbc.co.uk/things/4e40ce40-b632-4a42-98d7-cf97067f7bf9#id"
      }
    },
    "sport:discipline": {
      "rdf:about": "http://www.bbc.co.uk/things/654f550c-0c2d-2341-a8f5-66e3a9ba28ba#id",
      "domain:name": "Judo",
      "domain:document": {
        "rdf:about": "http://www.bbc.co.uk/sport/olympics/2012/sports/judo"
      },
      "rdf:type": {
        "rdf:about": "http://www.bbc.co.uk/ontologies/sport/SportsDiscipline"
      }
    },
    "rdf:type": {
      "rdf:about": "http://www.bbc.co.uk/ontologies/sport/Person"
    }
  },
  {
    "rdf:about": "http://www.bbc.co.uk/things/82f5db84-0591-49ee-b6f4-a1d26e9381fb#id",
    "domain:canonicalName": "Usain Bolt",
    "domain:name": "Usain Bolt",
    "foaf:familyName": "Bolt",
    "foaf:givenName": "Usain",
    "oly:gender": "M",
    "oly:worldOlympicDream": "true",
    "^par:holder": {
      "par:role_at": {
        "rdf:about": "http://www.bbc.co.uk/things/76369f3b-65a0-4e69-8c52-859adfdefa49#id",
        "domain:name": "Jamaica",
        "domain:shortName": "Jamaica",
        "domain:document": {
          "rdf:about": "http://www.bbc.co.uk/sport/olympics/2012/countries/jamaica"
        },
        "domain:externalId": {
          "rdf:about": "urn:ioc2012:jam"
        },
        "rdf:type": {
          "rdf:about": "http://www.bbc.co.uk/ontologies/sport/CompetitiveSportingOrganisation"
        }
      },
      "rdf:type": {
        "rdf:about": "http://www.bbc.co.uk/ontologies/sport/CompetesForRole"
      }
    },
    "domain:document": {
      "rdf:about": "http://www.bbc.co.uk/sport/olympics/2012/athletes/82f5db84-0591-49ee-b6f4-a1d26e9381fb"
    },
    "sport:discipline": {
      "rdf:about": "http://www.bbc.co.uk/things/b3a086df-ab42-2b44-be8b-76b600bfcdce#id",
      "domain:name": "Athletics",
      "domain:document": {
        "rdf:about": "http://www.bbc.co.uk/sport/olympics/2012/sports/athletics"
      },
      "rdf:type": {
        "rdf:about": "http://www.bbc.co.uk/ontologies/sport/SportsDiscipline"
      }
    },
    "rdf:type": {
      "rdf:about": "http://www.bbc.co.uk/ontologies/sport/Person"
    }
  }
]

Syntax and feedback

I have written a library to perform RDF to RDF Tree conversion, and I am looking for feedback on the syntax and general approach before I finalise the details.

The design of RDF Tree is intended to be lossless, from an information perspective, but to maximise practical use by developers.

Comparison to JSON LD

This approach above is a lightweight alternative to JSON LD. The framing approached used by JSON LD to assure consistent and predictable access to data is replaced with a more dynamic approach where the tree root is defined by the RDF API response. I have identified following differences to JSON LD:

  • RDF Tree is focused on developer simplicity: this includes the creators and consumers of APIs. An example of this would be the decision to continue to use prefixes for predicates. This makes the data easy to produce, and avoid the complexities of ‘contextualisation’ of shorter predicate names.
  • RDF Tree is not intended to be re-used as RDF data. APIs that produce RDF Tree would need to also produce RDF. Those interested in tree-like or tabular data would use RDF Tree, those interested in the graph data would use the RDF.
  • As the ontology structures and SPARQL queries change, little or no maintenance is required to continue to support predictable response data.
  • Any tree structured data can be produced (XML can be built from the same abstract tree)
About these ads

9 thoughts on “RDF Tree: Developer-friendly graph data

  1. Hello Dave!
    Very interesting post – thank you! I have a few questions about the proposed format. First of all, I am a bit unsure about the comparison with JSON-LD. For example, I think JSON-LD definitely was developed with simplicity in mind, and was specifically targetting web developers. Your representation of lists also seems quite close to ‘normal’ RDF lists or exploded lists e.g. using the OLO ontology, would it be possible to re-use those? Finally, it is looking like the proposed format does not include enough information to parse it as RDF (e.g. missing namespaces). If that’s indeed not a requirement, why bother with namespaces at all and to a lesser extent with URIs? If it is a requirement, then I would be curious to see how that solution compares to a JSON-LD + OLO solution.
    Cheers,
    Yves

    • Hi Yves!

      To clarify where I’m going with this approach, I’ll explain the scenario in more detail…

      I’ve already got a API endpoint that returns various flavours of RDF (Turtle, RDF XML, etc..) using content negotiation, but these formats are hard for developers to handle if they want to build a typical, less semantic app, such as one that renders a few tables/values on an HTML page (a very common scenario for us).

      From this perspective, it doesn’t matter if the full information is in the RDF Tree, allowing the retrieval of the RDF graph, because you can just get the original RDF from the same API endpoint… and I want to take advantage of this! The requirements are therefore as follows:

      - Easy as possible to create the data, therefore minimal setup-per-SPARQL-query
      - Easy as possible to read the data, therefore, stable and predictable structure (as long as the ontologies are the same)
      - Minimal maintenance if/when the ontologies change: hence the specification only of the tree root(s)

      I agree that the JSON-LD original intent was to simplify the data, but I feel the verbosity of the specification is because another requirement was kept (which I don’t have), to keep the full semantic data.

      With regard to OLO, I do feel that I am trying to do something a little different to normal. OLO (from what I read) was about improving the modelling of ordering so that ordered lists were robust to change, easier to query and unambiguous. The tree ontology is only intend for use in individual API responses – it does not represent anything other than the order or root of the returned data, and cannot be used in any other context. It is not a fundamental ordering such as a playlist or event sequence, it is dynamic metadata used to frame the display or processing of a single API response. Because of this, tree:next does the trick (I have considered not using tree:first). It is one triple per ‘thing’ in the list, and provides the simplest form of linked list. A very simple linked list is all that is required because the ordering is only valid in the context of the API response. To clarify this point: the tree ontology should never be used outside of an API response, and definitely never stored in a triple store!

      Nick Humphrey made the point about “why bother with namespaces or URIs at all?” also, and it’s a good one. I certainly think that 90% of use cases would not require this data, but I think that dereferencable URIs would help developers understand the data better, and might occassionally be used. I kept prefixes in, but namespaces out, to avoid name-clashes, but avoid adding the unused namespaces, as these API consumers don’t care about semantics.

      All this said, I will endeavour to move the syntax closer to JSON-LD with @id and @type for example. I would also like to explore mapping the tree ontology to automatically generated JSON-LD frames and using this to generate the necessary JSON-LD, but I am struggling with a lack of a mature Java JSON-LD library. We also have clients keen on XML representations with the same design principles, which I have already produced from the RDF Tree model.

      But please do keep the feedback coming! I am getting the feeling I am “going against the grain” with this, but I do really feel that my use-case does seem a bit different. In this use-case, JSON-LD, as a standard and a set of libraries, does not seem like the ideal fit for this use-case (right now).

      Thanks for the feedback,

      Dave

  2. Hi Dave,

    I’m the current chair of the JSON-LD Community Group, the RDFa Working Group, and a member of the RDF and HTML Working Groups.

    A couple of observations/questions:

    1. Do you realize that JSON-LD can express RDF lists like so: “theList”: ["a", "b", "c"] ? You can also embed other subjects in a list, like so: “theList”: [{...}, {...}, {...}]. http://json-ld.org/spec/ED/json-ld-syntax/20120930/#sets-and-lists
    2. JSON-LD documents can also have arrays as the top-level construct in the document. Arrays in JSON-LD can be ordered, or unordered.
    3. You can alias things like ‘@id’ and ‘@type’. http://json-ld.org/spec/ED/json-ld-syntax/20120930/#aliasing-keywords
    4. You can probably accomplish what you want to accomplish without using the JSON-LD API, framing need not enter the equation at all. I don’t see why you need it; more on this below.
    5. ‘Any tree structured data can be produced’ – the same is true for JSON-LD.
    6. We’ve found that removing Compact IRI expressions from the JSON, like ‘foaf:givenName’, leads to data that’s easier to consume for Web developers.
    7. You can test these ideas out here: http://json-ld.org/playground/

    I think, fundamentally, the important thing your “RDF Tree” algorithm does is give a specific structure to the resulting JSON (for your specific use case). That algorithm has nothing to do with JSON-LD, except that the JSON-LD Framing Algorithm does something similar, but doesn’t use SPARQL as the query mechanism (and is thus, the framing algorithm is a bad fit for this use case). Unless I’m missing something, and I could very well be, you are conflating the data the service generates based on a query and the resulting response with the data format for that response. The “RDF Tree” data you show above could easily be a JSON-LD document with one possible issue. That issue is the ‘^’ reverse relationship microsyntax mechanism in RDF Tree. Another JSON-LD Community Group member has already created a solution for reverse relationships that is compatible with JSON-LD, and which would probably work for your use case.

    After reading the entirety of this blog post and your exchange with Yves, twice, this use case looks like a fairly good fit for JSON-LD. So, perhaps there is some other requirement here that I’m unaware of, or don’t understand.

    • Hi Manu,

      Thank you for your detailed response. I will try and address some of your points…

      1. Yes I was aware of this. Most of my point in post is about the, consistent representation of the ordered list of results from an API call. If there are several list structures in the returned data, I need to know which one represents the result, and to always render this as a top level JSON array. This is not an incompatibility with JSON-LD, it just informs the algorithm to construct the data.
      2. This sounds good, and will help me to align the syntax
      3. I have already changed to using @id and @type in the latest algorithm
      4. I was coming to that conclusion too.
      5. So can an XML tree be constructed?
      6. Whilst I agree that name is better than foaf:name for developers, it introduces the naming lookup and conflict resolution problem. Going back to the key design aim, I don’t want developers to have to know that the data is in some particular form (such as JSON-LD). I want developers to treat the data as simply JSON (or XML) and run simple hash look-ups to retreive the data they want in a predictable manner. Having said this, I think I could further simplify the format to only include prefixes if a clash exists. What I don’t feel is necessary, is the construction of the @context data as the data is not intended for use as RDF.

      I really appreciate the feedback, and I will continue to align the syntax with JSON-LD where possible. I think I can approach a syntax that is essentially JSON-LD without the context or framing information and where simple predictable approaches are taken to naming conflicts. I am still considering my options around language and datatypes where I would want to take the same approach to try and achieve simplicity and predictability.

      Cheers,

      Dave

  3. There are two very simple approaches here:

    a) provide json/xml and point to a map that allows it to be viewed as RDF
    a-1) provide json/xml and point to an rdf version
    b) forget the data interchange format, as who uses JSON directly without parsing it first? concentrate on tooling which puts data in to a tree for users.

    Ultimately, you’ll never get far trying to push a graph in to a tree structure or URIs in as properties, as those are the two features which developers are not used to and find hard to wrap their heads around. So you either need a tree with no uris/curies (not rdf) or a tooling that lets people use graphs+uris easily. Anything in the middle is pretty much just rdf/xml all over again (sorry json-ld guys, I like you’re work but have always stuck to my guns on this one, see http://www.w3.org/2011/rdf-wg/wiki/JSON_Syntax_Options for full discussion, that’s about it all summed up)

    • Thanks Nathan,

      I guess I am trying to achieve “a tree with no uris/curies (not rdf)” but trying to take predictable and automated approaches to property naming and tree structure so that I don’t have to take an approach that involves bespoke templating for each API response. With this in mind, I am considering simple property names such as ‘name’ instead of ‘foaf:name’ except where a conflict exists. We only deal with around 20-30 ontology namespaces, so these could be prioritised to ensure the top priority namespace always used the shorter-form property name.

      Language and data types are going to introduce a challenge, your link to the JSON syntax options is going to be really useful for that!

      Dave

      • Hi Dave,

        I took the same approach in a clientside library JS3, if you take a look at https://github.com/webr3/js3/blob/master/source/propertymap.js, or even run the amibiguties method, you’ll see where there’s any overlap using the common vocabs.

        It is worth noting that providing a simple mapping from JSON to RDF via say a link header is very optimized, since most APIs will only have a handful of data structures they return, thus only a handful of maps, to cover an unbounded set of API responses to clients.

        Best,
        Nathan

  4. Hi Dave,

    in addition to the thoughtful comments from Yves and Manu, I always had in mind that your are looking for something like Minimum Spanning Graphs (http://onlinelibrary.wiley.com/doi/10.1002/cpe.1623/pdf) or Consise Bound Descriptions (http://www.w3.org/Submission/CBD/). So you just want to know everything about a (information) resource (see also http://infoserviceonto.smiy.org/2010/11/25/on-resources-information-resources-and-documents for a proposal of information resource definition). On the other side, when writing a common SQL query we usually also do the ordering via the query, or?

  5. Pingback: RDF Tree: An Updated Approach - semanticweb.com

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s