3.15 JSON Mapping
THIS PAGE IS A DRAFT. IT IS OUT OF SCOPE FOR SDATA 1.0
SData provides a standard JSON mapping for feeds and entries.
Requesting JSON Format
By default, SData feed and entries are returned in the Atom XML format. An SData consumer can request that the feed or entry be returned in JSON format by including the following HTTP Accept header in its request:
Accept: application/json
An alternative is to use the format query parameter:
http://www.example.com/sdata/myApp/myContract/prod/accounts?format=application/json
The first mechanism should be used when the consumer (the user agent) will systematically request the JSON format. The second one is more appropriate when the consumer normally uses Atom but switches to JSON occasionally.
JSON Feed Example
Our SData Atom feed example (see Typical SData Feed section) becomes:
{ "$url": "http://www.example.com/sdata/myApp/myContract/-/salesOrders", "$descriptor": "Sage App | Sales Orders", "$totalResults": 31465, "$startIndex": 1, "$itemsPerPage": 10, "$resources": [{ "$updated": "2008-03-31T13:46:45Z", "$key": "43660", "$descriptor": "Sales Order 43660", "$etag": "gJaGtgHyuAwW6jMI4i0njA==", "orderDate": "2001-07-01", "shipDate": null, "contact": { "$url": "http://www.example.com/sdata/myApp/myContract/-/contacts('216')", "$key": "216" }, "subTotal": 1553.10 }, { "$updated": "2008-03-31T13:46:45Z", "$key": "43661", "$descriptor": "Sales Order 43660", "$etag": "3nqPeQqoGoxQB5xf3NIijw==", "orderDate": "2001-07-01", "shipDate": null, "contact": { "$url": "http://www.example.com/sdata/myApp/myContract/-/contacts('281')", "$key": "281" }, "subTotal": 39422.12 }, { // 8 more entries }] }
JSON Entry Example
Our SData Atom entry example (see Typical SData Entry section) becomes:
{ "$url": "http://www.example.com/sdata/myApp/myContract/-/salesOrders('43660')", "$updated": "2008-03-31T13:46:45Z", "$key": "43660", "$descriptor": "Sales Order 43660", "$etag": "gJaGtgHyuAwW6jMI4i0njA==", "orderDate": "2001-07-01", "shipDate": null, "contact": { "$url": "http://www.example.com/sdata/myApp/myContract/-/contacts('216')", "$key": "216" }, "subTotal": 1553.10 }
Differences between the JSON and Atom formats
The JSON version is simpler than its Atom counterpart but it basically holds the same amount of information. The main differences are the following:
- Envelope markup (<feed>, <entry>, <link>, etc.) have been removed.
-
Redundant markup has been eliminated:
- <id>, <link rel=”self”> and sdata:url are merged into $url
- <title> and sdata:descriptor are merged into $descriptor
- urls that can be reconstructed in a predictable way have been removed (details below)* Namespaces have been eliminated but the JSON properties that hold “protocol” data (data in the Atom, opensearch, sdata namespaces) are prefixed by a dollar sign ($) so that they can be distinguished from properties that hold “application data”.
- Secondary features (<author> and <category> Atom elements, SLE elements) have been removed.
Implicit URLs
If we compare the JSON and Atom versions of the same feed, we see that many URLs have been omitted in the JSON feed. The expectation is that these URLs can be reconstructed by means of simple rules. More specifically:
- The first, last, next and previous link URLs can be derived from the feed URL by adding startIndex and count query parameters. The values of these parameters can easily be computed from the $startIndex, $totalResults and $itemsPerPage information which is included in the JSON feed. For example:
"$first": "http://www.example.com/sdata/myApp/myContract/-/salesOrders?startIndex=1&count=10", "$last": "http://www.example.com/sdata/myApp/myContract/-/salesOrders?startIndex=31461&count=10", "$next": "http://www.example.com/sdata/myApp/myContract/-/salesOrders?startIndex=11&count=10",
- The schema, template, … link URLs can be derived from the feed URL by appending /$schema, /$template, ….
"$schema": "http://www.example/sdata/myApp/myContract/-/salesOrders/$schema?version=5", "$template": "http://www.example/sdata/myApp/myContract/-/salesOrders/$template", "$post": "http://www.example/sdata/myApp/myContract/-/salesOrders", "$service": "http://www.example/sdata/myApp/myContract/-/salesOrders/$service"
- The URLs of feed entries can be obtained by appending $key to the feed URL. For example, the URL of the first feed entry is:
"$url": "http://www.example.com/sdata/myApp/myContract/-/salesOrders('43660')",
- The URLs of subobjects and subcollections can be obtained by appending the corresponding property path to the entry URL. For example, the URL of the first entry’s orderLines collection is:
"$url": "http://www.example.com/sdata/myApp/myContract/-/salesOrders('43660')/orderLines",
- The lookup link URL is obtained by stripping the key. So, for the contact subobject in the example, the lookup is:
"$lookup": "http://www.example.com/sdata/myApp/myContract/-/contacts
Embedded resources and collections
The JSON representations of related resources and collections are naturally embedded into the main resource when the consumer requests that the provider “include” them. For example, if the previous sales order resource is read with the include=contact,orderLines query parameter, the response becomes:
{ "$url": "http://www.example.com/sdata/myApp/myContract/-/salesOrders('43660')", "$updated": "2008-03-31T13:46:45Z", "$key": "43660", "$descriptor": "Sales Order 43660", "$etag": "gJaGtgHyuAwW6jMI4i0njA==", "orderDate": "2001-07-01", "shipDate": null, "contact": { "$url": "http://www.example.com/sdata/myApp/myContract/-/contacts('216')", "$key": "216", "firstName": "John", "lastName": "Doe", "email": "john.doe@acme.com" }, "orderLines": [{ "$url": "http://www.example.com/sdata/myApp/myContract/-/salesOrderLines('43660-1')", "$key": "43660-1", "salesOrderID": 43660, "lineNumber": 1, "product": { "$url": "http://www.example.com/sdata/myApp/myContract/-/products('758')", "$key": "758" }, "orderQty": 1, "unitPrice": 874.79 }, { "$url": "http://www.example.com/sdata/myApp/myContract/-/salesOrderLines('43660-2')", "$key": "43660-2", "salesOrderID": 43660, "lineNumber": 2, "product": { "$url": "http://www.example.com/sdata/myApp/myContract/-/products('437')", "$key": "437" }, "orderQty": 2, "unitPrice": 820.70 } ], "subTotal": 1553.10 }
JSON Schemas
SData also provides a standard JSON representation for schemas.
The content negociation protocol is the same as for feeds and entries (HTTP Accept header or format query parameter – see above).
SData schemas are an extension of JSON schemas, as specified in http://tools.ietf.org/html/draft-zyp-json-schema-02
The SME extensions are prefixed by a dollar sign ($), to avoid name collisions with the standard JSON schema markup.
For example, the definition or the product resource kind in our XSD schema example (see SData Example Schema section) becomes:
{ "name": "product", "title": "Product", "$role": "resourceKind", "$pluralName": "products", "$canGet": true, "$canPost": true, // more SME properties ... "properties": { "productNumer": { "type": "string", "title": "#", "optional": true, "$canSort": true, "$canFilter": true // more SME properties }, "name": { "type": "string,", "$title": "Name", "optional": true, "$canSort": true, "$canFilter": true // more SME properties } } }
TODO: This needs to be expanded with examples of constraints, types that reference other types, etc. But JSON schema seems powerful enough to capture what we capture today in XSD.
Other Payloads
The diagnoses and tracking XML payloads are mapped as follows to JSON:
{ "$diagnoses": [ { "severity": "error", "sdataCode": "BadWhereSyntax", "message": "Invalid query syntax: ...", ...}, { "severity": "warning", ...}, ... ] }
This is the markup for a top level diagnoses payload (status >= 400). Diagnoses can also be associated to feeds, resources or subresources. In this case, the $diagnoses element will appear at the same level as the $url, $key, etc. elements.
and:
{ "$tracking": { "phase": "Archiving FY 2007", "phaseDetail": "Compressing file archive.dat", "progress": 12.0, "elapsedSeconds": 95, "remainingSeconds": 568, "pollingMillis": 500 } }
SData providers SHOULD support the JSON mapping