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?
answer
- same meta-model, twin documents
- edmx:Edmx versus $Version
- dollar-prefixed fixed members
- omitted defaults, and they differ
- Accept decides; XML if silent
basics
~20 sIn 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 sOData 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{
"$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
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.
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.
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.
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.