skip to content

In OData 4.01, how does a CSDL JSON metadata document differ from CSDL XML for the same model, and how does a client request each?

level: middleimportance: nice to knowfreq 7%

answer

  1. same meta-model, twin documents
  2. edmx:Edmx versus $Version
  3. dollar-prefixed fixed members
  4. omitted defaults, and they differ
  5. Accept decides; XML if silent

basics

~20 s

In OData 4.01, CSDL XML (root edmx:Edmx) and CSDL JSON (one object with $Version and $EntityContainer) describe the same model; JSON omits defaults, and some defaults differ, such as nullability. A plain GET $metadata returns XML; application/json asks for JSON.

solid answer

~40 s

OData 4.01 defines the metadata meta-model in two twin documents. **CSDL XML** has an `edmx:Edmx` root with a `Version` attribute and one `edmx:DataServices` holding `edm:Schema` elements. **CSDL JSON** is one object with `$Version`, `$EntityContainer` (namespace-qualified) and a member per schema; model elements are members keyed by name, fixed members are `$`-prefixed, annotations are `@Term#Qualifier` members, and alias-qualified names are required where an alias exists. JSON omits defaults, and its defaults differ: an absent `$Type` means `Edm.String`, an absent `$Nullable` means not nullable, while XML treats a single-valued property without `Nullable` as nullable. A plain `GET $metadata` MUST return XML; `Accept: application/json` or `$format=json` asks for JSON.

code

json · 22 lines
json
{
  "$Version": "4.01",
  "$EntityContainer": "Sales.Container",
  "$Reference": {
    "https://oasis-tcs.github.io/odata-vocabularies/vocabularies/Org.OData.Core.V1.json": {
      "$Include": [ { "$Namespace": "Org.OData.Core.V1", "$Alias": "Core" } ]
    }
  },
  "Sales": {
    "Customer": {
      "$Kind": "EntityType",
      "$Key": [ "ID" ],
      "ID": { "$Type": "Edm.Int32" },
      "Name": { "@Core.Description": "Legal name" },
      "Nickname": { "$Nullable": true }
    },
    "Container": {
      "$Kind": "EntityContainer",
      "Customers": { "$Collection": true, "$Type": "Sales.Customer" }
    }
  }
}

go deeper

for a junior

Recall that $metadata comes as CSDL XML by default and, from OData 4.01, also as CSDL JSON when the client asks for application/json.

for a middle

Explain how each form is shaped, edmx:Edmx with schemas versus one object with $Version, $EntityContainer and $-prefixed members, and how annotations are written in each.

for a senior

Point out the defaults that differ, nullability above all, and how a converter or reader that carries XML habits into JSON corrupts every generated type.

for a principal

Weigh publishing both forms against keeping two representations consistent, and decide which one your client tooling standardises on and tests against.

