Strings to things in context

As part of work to convert plain JSON records to proper RDF in JSON-LD I often want to convert a string value to a URI that identifies a thing (real world concrete thing or a concept).

Simple string to URI mapping

Given a fragment of a schedule in JSON

{"day": "Tuesday"}

As well as converting "day" to a property in an RDF vocabulary I might want to use a concept term for “Tuesday” drawn from that vocabulary. JSON-LD’s @context lets you do this: the @vocab keyword says what RDF vocabulary you are using for properties; the @base keyword says what base URL you are using for values that are URIs; the @id keyword maps a JSON key to an RDF property; and, the @type keyword (when used in the @context object) says what type of value a property should be, the value of @type that says you’re using a URI is "@id" (confused by @id doing double duty? it gets worse). So:

{
  "@context": {
    "@vocab": "http://schema.org/",
    "@base": "http://schema.org/",
    "day": {
       "@id": "dayOfWeek",
       "@type": "@id"
    }
  },
  "day": "Tuesday"
}

Pop this in to the JSON-LD playground to convert it into N-QUADS and you get:

_:b0 <http://schema.org/dayOfWeek> <http://schema.org/Tuesday> .

Cool.

What type of thing is this?

The other place where you want to use URI identifiers is to say what type/class of thing you are talking about. Expanding our example a bit, we might have

{
  "type": "Schedule",
  "day": "Tuesday"
}

Trying the same approach as above, in the @context block we can use the @id keyword to map the string value "type" to the special value "@type"; and, use the @type keyword with special value "@id" to say that the type of value expected is a URI, as we did to turn the string “Tuesday” into a schema.org URI. (I did warn you it got more confusing). So:

{
  "@context": {
    "@vocab": "http://schema.org/",
    "@base": "http://schema.org/",
    "type": {
       "@id": "@type",
       "@type": "@id"    
    },
    "day": {
       "@id": "dayOfWeek",
       "@type": "@id"
    }
  },
  "type": "Schedule",
  "day": "Tuesday"
}

Pop this into the JSON-LD playground and convert to N-QUADS and you get

_:b0 <http://schema.org/dayOfWeek> <http://schema.org/Tuesday> .
_:b0 <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://schema.org/Schedule> .

As we want.

Mixing it up a bit

So far we’ve had just the one RDF vocabulary, say we want to use terms from a variety of vocabularies. For the sake of argument, let’s say that no one vocabulary is more important than another, so we don’t want to use @vocab and @base to set global defaults. Adding¬† another term from a custom vocab in to the our example:

{ 
  "type": "Schedule",
  "day": "Tuesday",
  "onDuty": "Phil" 
}

In the context we can set prefixes to use instead of full length URIs, but the most powerful feature is that we can use different @context blocks for each term definition to set different @base URI fragments. That looks like:

{
  "@context": {
    "schema": "http://schema.org/",
    "ex" : "http://my.example.org/",
    "type": {
       "@id": "@type",
       "@type": "@id",
       "@context": {
         "@base": "http://schema.org/"        
      }
    },
    "day": {
      "@id": "schema:dayOfWeek",
      "@type": "@id",
      "@context": {
         "@base": "http://schema.org/"        
      }
    },
   "onDuty": {
     "@id": "ex:onDuty",
       "@type": "@id",
       "@context": {
         "@base": "https://people.pjjk.org/"
      }
    }
  },
  "type": "Schedule",
  "day": "Tuesday",
  "onDuty": "phil"
}

Translated by JSON-LD Playground that gives:

_:b0 <http://my.example.org/onDuty> <https://people.pjjk.org/phil> .
_:b0 <http://schema.org/dayOfWeek> <http://schema.org/Tuesday> .
_:b0 <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <https://json-ld.org/playground/Schedule> .

Hmmm. The first two lines look good. The JSON keys have been translated to URIs for properties from two different RDF vocabularies, and their string values have been translated to URIs for things with different bases, so far so good. But, that last line: the @base for the type isn’t being used, and instead JSON-LD playground is using its own default. That won’t do.

The fix for this seems to be not to give the @id keyword for type the special value of "@type", but rather treat it as any other term from an RDF vocabulary:

{
  "@context": {
    "schema": "http://schema.org/",
    "ex" : "http://my.example.org/",
    "rdf": "http://www.w3.org/1999/02/22-rdf-syntax-ns#",
    "type": {
       "@id": "rdf:type",
       "@type": "@id",
       "@context": {
         "@base": "http://schema.org/"        
      }
    },
    "day": {
      "@id": "schema:dayOfWeek",
      "@type": "@id",
      "@context": {
         "@base": "http://schema.org/"        
      }
    },
   "onDuty": {
     "@id": "ex:onDuty",
       "@type": "@id",
       "@context": {
         "@base": "https://people.pjjk.org/"
      }
    }
  },
  "type": "Schedule",
  "day": "Tuesday",
  "onDuty": "phil"
}

Which gives:

_:b0 <http://my.example.org/onDuty> <https://people.pjjk.org/phil> .
_:b0 <http://schema.org/dayOfWeek> <http://schema.org/Tuesday> .
_:b0 <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://schema.org/Schedule> .

That’s better, though I do worry that the lack of a JSON-LD @type key might bother some.

Extensions and Limitations

The nested context for a JSON key works even if the value is an object, it can be used to specify the @vocab and @base and any namespace prefixes used in the keys and values of the value object. That’s useful if title in one object is dc:title and title in another needs to be schema:title.

Converting string values to URIs for things like this is fine if the string happens to match the end of the URI that you want. So, while I can change the a JSON key "author" into the property URI <https://www.wikidata.org/prop/direct/P50> I cannot change the value string "Douglas Adams" into <https://www.wikidata.org/entity/Q42>. For that I think you need to use something a bit more flexible, like RML, but please comment if you know of a solution to that!

Also, let me know if you think the lack of a JSON-LD @type keyword, or anything else shown above seems problematic.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.