SData prototypes
Prototypes play a role roughly corresponding to schemas in SData v1.1; the advantage is that they are expressed as SData JSON objects and are more flexible and compact than XML Schema Definition documents. Prototypes define the native and metadata properties of a resource kind, specifying the corresponding types and, for metadata, the default values.
The following subsections introduce the SData prototypes by:
- describing the format and function of the $prototype object,
- indicating how a $prototype object can surface in a response,
- showing how prototypes can be retrieved individually.
A prototype is a resource that bundles the metadata of a resource kind representation. If metadata is provided, the usage of prototypes in the JSON context is strongly RECOMMENDED but not mandatory.
$prototype object
A prototype is a JSON object and is formed according to the rules laid out in the document “JSON formatting of SData responses”. Metadata elements that appear in a prototype are:
Compliance | Description | |
---|---|---|
$properties | MUST | contains the metadata for all the individual properties of an object |
$links | MAY | contains the elements describing the possible operations for an element (see Section Links of this document) |
Example:
Consider the following Address resource kind with metadata enclosed in square brackets:
- ID: [integer, Mandatory]
- Street: [string, title=”Street”, Mandatory]
- StreetNumber: [integer, title=”Number”]
- City: [string, title=”City”, Mandatory]
- PostalCode: [string, title=”ZipCode”, Mandatory]
- Country: [referenceToCountryResourceKind, Mandatory]
- Name: [string]
- ISOCode: [string]
- [url=”http://www.example.com/sdata/MyApp/-/-/countries(‘{ISOCode}’)”]
The prototype describing this resource is shown below:
{ "$baseURL" : "http://www.example.com/sdata/MyApp/-/-", "$properties": { "ID": { "$title": "AddressId", "$type": "sdata/integer", "$isMandatory": true }, "Street": { "$title": "Street", "$type": "sdata/string", "$isMandatory": true }, "StreetNumber": { "$title": "Number", "$type": "sdata/integer" }, "City": { "$title": "City", "$type": "sdata/string", "$isMandatory": true }, "PostalCode": { "$title": "ZipCode", "$type": "sdata/string", "$isMandatory": true }, "Country": { "$title": "Country", "$type": "sdata/reference", "$links": { "$prototype": { "$title": "Country list prototype", "$id" : "lookup", "$url": "{$baseUrl}/$prototypes/countries('{$id}')" } }, "$isMandatory": true, "$item": { "$url": "http://www.example.com/sdata/MyApp/-/-/countries('{ISOCode}')", "$properties": { "Name": { "$title": "Country name", "$type": "sdata/string", "$isMandatory": true }, "ISOCode": { "$title": "Country code", "$type": "sdata/string", "$format": "country", "$isMandatory": true } } } } }, "$links": { "$updateFull": { "$title": "Update the resource", "$type": "application/json;vnd.sage=sdata", "$url": "{$url}", "$method": "PUT" }, "$prototype": { "$title" : "Customer address prototype", Page 24 "$id" : "detail", "$url" : "{$baseURL}/prototypes/addresses('{$id}')" } } }
Prototypes exposed by an application: the $prototypes URL segment
The prototypes of an application are SData JSON resources retrievable by a GET operation. The URL segments of a prototype are formed according to the pattern:
…/$prototypes/[<resourceKindName>[(‘<prototypeId>’)]] where:
- $prototypes: is a reserved segment located at the resource kind level 8. This MUST be supported if prototype resources are present.
- resourceKindName: is the name of the resource whose prototype it is. This SHOULD be supported if at least one prototype is available for a resource kind.
- prototypeId: MAY be present. It is the identifier for the prototype and relates to the representation of a resource 9.
Retrieving the prototype of a resource kind
The prototype of a resource kind is intimately related to the representation of a resource. This means that it is possible to have several prototypes, each describing individual representations of a resource. This is easy to see when looking at the differences between a feed of resources and an individual resource: in the first case the information is succinct, while in the second it would be rather extensive. Another example is the prototype for a representation of a resource in a mobile context (where bandwidth and screen area are prime assets) compared with that for a desktop or full-browser client.
Prototypes are reasonably static. This means that they should be retrieved once, cached and then applied many times. Consequently, a versioning mechanism (eTag or modifiedDate) would greatly benefit the client-side handling of prototype and therefore providers SHOULD support such a mechanism.
Requesting metadata Section of this document described the means for retrieving prototypes. The remainder of this section provides more details on retrieving multiple prototypes via links.
For example:
GET http://www.example.com/sdata/MyApp/myContract/-/$prototypes/addresses
would provide a response similar to:
{ "$baseURL" : "http://www.example.com/sdata/MyApp/-/-", "$url" : "{$baseURL}/prototypes/addresses", "$title" : "all Address prototypes", "$resources" : [ { "$id": "detail", "$prototype": { "$url" : "{$baseURL}/addresses('{$ID}')", "$properties": { "ID": { "$title": "AddressId", "$type": "sdata/integer", "$isMandatory": true } }, "...": "...", "$links": { "$prototype": { "$id": "detail", "$url": "{$baseURL}/prototypes/addresses('{$id}')", "$title": "Customer address prototype" } } } }, { "$id": "list", "$prototype": { "$url" : "{$baseURL}/addresses('{$ID}')", "$properties": { "ID": { "$title": "AddressId", "$type": "sdata/integer", "$isMandatory": true } }, "...": "...", "$links": { "$prototype": { "$id": "list", "$url": "{$baseURL}/prototypes/addresses('{$id}')", "$title": "Customer address feed" } } } } ] }
The GET operation on the reserved $prototypes segment will return links to all the prototypes exposed by the application. The returned payload contains:
- One array property for every resource kind where prototypes are available;
- The array element contains at least the following properties pertaining to the prototype:
- $url
- $resourceKind
- $id
- $title
Example:
GET http://www.example.com/sdata/MyApp/-/-/$prototypes
Would return a payload similar to:
{ "$baseURL" : "http://www.example.com/sdata/MyApp/-/-", "$title" : "Links to all prototypes", "$totalResults" : 32, "$startIndex" : 1, "$itemsPerPage" : 10, "$resources" : [ { "$title" : "Address entry prototype", "$resourceKind" : "address", "$url" : "{$baseURL}/prototypes/addresses('detail')", "$id" : "detail" }, { "$title" : "Address feed prototype", "$resourceKind" : "addresses", "$url" : "{$baseURL}/prototypes/addresses('list')", "$id" : "list" }, { "$title" : "Customer entry prototype", "$resourceKind" : "customer", "$url" : "{$baseURL}/prototypes/customers('detail')", "$id" : "detail" }, { "$title" : "Customer feed prototype", "$resourceKind" : "customers", "$url" : "{$baseURL}/prototypes/customers('list')", "$id" : "list" } ] }
Merge process
For the consumer of a JSON formatted response that leverages metadata, objects are obtained in their entirety by merging the prototype with the payload information. The information in the payload has precedence and overlays/overrides the prototype definitions. 11
The interplay between prototype and payload has several key attributes:
- Metadata is expressed in JSON.
- Metadata is available at the resource kind level and ideally even finer-granular levels.
- Ability to override metadata at any level (feed/entry/property).
- Reduce verbosity of transferred information.
The merge process is a conceptual process, meaning that a consumer will likely use a variety of local techniques to efficiently implement it while maintaining the same overall effect.
Example: Consider the following prototype:
{ "$baseUrl": "http://www.example.com/sdata/MyApp/-/-", "$url": "{$baseUrl}/addresses", "$title": "Address list", "$properties": { "ID": { "$title": "AddressId", "$type": "sdata/integer", "$isMandatory": true }, "Street": { "$title": "Street", "$type": "sdata/string", "$isMandatory": true }, "StreetNumber": { "$title": "Number", "$type": "sdata/integer" }, "City": { "$title": "City", "$type": "sdata/string", "$isMandatory": true }, "PostalCode": { "$title": "ZipCode", "$type": "sdata/string", "$isMandatory": true }, "Country": { "$title": "Country", "$type": "sdata/reference", "$links": { "$prototype": { "$id": "lookup", "$url": "{$baseUrl}/$prototypes/countries('{$id}')", "$title": "Country lookup prototype" } }, "$url": "http://www.example.com/sdata/MyApp/-/-/countries('{ISOCode}')", "$isMandatory": true, "$item": { "$properties": { "Name": { "$title": "Country name", "$type": "sdata/string", "$isMandatory": true }, "ISOCode": { "$title": "Country code", "$type": "sdata/string", "$isMandatory": true } } } }, "$links": { "$prototype": { "$id": "list", "$url": "{$baseUrl}/$prototypes/addresses('{$id}')", "$title": "Address feed prototype" } } } }
Matching the previous prototype is the following JSON formatted payload:
{ "$baseUrl": "http://www.example.com/sdata/MyApp/-/-", "$url": "{$baseUrl}/addresses?creditLimitExceeded=true" , "$title": "Addresses of accounts with exceeded credit limit" , "$resources": [ { "ID": "7123a", "Street": "Lerchenweg", "StreetNumber": 11, "PostalCode": 71711, "City": "Marbach am Neckar", "Country": { "Name": "Germany", "ISOCode": "DE" }, "$properties": { "PostalCode": { "$isMandatory": false } } }, { "ID": "hw7631", "Street": "Fleet Street", "StreetNumber": 31, "City": "London", "PostalCode": "EC4Y 8EQ", "Country": { "Name": "United Kingdom", "ISOCode": "GB" } } ] }
The above payload provides, in addition to the payload (in blue) a series of specific metadata (in yellow background) for:
- The URL of the feed ($url)
- The title of the feed ($title)
- The type of the first - German - address, that must be numeric according to German rules
After the merge process, the logical JSON object will contain the following:
{ "$baseUrl": "http://www.example.com/sdata/MyApp/-/-", "$url": "{$baseUrl}/addresses?creditLimitExceeded=true", "$title": "Addresses of accounts with exceeded credit limit", "$resources": [ { "ID": "7123a", "Street": "Lerchenweg", "StreetNumber": 11, "PostalCode": 71711, "City": "Marbach am Neckar", "Country": { "Name": "Germany", "ISOCode": "DE" }, "$properties": { "ID": { "$title": "AddressId", "$type": "sdata/integer", "$isMandatory": true }, "Street": { "$title": "Street", "$type": "sdata/string", "$isMandatory": true }, "StreetNumber": { "$title": "Number", "$type": "sdata/integer" }, "City": { "$title": "City", "$type": "sdata/string", "$isMandatory": true }, "PostalCode": { "$title": "ZipCode", "$type": "sdata/string", "$isMandatory": false }, "Country": { "$title": "Country", "$type": "sdata/reference", "$links": { "$prototype": { "$id": "lookup", "$url": "{$baseUrl}/$prototypes/countries('{$id}')", "$title": "Country lookup prototype" } }, "$url": "http://www.example.com/sdata/MyApp/-/-/countries('{ISOCode}')", "$prototype": "{$baseUrl}/$prototypes/countries('lookup')", "$isMandatory": true, "Name": { "$title": "Country name", "$type": "sdata/string", "$isMandatory": true }, "ISOCode": { "$title": "Country code", "$type": "sdata/string", "$isMandatory": true } } }, "$links": { "$prototype": { "$id": "list", "$url": "{$baseUrl}/$prototypes/addresses('{$id}')", "$title": "Address feed prototype" } } }, { "ID": "hw7631", "Street": "Fleet Street", "StreetNumber": 31, "City": "London", "PostalCode": "EC4Y 8EQ", "Country": { "Name": "United Kingdom", "ISOCode": "GB" }, "$properties": { "...": "...", "PostalCode": { "$title": "ZipCode", "$type": "sdata/string", "$isMandatory": true } }, "$links": { "$prototype": { "$id": "list", "$url": "{$baseUrl}/$prototypes/addresses('{$id}')", "$title": "Address feed prototype" } } } ] }
Please note that the $type of the PostalCode property of the first address object is ‘sdata/integer’ as overridden by the provider.
8. For more information, see the discussion in the SData URL chapter of the “SData 2.0 - Core” document
9. It is important to note that there may be several prototypes associated with a single resource kind. For example, an application could distinguish between information delivered for a feed of a resource kind, that of an individual entry and yet again to that of a resource at creation.
11. To remove a metadata element defined in the prototype, the payload defines the property with null value.