RDF Tree revisited: Developer-friendly JSON or XML from RDF graphs

In my previous post I talked about RDF Tree, an approach to building JSON or XML data from RDF graphs. Having received a number of useful comments, particularly from those involved with JSON-LD, I have revisited the approach and would like to present a revised version.

What is RDF Tree?

RDF Tree is an approach (and a Java library in-development to implement the approach), to producing developer-friendly serialisations of RDF graphs. It is not a serialisation format in itself like JSON-LD, but simply an approach to building predictable, stable JSON and XML representations of graph data.

The aims of this approach are as follows:

  • RDF Tree serialisations are non-semantic
    • Designed to power data-driven visual representation of data such as HTML
    • Designed to be lossy: the RDF graph cannot be recovered from the data
      • It is best practice to offer the data as RDF also for clients that require semantic data
  • RDF Tree is designed to be flexible
    • Whilst there are core principles, different rules, syntax and algorithms can be used to tailor the approach to a specific domain or use-case
  • RDF Trees are either single trees or multiple trees in an ordered list
    • Tree root(s) are indicated in the RDF using the tree ontology (see previous post)
    • For single trees, a specific root resource is known
    • For multiple trees, an ordered list of root resources is known (duplicates allowed)
    • RDF Trees can be built according to different rules
  • The general four rules for constructing the abstract tree from a graph structure are outlined in the previous blogpost.
  • As the rules can vary, there is no one canonical RDF Tree for a given graph input
  • Given a fixed set of rules, RDF Trees are produced as a function of a graph input
    • Rules include:
      • When to stop traversing the graph when building the tree
      • How to ‘canonicalise’ the resulting RDF Tree (e.g. deterministic property ordering)
  • The JSON or XML produced using this approach is largely indistinguishable from ‘vanilla’ JSON or XML
    • No superfluous meta or reference data is provided in order to extract the original graph or understand the specific semantics of the data
    • Designed for use with generic JSON or XML parsing libraries
  • Where naming conflicts exist, stable prefixes are used to distinguish between properties
  • Assumptions are made to optimise the approach
    • All data is considered single-language, different languages can be requested using the Accept-Language header
    • Datatype handling is minimal – datatypes are expected to be predictable
      • No datatypes in XML
      • JSON value types are respected
  • Where possible, the JSON syntax is aligned with JSON-LD, with the principal difference being the absence of the “@context” metadata
  • Inverse properties are included with the “^” prefix in JSON and a inverse=”true” attribute in XML

What does RDF Tree look like?

For the given RDF Turtle input:

@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:root <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> .

The following JSON is produced:

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

And the following XML is produced:

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

Property names

RDF Tree uses the property’s local name as the JSON field name. If a name conflict exists (more than one IRI exists for the same local name), then the IRI prefix is used to distinguish the properties, e.g. “foaf:name” where another “name” exists. A namespace priority list is used to determine which IRI can be expressed as just the local name, and which requires the prefix.

Essentially, no two properties can have the same name. However, property names can vary depending on the presence of other properties with the same local name.

The same approach is used in the XML element names, except the separator char is “-” resulting in disambiguated element names like <foaf-name/>.

Stable property set

Even though naming inconsistencies will be rare, the potential can be reduced by adding properties to a list of ‘stable’ IRIs with prefix and unique local name. This set will contain the definitive set of unambiguous local names. This set will never be visible to users of the data, and is simply there to ensure the stability of the data.

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)