## One meta-model, two serializations OData 4.0 described metadata in a single CSDL document that defined both the meta-model and its XML form. OData 4.01 replaced it with two twin specifications, **CSDL XML** and **CSDL JSON**, whose representation-independent text is identical. Both describe the same entity model: schemas, types, properties, navigation properties, operations, the entity container and annotations. The differences are in shape, in naming rules and in defaults. ## CSDL XML - The root element `edmx:Edmx` MUST carry a `Version` attribute (`4.0` for a 4.0 response, `4.01` for a 4.01 response) and exactly one `edmx:DataServices` element. - `edmx:DataServices` holds one or more `edm:Schema` elements, each with a `Namespace` and optionally an `Alias`. - Other documents, vocabularies included, are brought in with `edmx:Reference Uri="..."` and `edmx:Include Namespace="..." Alias="..."`. - Model elements are XML elements such as `EntityType`, `Property` and `EntitySet`; annotations are `Annotation` children or are grouped under `Annotations Target="..."`. - References may use namespace-qualified or alias-qualified names. ## CSDL JSON - One JSON object that MUST contain `$Version` (`4.0` or `4.01`). If it is a service's metadata document it MUST also contain `$EntityContainer`, the namespace-qualified name of the container, the one place where an alias is not allowed. - Each schema is a member named by its namespace. Types, operations and the container are members named by their simple names, so a client can look anything up by name without knowing its kind first. - Fixed members are prefixed with `$` and mirror the XML names (`$Key`, `$Type`, `$Nullable`), plus `$Kind`, whose value is the XML element's local name. One rename: an entity set's `EntityType` attribute becomes `$Type`. - References are a `$Reference` object whose entries carry `$Include` arrays. - Where an alias is defined, JSON requires alias-qualified names. - An annotation is a member named `@`, the term, and an optional `#` plus qualifier, for example `"@Core.Description#Tablet"`; external targeting uses a schema's `$Annotations` member. - To save size, `$Kind` is optional for structural properties, `$Type` is optional for strings, and members that hold their default value SHOULD be omitted. ## The defaults that differ Because JSON omits defaults so aggressively, a reader has to know them, and they are not all the same as XML's: | Detail | CSDL XML | CSDL JSON | |---|---|---| | Property type | `Type` attribute MUST be present | absent `$Type` means `Edm.String` | | Single-valued property, nullability unspecified | `Nullable` defaults to `true` | absent `$Nullable` means `false` | | Kind of element | the element name | `$Kind`, optional for structural properties | | Qualified names | namespace or alias | alias required where one is defined | A converter or a hand-written reader that carries the XML habit "no nullability facet means nullable" into JSON reads every non-nullable property of a JSON document as nullable; a JSON-to-XML converter that copies the omission instead of writing `Nullable="false"` makes the same error in the other direction. Generated client types then get nullability wrong across the whole model, which only shows up when a `null` arrives or is rejected. ## Requesting each form 1. `GET {service-root}/$metadata` with no `Accept` header and no `$format`: the service MUST return CSDL XML (`application/xml`). 2. `Accept: application/json`, or `$format=json`: asks for CSDL JSON. In metadata requests, `application/xml`, `application/json` and the abbreviations `xml` and `json` are reserved for these two representations. 3. When `$format` and `Accept` are both present, `$format` wins; a service that cannot produce the requested format answers `406 Not Acceptable`. 4. A client limited to 4.0 sends `OData-MaxVersion: 4.0`; the service MUST then return a 4.0 response, with `Version="4.0"` on `edmx:Edmx`. The conformance rules: a 4.01 service at the minimal level SHOULD publish `$metadata` in both representations; at the 4.0 minimal level CSDL XML is a SHOULD and CSDL JSON a MAY; the 4.0 advanced level MUST publish CSDL XML. A service can list the media types it offers for `$metadata` with the `Capabilities.SupportedMetadataFormats` annotation on its entity container. ## Choosing a form as a client - CSDL XML is the default and the form every 4.0 client understands. - CSDL JSON suits clients that already parse JSON and want name-based lookup; together with the 4.01 JSON batch format it allows pure JSON communication with a 4.01 service. - Whichever form a client reads, it must apply that form's own defaults, not the other's.

  • Why does OData CSDL JSON let most properties omit $Kind but always mark navigation properties?
    Structural properties are far more common, so CSDL JSON makes `$Kind` optional for them to keep documents small, while a navigation property object MUST contain `$Kind` with the value `NavigationProperty`. A reader can therefore treat any property object without `$Kind` as structural.
  • A client that only understands OData 4.0 fetches $metadata from a 4.01 service; what keeps the exchange working?
    The client sends `OData-MaxVersion: 4.0`, and the service MUST then answer with a 4.0 response, whose `edmx:Edmx` carries `Version="4.0"`. Without an `Accept` header or `$format` the document is CSDL XML, the only representation a 4.0 client knows.

saying these in an interview costs you the question

  • CSDL JSON is the XML document converted with element names kept as keys.
  • A CSDL JSON property without $Nullable is nullable, just as in CSDL XML.
  • CSDL JSON is served at a different URL from $metadata.
  • CSDL JSON may use full namespaces even where an alias is defined.
  • Any OData 4.0 service can be expected to serve CSDL JSON.