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. As of today, the new
graph:to-json primitive provides the parser, while a new kind of predicate provides the ability to navigate through the key-value hierarchy. For example, we can turn the following (serialized) JSON into a native JSON object:
@list j: "{\"foo\": true, \"bar\": [6, 9, 42]}" to-json.
:j.
[1] {"foo":true,"bar":[6,9,42]}
Now we can traverse as far into the hierarchy as we want:
:j. "foo".
[1] true
:j. "bar". each.
[1] 6
[2] 9
[3] 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:
# Fetches Twitter Places JSON from Twitter's REST API
# id: a place id (e.g. "90942366be65cd2c")
@list id twitter-place: \
"http://api.twitter.com/1/geo/id/" id concat. \
".json" concat. \
to-uri. get. to-json.
# Fetches the parent features of a place
# place: the JSON of an already-fetched place
@list place parent-place: \
place "contained_within". each. \
"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:
@list embarcadero: "90942366be65cd2c" :twitter-place.
:embarcadero.
[1] {"id":"90942366be65cd2c","bounding_box":{"type":"Pomµç–vöâ"Â&6ö÷&F–æFW2#¢âââ×ÐÐ
Now we can get the name of the neighborhood:
:embarcadero. "name".
[1] "The Embarcadero"
… the higher-level places it is contained in:
: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:
: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). JSON support is included in the
Ripple 0.6 (alpha) release.
Edit 2011-5-13: examples updated to use latest syntax