Académique Documents
Professionnel Documents
Culture Documents
1|Page
Motivation
OData/WCF Data Services currently supports a JSON format that has a couple of problems. The two that inspired the design of JSON Light are: Payload size: the current JSON format is too verbose on the wire thus unnecessarily increasing the payload size of a response. As a result we now refer to the existing JSON format also as JSON verbose. Ease-of-use for JScript/jQuery processors: JSON verbose has a set of wrapper objects (especially around collections) that make the format hard to use in JScript/jQuery since these wrappers were sprinkled in between the actual data. As a result extracting the actual data (without any control information) as JSON involved stripping out all such wrappers and, for a set of clients, made it harder to access the data than it should be.
Disclaimer: this document represents preliminary thinking about a light-weight JSON format for OData. Not all aspects of this new format have been locked down yet and in particular the names and constants are likely to change.
The approach we took to accomplish the above is as follows: in order to make the new format easy to process by JScript/jQuery clients, we have to continue to use a JSON representation on the wire (as opposed to a binary representation for example). Looking at the current JSON (verbose) payloads, large parts of the payload are highly stylized (for example, the edit links for all the entries in a feed might only differ in the value of the key property of the entry but otherwise be identical). To minimize the payload size, all the stylized pieces in the payload were removed from the actual wire format. Instead, expressions to compute the stylized payload parts were added in annotations to the metadata
2|Page
document. These metadata expressions (together with the data on the wire) can be used by the client to re-compute all the stylized payload pieces as if they would have been on the wire directly. The extensibility story of the new JSON Light format revolves around so-called annotations in the payload. Annotations are used to non-intrusively add control information to a JSON Light payload. Such annotations can refer to a JSON object or a JSON property and provide additional information about the instance (or property). OData defines a set of annotations and custom annotations can be adding domain-specific control information to the payload. In JSON Light, annotations are used to capture control information that cannot be expressed via metadata expressions (e.g., the next link of a feed) as well as to provide a mechanism to override the values that would otherwise be computed from the expressions (e.g., if the stream read link of one particular entry points to a different server than the metadata expression for all the other entries specifies). Computing values from metadata expressions is compute intensive and some clients might choose a larger payload size over the necessity to compute expressions; as a result the new $metadata query option allows the client to control whether or not control information should be embedded into the payload or will be computed from expressions. To make JSON Light payloads support streaming scenarios, we put in place a few restrictions with respect to order in which data must appear on the wire. More details are discussed in CONTENT TYPE AND Payload Ordering Constraints. The community is engaged via a shared repository for the JSON Light spec on GitHub and via various OData distribution and mailing lists.
Entry
{ "odata.metadata": "http://northwind.odata.org/$metadata #NorthwindModel.NorthwindEntities/Customers/@Element" "CustomerID": "ALFKI", "CompanyName": "Alfreds Futterkiste", "ContactName": "Maria Anders", "ContactTitle": "Sales Representative", "Address": "Obere Str. 57", "City": "Berlin" }
Comments:
3|Page
The odata.metadata property is an instance annotation that describes the kind and type of the payload. More details on the odata.metadata property can be found in THE METADATA URL. All other properties are regular data as you would expect in a JSON representation of a customer. Note in particular that no navigation properties are present in the payload since they are control information (e.g., the navigation link) that can be computed from metadata expressions.
Feed
{ "odata.metadata": "http://northwind.odata.org/$metadata #NorthwindModel.NorthwindEntities/Customers" "odata.count": 42, "value": [ { "CustomerID": "ALFKI", "CompanyName": "Alfreds Futterkiste", ... }, ... ], "odata.nextLink": "..." }
Comments: For non-object payloads we need a wrapper object at the top-level to store the odata.metadata property as well as other control information (e.g. the next link or count in the case of a feed, or the type name of an open primitive property). o Note that when a wrapper object is used, the actual data property is always the value of the value property. o For payloads that are represented as top-level object (e.g., entities), no wrapper is needed.
Property
{ "odata.metadata": "http://northwind.odata.org/$metadata #NorthwindModel.Customer/ContactName", "value": "Maria Anders" }
Comments: The sample above shows a primitive property payload. The same structure of top-level property payload holds for complex properties (where the value of the value property would be a JSON object) and collection properties (where the value of the value property would be a JSON array).
4|Page
Comments: The above sample shows an order entry with expanded Customer and Order_Details navigation properties. Note that the values of the Customer and Order_Details navigation properties are a plain JSON object and a plain JSON array as they would naturally be modeled in JSON.
Service document
{ odata.metadata: " http://northwind.odata.org/$metadata" value: [ { name: "Categories" url: "http://northwind.odata.org/Categories" }, ... ] }
Comments: The service document contains an array of collections each of which consists of the name of the entity set and the URL to reach the data in the collection. Note that the URL can be a relative URL (and is made absolute in the sample only for demonstration purposes).
5|Page
Comments: The first sample shows the entity reference link for the singleton navigation property Customer on type Order. The second sample shows the entity reference links for the collection navigation property Orders on type Customer (including the next link and count of the link collection). The third sample is similar to the first one in that it has a single entity reference link; but this time the sample is a singleton entity reference link selected from the set of entity reference links for the Orders navigation property on type Customer. Note how the @Element item selector in the metadata URI indicates that the payload is a single item from a collection; more on the metadata URI and its structure can be found in THE METADATA URL.
Comments: 6|Page
A top-level (streaming) collection is a streaming collection of primitive or complex values produced by an operation (service operation, function or action). Note that the only difference between a top-level collection property and a top-level streaming collection is that the metadata URI for a (collection) property points to a property while it points to a function import in the streaming collection case.
Error
{ "odata.error": { "code": "error-code", "message": { "lang": "en-US", "value": "Error in query syntax." } } }
Comments: Note that error payloads do not have a metadata URI. Also note that top-level error payloads and in-stream errors share the same format on the wire.
Entry
{ "CustomerID": "ALFKI", "CompanyName": "Alfreds Futterkiste", "ContactName": "Maria Anders", "ContactTitle": "Sales Representative", "Address": "Obere Str. 57", "City": "Berlin" }
Comments: A typical entry can be (modified and) round-tripped to the server directly.
7|Page
The odata.metadata annotation can but does not have to be removed; the server will ignore it if it is present.
Comments: The above payload can be used when inserting a new order and at the same time inserting a customer and order details. The format for this payload kind (again modulo the odata.metadata annotation) is the same as for the response payload.
8|Page
When inserting or updating an entry (an order in this sample), relationships of navigation properties can be changed via bind operations. A bind operation is encoded as a property annotation on the property it belongs to and has a single value (for singleton navigation properties) or an array of values (for collection navigation properties). Note that the above payload shows that for insert scenarios, collection navigation property bind operations and deep insert operations can be combined. In this case, the bind operations have to appear before the deep insert operations in the payload. In update scenarios, only bind operations but not deep inserts are allowed.
Property
{ "value": Maria Anders }
Comments: Property payloads in requests do not differ from property payloads in responses (modulo the odata.metadata annotation).
Comments: As with property payloads, entity reference link payloads do not differ from the response format (modulo the odata.metadata annotation).
Parameters
{ "param1": 42, "param2": { "Street": "One Microsoft Way", "Zip": 98052 }, "param3": [ 1, 42, 99 ] }
The values of a parameter record follow the same format rules as values for primitive, complex and collection properties.
Feed
{metadata-uri}#{schema}.{entity-container}/{entity-set} http://northwind.odata.org/$metadata#NorthwindModel.NorthwindEntities/Customers
If all entries in a feed are known to not only come from the specified set but all be of a more derived type than the base type of the set a type cast segment is added to the metadata URI
{metadata-uri}#{schema}.{entity-container}/{entity-set}/{schema}.{type-name} http://northwind.odata.org/$metadata# NorthwindModel.NorthwindEntities/Customers/NorthwindModel.DerivedCustomerType
TODO: metadata URLs for entries and feeds also allow the optional specification of the $select projection (to guide processors what metadata expressions to expand). We have not settled on naming, scope and syntax. Add this information here once we do.
Entry
{metadata-uri}#{schema}.{entity-container}/{entity-set}/@Element http://northwind.odata.org/$metadata# NorthwindModel.NorthwindEntities/Customers/@Element
If the entry is known to be of a more derived type than the base type of the set a type cast segment is added to the metadata URI
{metadata-uri}#{schema}.{entity-container}/{entity-set}/{schema.}{type-name}/@Element http://northwind.odata.org/$metadata# NorthwindModel.NorthwindEntities/Customers/NorthwindModel.DerivedCustomer/@Element
Property
10 | P a g e
A property payload can be produced either by a query for the value of a property or by calling an operation (service operation, function, action).
{metadata-uri}#{schema}.{type-name}/{property-name} http://northwind.odata.org/$metadata# NorthwindModel.NorthwindEntities/Customers(ALFKI)/ContactName {metadata-uri}#{schema}.{entity-container}/{function-import-name} http://northwind.odata.org/$metadata# NorthwindModel.NorthwindEntities/TopTenOrderAmounts
Service Document
The metadata URI in a service document is the URI of the metadata document itself
{metadata-uri} http://northwind.odata.org/$metadata
Top-level collection
Such streamable collections of primitive and complex types are only produced by operations (service operations, functions, actions)
{metadata-uri}#{schema}.{entity-container}/{function-import-name} http://northwind.odata.org/$metadata# NorthwindModel.NorthwindEntities/TopTenOrderAmounts
Error
No metadata URI exists for a top-level error payload 11 | P a g e
Parameters
No metadata URI exists for parameter payloads because they can only appear in requests where metadata URIs dont exist
Annotations
Annotations are properties that have a dot (.) in the name There are instance annotations and property annotations o Instance annotations belong to the object they appear in (e.g., odata.metadata, odata.count, etc.) o Property annotations belong to the property that is part of their name (e.g., Orders@odata.navigationlinkurl, etc.) Instance annotations are used when annotating a value that is a JSON object; property level annotations are used to annotate a value that is a primitive value or an array (in both cases there is no place to put the annotation inside the value). Property-level annotations are also used to provide annotations for values that are not present in the payload (e.g., there is no JSON property for a navigation property in the payload; instead the navigation link is specified via a property annotation). All annotations that start with odata. are reserved for future extensions of the protocol and format Custom annotations are annotations that have a non-empty prefix that is different from odata.. Custom annotations are currently ignored when reading payloads.
{ "odata.metadata": "http://northwind.odata.org/$metadata #NorthwindModel.NorthwindEntities/Customers" "value": [ { "odata.type": "NorthwindModel.PreferredCustomer", "CustomerID": "ALFKI", "CompanyName": "Alfreds Futterkiste", ... }, ...
12 | P a g e
an open property. The annotation is a property annotation for open primitive and collection properties and an instance annotation inside a complex property value. The sample below shows an open property OpenAge of type Edm.Int32 where the type is specified via a property annotation.
o a feed (at the top-level or inside an expanded collection property) o a collection of entity reference links ($links response) at the top level See the sample in Section FEED above. The rest of the odata.* annotations are used to provide control information for an entry. Note that all of the control information for an entry is usually computed from metadata expressions and thus not present in the payload. odata.* annotations can be present in the payload to override the metadata expression evaluation in the case of exceptions to them (e.g., a custom edit link). o Instance annotations are used for the ID, ETag, read link, edit link, media read link, media edit link, media content type, media ETag, actions and functions of an entry. o Property annotations are used to describe the navigation and association links of a property, the read link, edit link, content type and ETag of a named stream The amount of control information in the payload can also be controlled by the $metadata query option. See Section CONTROLLING THE AMOUNT OF CONTROL INFORMATION IN THE PAYLOAD for more details. An example that shows all the OData instance and property annotations in the payload, i.e., if none of the control information would be computed from metadata expressions, is shown in Section $METADATA=ALL.
Annotation Groups
Annotation groups are a way to group multiple instance and/or property annotations into a named group that can be reused throughout the payload. Annotation groups are useful to further reduce the size of the payload when the same set of annotations appears repeatedly in a payload. The typical scenario when this happens is when a feed contains entries with open entity types and the same set of open (i.e., undeclared) properties appears in multiple or all of the entries in the feed. For each open property, a property annotation is used to describe its type. Without annotation groups these property 13 | P a g e
annotations would have to appear in every entry of a feed; annotation groups provide a way to declare these property annotations only once and then reference them.
{ "odata.metadata": "http://northwind.odata.org/$metadata #NorthwindModel.NorthwindEntities/OpenCustomers" "value": [ { "odata.annotationgroup": { "name": "openpropertytypes", "OpenAddress@odata.type": "Edm.String", "OpenPhone@odata.type": "Edm.Int32", }, "CustomerID": "ALFKI", "CompanyName": "Alfreds Futterkiste", "OpenAddress": "Obere Str. 57, 12209 Berlin, Germany", "OpenPhone": 0074321, ... }, { "odata.annotationgroupref": "openpropertytypes" "CustomerID": "ANTON", "CompanyName": "Antonio Moreno", "OpenAddress": "Mataderos 2312, 05023 Mxico D.F., Mexico", "OpenPhone": 5553932, ... }, ... ], }
Annotation groups are a syntactic grouping construct only; they do not introduce any new semantic meaning. The semantics of the annotation group is the same as if the annotations in the group would be inlined directly inside the owning object. The advantage is that instead of having to repeat the annotations in each entry of a feed, only the first entry defines the annotation group, the other entries simply reference it by name. Note that the declaration of an annotation group also applies the annotation group to the declaring object; thus there is no reference to the annotation group in the entry that declares it. On the other hand that means that an annotation group can only be introduced in an object that also applies it (and not as stand-alone construct at the beginning of the feed or always inside the first entry). Annotation groups and annotation group references have to be the first property in an object; in top-level objects, annotation groups and annotation group references appear after the odata.metadata annotation (and thus are the objects second property).
14 | P a g e
$metadata=default
The default $metadata query option does not have to be included in the request URL and will be assumed if no other $metadata query option is present in the URL. It indicates that the server should use metadata expressions (to the extent the server supports them) to reduce the payload size. The response payload should also contain the odata.metadata annotation, the odata.nextLink and the odata.count annotations. Note that more odata.* annotations will appear in the payload if their values are not the same as the ones computed from metadata expressions (or the server does not support metadata expressions) and have to be treated as exceptions.
$metadata=none
When the client specifies that no control information should be included in the payload, it is assumed that the client has out-of-band knowledge of the payload kind and data types in the payload (or does not care). In this case, the odata.metadata URL is omitted from the payload otherwise the same set of control information as in the default option is present in the payload (including odata.nextLink and odata.count annotations as well as exceptions to the values that would be computed from metadata expressions.)
$metadata=all
In cases where the client is not capable of computing control information from metadata expressions or favors larger payload size over the additional computational requirements, $metadata=all tells the server to compute the metadata expressions on the server and include the control information explicitly in the payload. This, of course, will re-introduce the redundancies and inefficiencies of the current JSON verbose format but is a way less capable or resource constrained clients to work with the JSON Light format. The sample below is a feed response payload where all the control information has been embedded in the payload. 15 | P a g e
"odata.metadata": "http://northwind.odata.org/$metadata #NorthwindModel.NorthwindEntities/Customers", "odata.count": 42, "value": [ { "odata.id": "http://northwind.odata.org/Customers(ALFKI)" "odata.etag": "etag-value" "odata.readLink": "http://northwind.odata.org/read/Customers(ALFKI)" "odata.editLink": "http://northwind.odata.org/edit/Customers(ALFKI)" "odata.mediaReadLink": "http://northwind.odata.org/Customers(ALFKI)/Image" "odata.mediaEditLink": "http://northwind.odata.org/Customers(ALFKI)/$value" "odata.mediaETag": "stream-etag-value" "odata.mediaContentType": "image/jpg" "odata.actions": [ ... actions for this entry ...] "odata.function": [ ... functions for this entry ...] "VoiceMail@odata.mediareadLink": "http://.../Customers(ALFKI)/Voice" "VoiceMail@odata.mediaeditLink": "http://.../Customers(ALFKI)/Voice/$value" "VoiceMail@odata.mediaetag": "voice-etag-value" "VoiceMail@odata.mediacontentType": "audio/basic" "Orders@odata.navigationLinkUrl": "http://.../Customers(ALFKI)/Orders" "Orders@odata.associationLinkUrl": "http://.../Customers(ALFKI)/OrdersRel" "CustomerID": "ALFKI", "CompanyName": "Alfreds Futterkiste", "OpenAddress@odata.type": "Edm.String" "OpenAddress": "Obere Str. 57, 12209 Berlin, Germany", ... }, ... ], "odata.nextLink": "http://northwind.odata.org/next(123122)" }
odata.metadata: the metadata URI of the payload. odata.count: the inline count of a feed or collection of entity reference links. odata.nextLink: the next link of a feed or collection of entity reference links. odata.id: the ID of the entry. odata.etag: the ETag of the entry. odata.readLink: the link used to read the entry. odata.editLink: the link used to edit/update the entry. odata.mediaReadLink: the link used to read the default stream of an MR/MLE entry. odata.mediaEditLink: the link used to edit/update the default stream of an MR/MLE entry. odata.mediaETag: the ETag of the default stream of an MR/MLE entry. odata.mediaContentType: the content type of the default stream of an MR/MLE entry.
16 | P a g e
VoiceMail@odata.mediaReadLink: the link used to read the stream of the VoiceMail named
stream property.
VoiceMail@odata.mediaEditLink: the link used to edit/update the stream of the VoiceMail
property.
Orders@odata.navigationLinkUrl: the link used to retrieve the values of the Orders
navigation property.
Orders@odata.associationLinkUrl: the link used to describe the relationship between the
customer instance and its orders. OpenAddress@odata.type: the type name of the open property OpenAddress.
17 | P a g e
The odata.id and odata.etag annotations must appear before any property or property annotation. All property annotations for property SomeProperty have to appear as a group immediately before the property itself. Note that the one exception to this rule is the SomeProperty@odata.nextlink annotation of an expanded collection navigation property; the next link can oftentimes only be computed after all the entries in a feed have been written and thus is allowed to appear after the SomeProperty property. All other odata.* annotations can appear anywhere in the payload (as long as they are not violating any of the above rules).
Metadata Expressions
Metadata expressions are specified in the metadata document (returned from the $metadata endpoint of an OData service) using CSDL annotations. The metadata expressions represent instructions how to compute parts of the OData control information for an entry. When reading an OData payload, these expressions are then used to compute control information that has been omitted from the payload. They are also used to guide a URI builder when producing request URLs for a query. As a result, the metadata expressions give you a way to completely customize the URI scheme you use with your service. This section first gives an overview of how CSDL annotations are represented in the metadata document and then focuses on the OData metadata expressions themselves.
CSDL Annotations
Metadata expressions are encoded in the metadata document as CSDL annotations. Such annotations have a target that describes what part of the metadata they apply to (e.g., an entity set or an entity container or an entity type). A qualifier supports further specifying what part of the target the annotation should be applied to (e.g., the target of an annotation might be an entity type and the qualifier would specify a particular property within this entity type). Once the target is identified, the annotation has a term that allows us to annotate a target with multiple annotations for different purposes using different terms. Finally the annotation has a value or an expression body. Here is the general structure of a CSDL annotation (more details about CSLD annotations can be found in the CSDL spec (TODO: add reference)).
<Annotations Target="{annotation-target}"> <ValueAnnotation Term="{term}" Qualifier="{qualifier}" String="{string-value}" /> <ValueAnnotation Term="{term}" Qualifier="{qualifier}"> {expression-body} </ValueAnnotation> </Annotations>
odata.expression.baseUri: the base URI for all the URIs built for the entity container specified
in as the target. If this annotation is not specified, the metadata URI of the service is used as a base URI. Entity set target
odata.expression.entitySetUri: the relative or absolute URI for the annotated entity set. odata.expression.entityInstanceUri: the relative or absolute URI for an entity instance of
(specified in the qualifier) of an entity instance of the annotated entity set. odata.expression.associationLinkUri: the relative or absolute URI for the association link (specified in the qualifier) of an entity instance of the annotated entity set. odata.expression.id: the ID of an entity instance of the annotated entity set. odata.expression.etag: the ETag of an entity instance of the annotated entity set. odata.expression.operationTargetUri: the target URI of an operation (action or function) for an entity instance of the annotated entity set. odata.expression.operationTitle: the title of an operation (action or function) for an entity instance of the annotated entity set odata.expression.mediaReadLinkUri: the relative or absolute URI for the read link of the stream property (specified in the qualifier) of an entity instance of the annotated entity set. If the qualifier is left empty, the annotation applies to the default stream of an MLE/MR entry. odata.expression.mediaEditLinkUri: the relative or absolute URI for the edit link of the stream property (specified in the qualifier) of an entity instance of the annotated entity set. If the qualifier is left empty, the annotation applies to the default stream of an MLE/MR entry. odata.expression.mediaETag: the ETag of the stream property (specified in the qualifier) of an entity instance of the annotated entity set. If the qualifier is left empty, the annotation applies to the default stream of an MLE/MR entry. odata.expression.mediaContentType: the content type of the stream property (specified in the qualifier) of an entity instance of the annotated entity set. If the qualifier is left empty, the annotation applies to the default stream of an MLE/MR entry.
Here is a sample of the annotations for the Customers entity set needed to compute the navigation link URI of the Orders navigation property.
<Annotations Target="NorthwindModel.NorthwindEntities"> <ValueAnnotation Term="odata.expression.baseUri" String="http://northwind.odata.org/" /> </Annotations> <Annotations Target="NorthwindModel.NorthwindEntities/Customers"> <ValueAnnotation Term="odata.expression.entitySetUri" String="Customers/" /> <ValueAnnotation Term="odata.expression.entityInstanceUri"> <Apply Function="odata.stringConcat">
19 | P a g e
<Apply Function="odata.toRawLiteral"> <Path>ID</Path> </Apply> <String>/</String> </Apply> </ValueAnnotation> <ValueAnnotation Term="odata.expression.navigationLinkUri" Qualifier="Orders" String="Orders/"> </Annotations>
You will note that this is not a single expression for the navigation link URI but instead a set of expressions that will be combined to produce the actual navigation link URI. The reason for that is that when creating request URIs we need to be able to recursively follow navigation links and thus composability of the expressions is a must. How the composition works (based on the sample expressions above) will be shown in the next section. Also note that when no expressions are specified in the metadata document, the WCF Data Services default expressions are assumed. Similarly, when only a select few expressions are specified in the metadata document, the default expressions will be used for the non-specified targets. For cases where this behavior is undesired, a new query option is introduced that will control whether default expressions should be assumed or not. The query option (in spirit) will be $useDefaultExpressions=true|false but the actual naming and allowed values are still being discussed.
Computed Value
Composition rule
(values in curly braces are results from other expressions, i.e., {odata.editLink} is the result )
odata.expression.id odata.expression.etag odata.expression.baseUri & odata.expression.entitySetUri & odata.expression.entityInstanceUri (TODO: fix this composition) odata.expression.baseUri &
odata.editLink
20 | P a g e
{odata.editLink} & odata.expression.mediaReadLinkUri [no qualifier] {odata.editLink} & odata.expression.mediaEditLinkUri [no qualifier] odata.expression.mediaETag [no qualifier] odata.expression.mediaContentType [no qualifier]
{odata.editLink} & odata.expression.mediaReadLinkUri [<named-stream> qualifier] {odata.editLink} & odata.expression.mediaEditLinkUri [<named-stream> qualifier] odata.expression.mediaETag [<named-stream> qualifier] odata.expression.mediaContentType [<named-stream> qualifier]
<nav-prop>@odata.navigationLinkUrl <nav-prop>@odata.associationLinkUrl
{odata.editLink} & odata.expression.navigationLinkUri [<nav-prop> qualifier] {odata.editLink} & odata.expression.associationLinkUri [<nav-prop> qualifier]
odata.expression.operationTitle [qualifier is the name of the action or function] {odata.editLink} & odata.expression.operationTargetUri [qualifier is the name of the action or function]
Combinator Functions
This section explains some details around the & operator used in the previous section to combine values computed from metadata expressions to form a full URI. In general and if no combinatory function is specified in the expression we use standard URI composition rules and thus treat the values created from metadata expressions as relative URIs. For some URI schemes, however, this does not work and so two more combinatory functions can be used to change how the values are combined. To make the different behaviors of the combinator functions more obvious, we use an example of an entity set URI http://northwind.odata.org/Customers and a relative entity instance URI (1). 21 | P a g e
URI composition: the default combinatory function (that can be omitted from the payload). Follows standard URI composition rules. o Composition result: http://northwind.odata.org/(1) o Since the Customers segment is not terminated in a /, URI composition rules will replace the last segment with (1). String concatenation: treats the values generated from the metadata expressions as strings and simply concatenates them. o Composition result: http://northwind.odata.org/Customers(1) AddSegment: a combinator function that expresses that the value computed from the metadata expression should be added as a new URI segment to the URI created so far. o Composition result: http://northwind.odata.org/Customers/(1) o Since the Customers segment is not terminated in a/, the AddSegment composition will first append a / (if none exists) and then apply URI composition rules.
Here is how the combinatory function is specified for a metadata expression; it is an annotation on the annotation.
<Annotations Target="NorthwindModel.NorthwindEntities"> <ValueAnnotation Term="odata.expression.baseUri" String="http://northwind.odata.org/" /> </Annotations> <Annotations Target="NorthwindModel.NorthwindEntities/Customers"> <ValueAnnotation Term="odata.expression.entitySetUri" String="Customers/" /> <ValueAnnotation Term="odata.expression.entityInstanceUri"> <ValueAnnotation Term="odata.combinator" String="StringConcat" /> <Apply Function="odata.toRawLiteral"> <Path>ID</Path> </Apply> </ValueAnnotation> <ValueAnnotation Term="odata.expression.navigationLinkUri" Qualifier="Orders" String="Orders/"> <ValueAnnotation Term="odata.combinator" String="AddSegment" /> </ValueAnnotation> </Annotations>
URIs.
odata.toRawLiteral: converts the value of its argument to its raw from. odata.getTypeName: gets the type name of the current entry. odata.stringConcat: concatenates multiple strings.
Functions are used with an <Apply> expression as shown in the sample above.
22 | P a g e
Conclusion
In closing, please remember that this is an introductory document and by no means exhaustive. It has no normative character whatsoever and some things are likely going to change as we progress in our designs and implementations. If you got a better idea what the JSON Light format for OData is all about and what overall direction we are taking, this document has served its purpose. For more details, questions, feedback or comments please contact odatateam@microsoft.com.
23 | P a g e