Ripple on JSON

timrdf edited this page Mar 10, 2012 · 7 revisions

What is first

Let's get to it

There are a number of popular formats for structured data on the Web. Ripple supports RDF/XML as well as several other RDF document formats, allowing you to navigate programmatically through the Web of Data, and even embed programs in the Web of Data. However, there's also a lot of good JSON data out there which shares many of the advantages of RDF data: unambiguous syntax, a graph-like structure, and the ability to link from one document to another (albeit in domain-specific ways). For example, here's a bit of JSON about San Francisco's Embarcadero neighborhood, provided by Twitter:

{
    "place_type":"neighborhood",
    "contained_within":[
        {
            "place_type":"city",
            "full_name":"San Francisco, CA",
            "name":"San Francisco",
            "url":"http://api.twitter.com/1/geo/id/5a110d312052166f.json",
            "id":"5a110d312052166f"
            ...
        }
    ],
    "country":"The United States of America",
    "country_code":"US",
    "full_name":"The Embarcadero, San Francisco",
    "name":"The Embarcadero",
    "url":"http://api.twitter.com/1/geo/id/90942366be65cd2c.json",
    "id":"90942366be65cd2c"
    ...
}

Ripple's HTTP client is capable of fetching JSON documents on the fly, so all we need is a way to parse the retrieved JSON, as well as a way to navigate through it. The graph:to-json primitive provides the parser, while key/value predicates (see the graph library) provide the ability to navigate through the key-value hierarchy. For example, we can turn the following (serialized) JSON into a native JSON object:

1)  @list j: "{\\"foo\\": true, \\"bar\\": [6, 9, 42]}" to-json.

2)  :j.

  [1]  {"foo":true,"bar":[6,9,42]}

Now we can traverse as far into the hierarchy as we want:

3)  :j. "foo".

  [1]  true

4)  :j. "bar". each.

  [1]  6
  [2]  9
  [3]  42

We can also use the key/value primitives to find all keys, values, or key/value pairs of the object:

5)  :j. keys.

  [1]  "foo"
  [2]  "bar"

6)  :j. values.

  [1]  true
  [2]  (6 9 42)

7)  :j. key-values.

  [1]  "foo" true
  [2]  "bar" (6 9 42)

Note that JSON arrays become lists in Ripple, while booleans become xsd:boolean values, and numbers become xsd:integer, xsd:long or xsd:double values. Here's an example which uses the Twitter data mentioned above:

8)  # Fetches Twitter Places JSON from Twitter's REST API
9)  # id: a place id (e.g. "90942366be65cd2c")
10)  @list id twitter-place: \\\\
11)      "http://api.twitter.com/1/geo/id/" id concat. \\\\
12)      ".json" concat. \\\\
13)      to-uri. get. to-json.
14)
15)  # Fetches the parent features of a place
16)  # place: the JSON of an already-fetched place
17)  @list place parent-place: \\\\
18)      place "contained_within". each. \\\\
19)      "id". :twitter-place.

These two mappings allow us to turn a Twitter Places id into a native JSON object after fetching the corresponding JSON document from Twitter, and to continue traversing to places described in other JSON documents:

20)  @list embarcadero: "90942366be65cd2c" :twitter-place.

21)  :embarcadero.

   [1]  {"id":"90942366be65cd2c","bounding_box":{"type":"Polygon","coordinates": ... }}

Now we can get the name of the neighborhood:

22)  :embarcadero. "name".

   [1]  "The Embarcadero"

... the higher-level places it is contained in:

23)  :embarcadero. :parent-place* "full_name".

   [1]  "The Embarcadero, San Francisco"
   [2]  "San Francisco, CA"
   [3]  "California, US"
   [4]  "The United States of America"

...and so on:

24)  :embarcadero. :parent-place{2} "geometry". "coordinates". each. each.

   [1]  (-124.482003E0 32.528832E0)
   [2]  (-114.131211E0 32.528832E0)
   [3]  (-114.131211E0 42.009517E0)
   [4]  (-124.482003E0 42.009517E0)
   [5]  (-124.482003E0 32.528832E0)

Note that unlike RDF data, JSON documents are currently not cached in Ripple (so there is no speedup for repeated queries).

This code is in the Web!

The above examples have been published as linked RDF/XML at the following namespace: http://ripple.fortytwo.net/code/2011/06/rippleSailExamples#. Try the following code without first pasting in the definitions for the embarcardero or parent-place programs:

@prefix : <http://ripple.fortytwo.net/code/2011/06/rippleSailExamples#>
:embarcadero. :parent-place* "full_name".

After a few moments, you should see the expected values appear in the console. Ripple has just fetched the programs in question from the Web of Data and executed them on the fly.

What is next