Jangle is a specification for applying the Atom Publishing Protocol (AtomPub) to library resources and for exposing these resources simply and RESTfully.
There are three basic principles that define Jangle:
For search (which is optional), Jangle employs OpenSearch and the Atom Syndication Format for search results and CQL to provide specificity and a common vocabulary of indexes among implementations.
The metadata formats that are exposed at the various entities are defined by the communities of practice that are implementing Jangle, so Integrated Library Management Systems may or may not expose the same metadata formats as an Electronic Resources Management System or Interlibrary Loan System. Alternate metadata formats for feeds or individual resources are advertised via atom:link elements using "rel" attributes comprised of Jangle-specific URIs.
For an application to be "Jangle-compliant", it is not necessary to provide both connector and core components. Compatibility with either component is sufficient to the specification. For example, an application may only provide the connector functionality (a RESTful JSON interface that conforms to the Jangle connector API) or the developer may choose to bypass the core/connector design and apply the AtomPub interface directly to a business logic layer, as long is its behavior resembles that of the Jangle AtomPub specification.
The Jangle data model is divided up into four distinct concepts, referred to as entities, that are reflected as paths in any Jangle implementation. None of the entities are required, although it is expected that some entities are dependent on others to make sense.
Jangle URIs follow a strict design model. In implementations using the standard Jangle core/connector architecture, URIs conform to the following syntax:
http://example.org/{service_name}/{entity}/{id}/{relationship}
which, in reality, translates to:
At the entity paths (i.e. /actors/, /collections/, /items/, /resources/), it is expected that the results will be returned descending by last modified timestamp.
Feeds returned at the relationship level (http://demo.jangle.org/openbiblio/actors/1711/items/) would return entries with identifiers at the base level:
http://demo.jangle.org/openbiblio/items/000005779
A Jangle implementation that only conforms to the AtomPub syntax can return URIs that look like:
http://example.org/{entity}/{id}/{relationship}
Ids can be sent as a list (separated by commas or semicolons) or as a range (delimited by hyphens).
Only one entity can be requested at a time (i.e. only actors or items, not a list of actors and items) and there should be no expectation of preserving order (although this is desirable if possible).
Jangle core servers *must* have an AtomPub service document at /services/ (or provide redirection from /services/ to an AtomPub service document).
Entities can be filtered via Atom categories. Categories are defined with the following URL pattern:
http://demo.jangle.org/openbiblio/resources/-/opac
The lone hyphen in the path indicates that the rest of the path are categories. This means that category filters can only be placed on the last entity requested, so if the URI contains a relationship, such as /actors/1711/items/, the filter can only be applied to items, i.e. /actors/1711/items/-/holds.
The Jangle Core is the Atom Publishing Protocol interface to Jangle. It enables clients to access and (eventually) create and modify library resources RESTfully using regular AtomPub libraries (and conventions). The core also serves as the mediator between the client and the connector, receiving incoming requests, brokering them to the appropriate connector, parsing the JSON responses and serializing them into appropriate XML documents (Atom Syndication Format, Atom Services documents, OpenSearch Description documents, etc.).
A Jangle application does not need to follow the core/connector to be compliant, as long as the client interface responds appropriately. To be compliant as a Jangle core, however, it must be able to communicate with at least one connector.
The only HTTP methods covered by this specification are GET and HEAD. Future revisions will define how the other methods are handled.
All Jangle applications must respond with an Atom Services document at base_url/services/ (or be redirected to one) and return the content-type application/atomsvc+xml. This is the only reserved path for Jangle core compliant applications.
Typically, Jangle applications proxy a connector off of a subdirectory off of the base_url.
Example:
http://demo.jangle.org/openbiblio/
Where http://demo.jangle.org/ is the base url and an openbiblio is the path to the connector.
Jangle core communicates to connectors via HTTP. The connector responses are JSON objects.
Jangle core should submit all incoming HTTP header information to the connector (excluding accept headers). The content-type that Jangle core should request from the connector is application/json. The core is expected to also send an additional header, X-Connector-Base which is set to the URI of the Jangle core host and the path to the connector.
Example of X-Connector-Base header:
X-Connector-Base: http://demo.jangle.org/openbiblio/
Without this header, a Jangle connector cannot construct appropriate URIs, especially within the resource data.
The core should return the HTTP status codes verbatim from the connector.
Connector entity paths do not have to conform to the Jangle entity names. The paths to entities must be declared in the connector services response, however all public Jangle URIs must conform to the Jangle entity URI structure.
All Jangle implementations must return an Atom Services document from the base_url/services/ URL.
Example:
http://demo.jangle.org/services/
Methods allowed: GET, HEAD
Content-type required: application/atomsvc+xml
The schema and syntax of the service document is identical to how it is defined in RFC 5023.
Here is how Jangle uses the Atom Service elements:
An example service document:
<?xml version="1.0" encoding="UTF-8"?>
<service xmlns="http://www.w3.org/2007/app" xmlns:atom="http://www.w3.org/2005/Atom">
<workspace>
<atom:title>openbiblio</atom:title>
<collection href="http://demo.jangle.org/openbiblio/items">
<atom:title>Holdings records</atom:title>
</collection>
<collection href="http://demo.jangle.org/openbiblio/actors">
<atom:title>Borrowers</atom:title>
</collection>
<collection href="http://demo.jangle.org/openbiblio/collections">
<atom:title>Categories</atom:title>
</collection>
<collection href="http://demo.jangle.org/openbiblio/resources">
<atom:title>Bibliographic records</atom:title>
<categories fixed="no">
<atom:category
scheme="http://jangle.org/vocab/terms#dlf-ilsdi-resource"
term="opac" />
</categories>
</collection>
</workspace>
<workspace>
<atom:title>prism</atom:title>
<collection href="http://demo.jangle.org/prism/actors">
<atom:title>Borrowers</atom:title>
</collection>
<collection href="http://demo.jangle.org/prism/collections">
<atom:title>Categories</atom:title>
</collection>
<collection href="http://demo.jangle.org/prism/resources">
<atom:title>Bibliographic records</atom:title>
</collection>
</workspace>
</service>
Here is how the connector JSON elements should be applied to the Atom Service document.
The "type" element being set to "services" tells the Jangle core that it should serialize the response into an Atom Service document.
Feed documents are main document types used in Jangle. They are Atom Syndication Format documents and carry the resource data in the atom:entry/content element. Feed elements are used for both collections of resources and single resources.
Example:
http://demo.jangle.org/openbiblio/actors/
Methods allowed: GET, HEAD
Content-type required: application/atom+xml
The schema and syntax of the Atom Feed is structurally identical to how it is defined in RFC 4287, although Jangle defines two extension attributes that appear in <link> elements: jangle:format and jangle:relationship.
Since each entity could span many thousands to millions of resources, Jangle uses feed paging as defined in RFC 5005. The way paged feeds are retrieved and how many resources are contained in given feed is left to the discretion of the implementer.
As a general rule, Jangle feeds that contain more than one resource should be sorted descending by last modification date. Feeds of lists or ranges of resources have no set expectation on the order of resources contained.
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xmlns:jangle="http://jangle.org/vocab/">
<title>openbiblio</title>
<link href="http://demo.jangle.org/openbiblio/resources/"/>
<updated>2008-10-02T14:39:37Z</updated>
<link rel="first" type="application/atom+xml" href="http://demo.jangle.org/openbiblio/resources/?offset=0"/>
<link rel="last" type="application/atom+xml" href="http://demo.jangle.org/openbiblio/resources/?offset=6000"/>
<link rel="next" type="application/atom+xml" href="http://demo.jangle.org/openbiblio/resources/?offset=100"/>
<link rel="self" type="application/atom+xml" href="http://demo.jangle.org/openbiblio/resources/" jangle:format="http://jangle.org/vocab/formats#http://www.loc.gov/MARC21/slim"/>
<link rel="http://jangle.org/vocab/formats#http://purl.org/dc/elements/1.1/" type="application/atom+xml" href="http://demo.jangle.org/openbiblio/resources/?format=dc"/>
<link rel="http://jangle.org/vocab/formats#application/marc" type="application/atom+xml" href="http://demo.jangle.org/openbiblio/resources/?format=marc"/>
<link rel="http://jangle.org/vocab/formats#http://www.openarchives.org/OAI/2.0/oai_dc/" type="application/atom+xml" href="http://demo.jangle.org/openbiblio/resources/?format=oai_dc"/>
<link rel="http://jangle.org/vocab/formats#http://www.loc.gov/mods/v3" type="application/atom+xml" href="http://demo.jangle.org/openbiblio/resources/?format=mods"/>
<link rel="search"
href="http://demo.jangle.org/openbiblio/resources/search/description/"
type="application/opensearchdescription+xml" />
<id>http://demo.jangle.org/openbiblio/resources/</id>
<entry>
<title>The Untamed</title>
<link href="http://demo.jangle.org/openbiblio/resources/5878" jangle:format="http://jangle.org/vocab/formats#http://www.loc.gov/MARC21/slim"/>
<link rel="related" type="application/atom+xml" href="http://demo.jangle.org/openbiblio/resources/5878/collections/" jangle:relationship="http://jangle.org/vocab/Entity#Collection"/>
<link rel="alternate" type="text/html" href="http://catalog.jangle.org/openbiblio/shared/biblio_view.php?bibid=5878&tab=opac"/>
<link rel="http://jangle.org/vocab/formats#http://purl.org/dc/elements/1.1/" type="application/atom+xml" href="http://demo.jangle.org/openbiblio/resources/5878?format=dc"/>
<link rel="http://jangle.org/vocab/formats#application/marc" type="application/atom+xml" href="http://demo.jangle.org/openbiblio/resources/5878?format=marc"/>
<link rel="http://jangle.org/vocab/formats#http://www.openarchives.org/OAI/2.0/oai_dc/" type="application/atom+xml" href="http://demo.jangle.org/openbiblio/resources/5878?format=oai_dc"/>
<link rel="http://jangle.org/vocab/formats#http://www.loc.gov/mods/v3" type="application/atom+xml" href="http://demo.jangle.org/openbiblio/resources/5878?format=mods"/>
<author>
<name>Brand, Max,</name>
</author>
<id>http://demo.jangle.org/openbiblio/resources/5878</id>
<updated>2008-03-18T15:57:00-04:00</updated>
<content type="application/xml">
<record xmlns='http://www.loc.gov/MARC21/slim'><leader> Z 22 4500</leader><datafield tag='245' ind1='0' ind2='0'><subfield code='a'>The Untamed</subfield><subfield code='b'></subfield><subfield code='c'>by Max Brand</subfield></datafield><datafield tag='100' ind1='0' ind2='z'><subfield code='a'>Brand, Max,</subfield></datafield><datafield tag='42' ind1='z' ind2='z'><subfield code='a'>dc</subfield></datafield><datafield tag='100' ind1='z' ind2='z'><subfield code='d'>1892-1944</subfield></datafield><datafield tag='245' ind1='z' ind2='z'><subfield code='h'>[electronic resource] /</subfield></datafield><datafield tag='260' ind1='z' ind2='z'><subfield code='b'>Project Gutenberg,</subfield><subfield code='c'>2004</subfield></datafield><datafield tag='500' ind1='z' ind2='z'><subfield code='a'>Project Gutenberg</subfield></datafield><datafield tag='506' ind1='z' ind2='z'><subfield code='a'>Freely available.</subfield></datafield><datafield tag='516' ind1='z' ind2='z'><subfield code='a'>Electronic text</subfield></datafield><datafield tag='830' ind1='z' ind2='z'><subfield code='a'>Project Gutenberg</subfield><subfield code='v'>10886</subfield></datafield><datafield tag='856' ind1='z' ind2='z'><subfield code='u'>http://www.gutenberg.org/etext/10886</subfield><subfield code='3'>Rights</subfield></datafield><datafield tag='856' ind1='z' ind2='z'><subfield code='u'>http://www.gutenberg.org/license</subfield></datafield></record> </content>
<category term="opac"/>
</entry>
...
</feed>
Jangle has defined special atom link "rel" attribute values to define alternate data formats available for the resources in a given feed and the related resources to any given atom:entry.
To declare a feed for the same set of resources (or resource) using a different record format (in this case, Dublin Core rather than marcxml), the following syntax is used:
<link rel="http://jangle.org/vocab/formats#http://purl.org/dc/elements/1.1/" type="application/atom+xml" href="http://demo.jangle.org/openbiblio/resources/5878?format=dc"/>
The rel attribute uses a URI that unambiguously declares the format that would be delivered in the linked feed. The URI http://jangle.org/vocab/formats# must be present to use this link syntax. The remaining URI fragment (in this case, http://purl.org/dc/elements/1.1/) is an identifier to denote the data format present. It does not have to be a namespace URI (any string will do). The full URI (http://jangle.org/vocab/formats#http://purl.org/dc/elements/1.1/) should be dereferenceable to a definition of the identifier. It is within the scope of the Jangle initiative to provide this vocabulary (and a means to contribute to it).
The type attribute must be application/atom+xml. The href attribute should be the URI to the feed which contains the alternate data format. The URI pattern is left to the discretion of the implementer.
These link elements may appear as child elements of the <feed> element or an <entry> element.
Jangle feeds also define relationships between resources. If an Actor has multiple Items associated with it (for example, a borrower has several items checked out), that relationship can be declared like this:
<link type="application/atom+xml" href="http://demo.jangle.org/openbiblio/actors/1711/items/" rel="related" jangle:relationship="http://jangle.org/vocab/Entity#Item"/>
Jangle defined an extension attribute, jangle:relationship to provide the connection between two (or more) resources.
In order to explicitly identify the format of the entity content element payload, Jangle has defined another extension attribute: jangle:format. For feeds with homogeneous record formats (i.e. all entries use the same format), this extension should appear on the rel="self" link element at the feed level and on a link element in the entry. If the record formats are heterogeneous within the feed, the attribute need only appear on link elements within the entry elements. The value of the attribute should be a Jangle format URI (such as: http://jangle.org/vocab/formats#application/marc).
If an entity is flagged as searchable in the connector services response, the Jangle core should also provide an OpenSearch autodiscovery link:
<link rel="search"
href="http://demo.jangle.org/openbiblio/resources/search/description/"
type="application/opensearchdescription+xml" />
Which would actually return a Jangle Explain document.
Here is how the connector JSON elements should be applied to the Atom Syndication Format document.
The "type" element being set to "feed" tells the Jangle core that it should serialize the response into an Atom Syndication Format document.
See an example feed for a point of reference.
To build the <link> elements, it is probably easiest to look at this from the opposite direction.
The link elements would be built from the "alternate_formats", ["data"][i]["alternate_formats"], ["data"][i]["relationships"], "links", ["data"][i]["links"] elements.
From the "alternate_formats" and ["data"][i]["alternate_formats"] elements, the <link> element's attributes would come from:
The ["data"][i]["relationships"] are used the following way:
The <link> tags created from the "link" elements is pretty straightforward. The "rel" attribute value is set from the hash key ("alternate", "relation", etc.). The remaining object properties should be set as the element attributes ("href", "type", "title", etc.).
Jangle connector feed or search responses can include URLs to XSLT stylesheets to be applied at either the "feed" level or the "entry/content" level. Jangle does not define which version of XSLT is permissible, but developers should not assume all Jangle core implementations can support XSLT 2.0.
The stylesheet URLs must be accessible to the Jangle core via HTTP.
"Feed" level stylesheets (which is sent in JSON as an array) are to be applied after the connector response has been serialized as an Atom Syndication Format feed. The stylesheets are applied serially, in the order they appear in the array.
Resource level stylesheets can be applied at any time, since they should transform the data in the atom:entry/content tag as an independent document, not as a child element in the Atom feed.
The stylesheets should not transform the Atom into another format, although they can be used to set the values of the Atom elements or provide Atom extensions.
Searching in Jangle is provided through OpenSearch. The query syntax uses CQL to define search indexes.
Jangle extends the OpenSearch Description document to include elements of the SRU explain document schema.
Search results use a similar Atom document to the feed results, but include the OpenSearch extensions.
The description document advertises how to search resources for clients.
Example:
http://demo.jangle.org/openbiblio/resources/search/description/
Methods allowed: GET, HEAD
Content-type required: application/opensearchdescription+xml
Jangle extends the standard OpenSearch Description document with elements from the SRU Explain schema. This allows clients to know which indexes are available to search on in the query element.
Example Jangle Search Description Document.
<?xml version="1.0" encoding="utf-8"?>
<OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/" xmlns:jangle="http://jangle.org/opensearch/">
<ShortName>Bibliographic records</ShortName>
<LongName>Search Bibliographic records in OpenBiblio</LongName>
<Description>Bibliographic records search. Defaults to keyword anywhere.</Description>
<Tags>catalog library</Tags>
<SyndicationRight>open</SyndicationRight>
<Url type="application/atom+xml" template="http://demo.jangle.org/openbiblio/resources/search/?offset={startIndex?}&count={count?}&query={searchTerms?}&format={jangle:format?}"/>
<Query role="example" searchTerms="dc.creator=thomas">
<zr:explain xmlns:zr="http://explain.z3950.org/dtd/2.1/">
<zr:indexInfo>
<zr:set name="dc" identifier="info:srw/cql-context-set/1/dc-v1.1"/>
<zr:set name="rec" identifier="info:srw/cql-context-set/2/rec-1.1"/>
<zr:set name="cql" identifier="info:srw/cql-context-set/1/cql-v1.2"/>
<zr:index>
<zr:map><zr:name set="dc">title</zr:name></zr:map>
</zr:index>
<zr:index>
<zr:map><zr:name set="dc">creator</zr:name></zr:map>
</zr:index>
<zr:index>
<zr:map><zr:name set="dc">subject</zr:name></zr:map>
</zr:index>
<zr:index>
<zr:map><zr:name set="dc">publisher</zr:name></zr:map>
</zr:index>
<zr:index>
<zr:map><zr:name set="dc">format</zr:name></zr:map>
</zr:index>
<zr:index>
<zr:map><zr:name set="dc">identifier</zr:name></zr:map>
</zr:index>
<zr:index>
<zr:map><zr:name set="rec">identifier</zr:name></zr:map>
</zr:index>
<zr:index>
<zr:map><zr:name set="rec">collectionName</zr:name></zr:map>
</zr:index>
<zr:index>
<zr:map><zr:name set="rec">lastModificationDate</zr:name></zr:map>
</zr:index>
<zr:index>
<zr:map><zr:name set="rec">creationDate</zr:name></zr:map>
</zr:index>
<zr:index>
<zr:map><zr:name set="cql">allRecords</zr:name></zr:map>
</zr:index>
<zr:index>
<zr:map><zr:name set="cql">allIndexes</zr:name></zr:map>
</zr:index>
<zr:index>
<zr:map><zr:name set="cql">anyIndexes</zr:name></zr:map>
</zr:index>
<zr:index>
<zr:map><zr:name set="cql">keywords</zr:name></zr:map>
</zr:index>
</zr:indexInfo>
</zr:explain>
<Query>
</OpenSearchDescription>
The "explain" data becomes a child element of the OpenSearch:Query element.
Jangle search results are mostly identical to standard Entity feeds with the notable addition of the OpenSearch extension.
Search feeds can be identified from the Jangle connector by the "type" property being set to "search".
In addition to the mapping of the JSON properties to the feed document, the following properties should be mapped to OpenSearch elements:
The <opensearch:itemsPerPage> element can be set to the number of objects in the "data" array. Note: there is an assumption that Jangle feeds provide a consistent number of resources per page. As a result, provisions may need to be made for the last page in a result set in the event that the number of resources in the feed is less than the normal page default.
The search query terms (which should be a CQL query string) should be used to make the OpenSearch Query element:
<opensearch:Query role="request" searchTerms="{cql_query_string}" startIndex="0" />
Since feed offsets start with "0", search startIndex attributes should also be set to "0".
Search results feeds must include the OpenSearch namespace declaration: http://a9.com/-/spec/opensearch/1.1/
The Jangle connector is an application that translates the back-end business logic, data models, relationships and workflows into the Jangle entities. In version 1.0, connectors must be REST based and follow the URI structure of standard Jangle resources (although the resources themselves can have a different name locally, see the section on connector service responses for more information). All responses are a combination of HTTP status codes and JSON data structures.
There are four kinds of responses a Jangle connector may return:
All Jangle connectors must return a services response at {base_url}/services/.
Jangle connectors should not define absolute URLs internally, since it is possible that the same connector could be used by multiple Jangle cores (which, in turn, would mint different URIs for the same resource). Instead, connectors should be written to expect an HTTP Request header, $HTTP_X_CONNECTOR_BASE, to be sent by the Jangle core, and use that to construct absolute URIs. In the absence of this header, it is permissible for a connector to send a relative URI.
While connector paths do not have to conform to Jangle entity names, the URIs they create do, so if "actors" is mapped to /borrowers/ and "items" is mapped to /holdings/, URIs would still need to be returned as /actors/{id}/items/ and not /borrowers/{id}/holdings/.
Note, the only status codes defined here are in relation to GET requests at specific resources. Since version 1.0 of the spec only defines GET, it is out of scope of this document to define responses to other HTTP methods.
For a successful request that returns a valid Jangle JSON response, return a 200 (OK).
Return a 301 (Moved Permanently) or 302 (Found) for requests that need to be redirected to the appropriate URI. Examples would be redirecting a request for /resources to /resources/ or if identifiers changed (say after an ILMS migration, for example) /resources/1234 to /resources/9876.
Connectors can utilize the 304 (Not Modified)/etags response for any request, although Jangle core servers may not be able to use it (depending on whether or not they maintain caches).
Authentication/Authorization (401 and 403 responses) is at the discretion of the implementer. They are legal on *any* URI.
For any invalid path or id, return a 404 (Not Found) response.
For a URI that was once valid but is no longer (whether via deletion or deactivation of some sort), it is acceptable to send either a 410 (Gone) or a 404 (Not Found).
For version 1.0 of this spec, clients should expect to receive a 405 (Method Not Allowed) by sending any HTTP method besides GET to a resource. However, it is possible and acceptable for implementers to create their own methods for handling create, update and delete requests, so this is not mandatory.
Connector service responses must resolve at base_url/services/ (or be redirected to from there).
Example:
http://connector.jangle.org/services/
Methods allowed: GET, HEAD
Content-type required: application/json
JSON Schema:
{
"description":"Services available from this Jangle connector",
"type":"object",
"properties":{
"type":{"type":"string","pattern":"/^services$/"},
"version":{"type":"string","pattern":"/^1\.0$/"},
"title":{"type":"string",},
"request":{"type":"string"},
"entities":{"type":"object",
"properties:{
"Actor:{"optional":true,"type":"object",
"properties":{
"title":{"type":"string"},
"searchable":{"type":["boolean","string"],"format":"uri"},
"path":{"type":"string"},
"categories":{"optional":true,"type":"array"}
},
"Collection:{"optional":true,"type":"object",
"properties":{
"title":{"type":"string"},
"searchable":{"type":["boolean","string"],"format":"uri"},
"path":{"type":"string"},
"categories":{"optional":true,"type":"array"}
},
"Item:{"optional":true,"type":"object",
"properties":{
"title":{"type":"string"},
"searchable":{"type":["boolean","string"],"format":"uri"},
"path":{"type":"string"},
"categories":{"optional":true,"type":"array"}
},
"Resource:{"optional":true,"type":"object",
"properties":{
"title":{"type":"string"},
"searchable":{"type":["boolean","string"],"format":"uri"},
"path":{"type":"string"},
"categories":{"optional":true,"type":"array"}
}
},
"categories":{"optional":true,"type":"object"}
}
Which, in real terms, would return a response that looks something like:
{
"title": "openbiblio",
"version" "1.0",
"type": "services",
"entities":
{
"Resource":
{
"searchable": "/resources/search/description",
"title": "Bibliographic records",
"path": "/resources/",
"categories": ["opac"]
},
"Collection":
{
"searchable": false,
"title": "Categories",
"path": "/collections/"
},
"Item":
{
"searchable": false,
"title": "Holdings records",
"path": "/items/"
},
"Actor":
{
"searchable": false,
"title": "Borrowers",
"path": "/actors/"
}
},
"request": "/services/",
"categories":{
"opac":{
"scheme": "http://jangle.org/vocab/terms#dlf-ilsdi-resource",
"label": "Resources that are available for harvesting in a discovery interface"
}
}
}
The definitions of each field are:
And the definitions of the fields for the entities are:
And the categories are defined like this:
Feed responses are returned from any of the entity paths, including requests for single resources and relationships.
Example:
http://connector.jangle.org/resources/
Methods allowed: GET, HEAD
Content-type required: application/json
JSON schema:
{
"description":"A feed of entity resources",
"type":"object",
"properties":{
"type":{"type":"string","pattern":"/^feed|search$/"},
"request":{"type":"string","format":"uri"},
"time":{"type":""string","format":"date-time"},
"offset":{"type":"integer"},
"totalResults":{"type":"integer"},
"extensions":{"type":"object","optional":true,"additionalProperties":{
"type":"string","format":"uri"
}
},
"formats":{"type":"array","items":{"type":"string","format":"uri"}},
"alternate_formats":{"type":"object","optional":true,
"additionalProperties":{
"type":"string","format":"uri"
}
},
"stylesheets":{"type":"array","optional":true,"items":{"type":"string","format":"uri"}},
"categories":{"type":"array","optional":true,"items":{"type":"string"}},
"data":{"type":"array","items":[
"type":"object",
"properties":{
"id":{"type":"string","format":"uri"},
"title":{"type":"string"},
"updated":{"type":string","format":"date-time"},
"created":{"type":string","format":"date-time","optional":true},
"description":{"type":"string","optional":true},
"content":{"type":"string","optional":true,"requires":["content_type","format"]},
"content_type":{"type":"string","requires":["content","format"],"optional":true},
"stylesheet":{"type":"string","optional":true,"format":"uri"},
"format":{"type":"string","format":"uri","requires":["content","content_type"]},
"alternate_formats:{"type":"object","optional":true,
"additionalProperties":{
"type":"string","format":"uri"
}
},
"relationships":{"type":"object","optional":true,
"additionalProperties":{
"type":"string","format":"uri"
}
},
"categories":{"type":"array","optional":true,"items":{"type":"string"}},
"author":{"type":"string","optional":true},
"links":{"type":"object","optional":true,"properties":{
"alternate":{"type":"object","optional":true,"properties":{
"type":{"type":"string"},
"title":{"type":"string","optional":true},
"href":{"type":"string","format":"uri"}
}
},
"related":{"type":"object","optional":true,"properties":{
"type":{"type":"string"},
"title":{"type":"string","optional":true},
"href":{"type":"string","format":"uri"}
}
}
},
"additionalProperties":{"type":"object"}
},
"additionalProperties":{"type":"any"}
}
}
An actual JSON feed response returning MARCXML records:
{
"type": "feed",
"totalResults": 6077,
"time": "2008-09-30T16:11:03-04:00",
"offset": 0,
"request": "http://demo.jangle.org/openbiblio/resources/",
"alternate_formats":
{
"http://jangle.org/vocab/formats#http://purl.org/dc/elements/1.1/": "http://demo.jangle.org/openbiblio/resources/?format=dc",
"http://jangle.org/vocab/formats#http://www.loc.gov/mods/v3": "http://demo.jangle.org/openbiblio/resources/?format=mods",
"http://jangle.org/vocab/formats#http://www.openarchives.org/OAI/2.0/oai_dc/": "http://demo.jangle.org/openbiblio/resources/?format=oai_dc",
"http://jangle.org/vocab/formats#application/marc21": "http://demo.jangle.org/openbiblio/resources/?format=marc"
},
"formats":["http://jangle.org/vocab/formats#http://www.loc.gov/MARC21/slim"],
"data":
[
{
"alternate_formats":
{
"http://jangle.org/vocab/formats#http://purl.org/dc/elements/1.1/": "http://demo.jangle.org/openbiblio/resources/5878?format=dc",
"http://jangle.org/vocab/formats#http://www.loc.gov/mods/v3": "http://demo.jangle.org/openbiblio/resources/5878?format=mods",
"http://jangle.org/vocab/formats#http://www.openarchives.org/OAI/2.0/oai_dc/": "http://demo.jangle.org/openbiblio/resources/5878?format=oai_dc",
"http://jangle.org/vocab/formats#application/marc21": "http://demo.jangle.org/openbiblio/resources/5878?format=marc"
},
"links":
{
"alternate":
[
{
"type": "text/html",
"title": "Link to native interface",
"href": "http://catalog.jangle.org/openbiblio/shared/biblio_view.php?bibid=5878\u0026tab=opac"
}
],
"related":
[
{
"type": "application/atom+xml",
"href": "http://demo.jangle.org/openbiblio/resources/5878/collections/"
}
]
},
"title": "The Untamed",
"author": "Brand, Max,",
"updated": "2008-03-18T15:57:00-04:00",
"categories":
[
"opac"
],
"content": "\u003Crecord xmlns='http://www.loc.gov/MARC21/slim'\u003E\u003Cleader\u003E Z 22 4500\u003C/leader\u003E\u003Cdatafield tag='245' ind1='0' ind2='0'\u003E\u003Csubfield code='a'\u003EThe Untamed\u003C/subfield\u003E\u003Csubfield code='b'\u003E\u003C/subfield\u003E\u003Csubfield code='c'\u003Eby Max Brand\u003C/subfield\u003E\u003C/datafield\u003E\u003Cdatafield tag='100' ind1='0' ind2='z'\u003E\u003Csubfield code='a'\u003EBrand, Max\u003C/subfield\u003E\u003C/datafield\u003E\u003Cdatafield tag='42' ind1='z' ind2='z'\u003E\u003Csubfield code='a'\u003Edc\u003C/subfield\u003E\u003C/datafield\u003E\u003Cdatafield tag='100' ind1='z' ind2='z'\u003E\u003Csubfield code='d'\u003E1892-1944\u003C/subfield\u003E\u003C/datafield\u003E\u003Cdatafield tag='245' ind1='z' ind2='z'\u003E\u003Csubfield code='h'\u003E[electronic resource]/\u003C/subfield\u003E\u003C/datafield\u003E\u003Cdatafield tag='260' ind1='z' ind2='z'\u003E\u003Csubfield code='b'\u003EProject Gutenberg,\u003C/subfield\u003E\u003Csubfield code='c'\u003E2004\u003C/subfield\u003E\u003C/datafield\u003E\u003Cdatafield tag='500' ind1='z' ind2='z'\u003E\u003Csubfield code='a'\u003EProject Gutenberg\u003C/subfield\u003E\u003C/datafield\u003E\u003Cdatafield tag='506' ind1='z' ind2='z'\u003E\u003Csubfield code='a'\u003EFreely available.\u003C/subfield\u003E\u003C/datafield\u003E\u003Cdatafield tag='516' ind1='z' ind2='z'\u003E\u003Csubfield code='a'\u003EElectronic text\u003C/subfield\u003E\u003C/datafield\u003E\u003Cdatafield tag='830' ind1='z' ind2='z'\u003E\u003Csubfield code='a'\u003EProject Gutenberg\u003C/subfield\u003E\u003Csubfield code='v'\u003E10886\u003C/subfield\u003E\u003C/datafield\u003E\u003Cdatafield tag='856' ind1='z' ind2='z'\u003E\u003Csubfield code='u'\u003Ehttp://www.gutenberg.org/etext/10886\u003C/subfield\u003E\u003Csubfield code='3'\u003ERights\u003C/subfield\u003E\u003C/datafield\u003E\u003Cdatafield tag='856' ind1='z' ind2='z'\u003E\u003Csubfield code='u'\u003Ehttp://www.gutenberg.org/license\u003C/subfield\u003E\u003C/datafield\u003E\u003C/record\u003E",
"content_type": "application/xml",
"format":"http://jangle.org/vocab#http://www.loc.gov/MARC21/slim",
"created": "2008-03-18T15:57:00-04:00",
"id": "http://demo.jangle.org/openbiblio/resources/5878",
"relationships":
{
"http://jangle.org/rel/related#Collection": "http://demo.jangle.org/openbiblio/resources/5878/collections/"
}
}
]
}
The definitions of each field are:
The objects in the data array have the following elements:
Additional elements can be added to data objects, their keys need to conform to a convention of xmlns_prefix:element_name. Object syntax of data contained in an extension element has not yet been decided.
Explain responses are means to advertise the search attributes of a given entity. At their simplest, they provide enough information to create an OpenSearch Description Document, but they can also include the supported CQL indexes, relations and modifiers available for a search client to perform more sophisticated queries.
Example:
http://connector.jangle.org/resources/search/description/
Methods allowed: GET, HEAD
Content-type required: application/json
JSON Schema:
{
"description":"A Jangle Explain Response",
"type":"object",
"properties":{
"type":{"type":"string","pattern":"/^explain$/"},
"request":{"type":"string","format":"uri"},
"shortname":{"type":"string","optional":true,"maxLength":16},
"description":{"type":"string","maxLength":1024},
"template":{"type":"string","format":"uri"},
"contact":{"type":"string","format":"email","optional":true},
"tags":{"type":"array","items":{"type":"string"},"optional":true},
"longname":{"type":"string","optional":true,"maxLength":48},
"image":{"type":"object","optional":true,"properties":{
"height":{"type":"integer","minimum":0,"optional":true},
"width":{"type":"integer","minimum":0,"optional":true},
"type":{"type":"string","optional":true},
"location":{"type":"string","format":"url"}}
},
"query":{"type":"object","optional":true,"properties":{
"example":{"type":"string","optional":true},
"context-sets",{"type":"array","items":{"type":"object","optional":true,"properties":{
"identifier":{"type":"string","format":"uri"},
"name":{"type":"string"},
"indexes":{type":"array","items":{"type":"string"}}
}
}
},
"developer":{"type":"string","optional":true,"maxLength":64},
"attribution":{"type":"string","optional":true,"maxLength":256},
"syndicationright":{"type":"string","enum":
["open","limited","private","closed"],"optional":true},
"adultcontent":{"type":"boolean","optional":true},
"language":{"type":["string","array"],"items":{"type":"string"},"optional":true},
"inputencoding":{"type":["string","array"],"items":{"type":"string"},"optional":true},
"outputencoding":{"type":["string","array"],"items":{"type":"string"},"optional":true}
}
}
An example JSON response:
{
"type":"explain",
"shortname":"Bibliographic records",
"longname":"Search Bibliographic records in OpenBiblio",
"request":"http:\/\/connector.jangle.org\/resources\/search\/description\/",
"description":"Bibliographic records search. Defaults to keyword anywhere.",
"template":
"http:\/\/connector.jangle.org\/resources\/search\/description\/?offset={startIndex?}&
count={count?}&query={searchTerms?}&format={jangle:format?}",
"tags": ["catalog", "library"],
"syndicationright":"open",
"query":{
"example":"dc.creator=thomas",
"context-sets":[
{
"name":"dc",
"identifier":"info:srw/cql-context-set/1/dc-v1.1",
"indexes":["title","creator","subject","publisher","format","identifier"]
},
{
"name":"rec",
"identifier":"info:srw/cql-context-set/2/rec-1.1",
"indexes":["identifier","collectionName","lastModificationDate","creationDate"]
},
{
"name":"cql",
"identifier":"info:srw/cql-context-set/1/cql-v1.2",
"indexes":["allRecords","allIndexes","anyIndexes","keywords"]
}
],
},
}
}
The definitions of each field are:
Search JSON responses follow the same syntax as feed responses, the only difference being the "type" element, which should be "search".
Example:
http://connector.jangle.org/resources/search/?offset=100&count=100&query=rec.lastModificationDate>=2008-08-08
Methods allowed: GET, HEAD
Content-type required: application/json
All searchable resources must be declared as searchable in the services response and have a corresponding explain response. Connectors do not need to explicitly send a link element advertising the URI of the explain resource, since the Jangle core can glean this from the services resource.
The query terms must be in CQL syntax. If no indexes or relations are sent, the search should be translated to "cql.serverChoice all {query_terms}" (i.e. indexes of the implementor's choice, boolean ANDed). It is acceptable for a search service to support only this (i.e. no defined indexes, relations or modifiers).