Namespaces and Larger Schemas
The draft's vocabulary has no namespace. Most real vocabularies have
one: XHTML, SVG, Atom, and SCXML, the W3C's language for state
machines. This section, and the rest of the chapter, works in the
scxml directory, on the Vane feature flag written as an SCXML
machine, flag.scxml. Project: Self-Describing State Machines explains
the machine itself; here it is a document with three namespaces in it:
- SCXML's,
http://www.w3.org/2005/07/scxml, for the states and transitions; urn:example:vane, with the prefixvane, for the annotations that describe the machine's events and data;- XHTML's,
http://www.w3.org/1999/xhtml, for the documentation of each state and event.
Here is its start and one state:
<?xml version="1.0" encoding="UTF-8"?>
<scxml xmlns="http://www.w3.org/2005/07/scxml"
xmlns:vane="urn:example:vane"
version="1.0" name="feature_flag" initial="stable" datamodel="ecmascript">
<vane:events>
<state id="stable" vane:implements="req-2">
<div xmlns="http://www.w3.org/1999/xhtml">
<p>The flag keeps its percentage. It waits for a rollout.</p>
</div>
<transition event="rollout.request" target="checking"/>
<transition event="status.request" vane:implements="req-6">
<send event="status.response"/>
</transition>
</state>
Two schemas describe it: scxml.xsd for SCXML, and vane-data.xsd
for the annotations. XHTML has no schema here, and the machine is
valid all the same:
$ sxml validate --schema=scxml.xsd flag.scxml; echo "exit status $?"
exit status 0
This section shows how a schema describes a namespace, how two schemas for two namespaces work together, and how a schema leaves room for namespaces it doesn't know.
A target namespace
targetNamespace on xs:schema puts every global declaration and
named type of the schema into that namespace. Here is the top of
scxml.xsd:
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:vc="http://www.w3.org/2007/XMLSchema-versioning"
xmlns:sc="http://www.w3.org/2005/07/scxml"
targetNamespace="http://www.w3.org/2005/07/scxml"
elementFormDefault="qualified"
vc:minVersion="1.1">
Inside the schema, references to its own components are QNames in the
target namespace. scxml.xsd binds the prefix sc to it, so a
reference to its own group or type is written with the prefix:
<xs:complexType name="executable-block">
<xs:group ref="sc:executable" minOccurs="0" maxOccurs="unbounded"/>
</xs:complexType>
The other way is to make the target namespace the schema's default
namespace, with xmlns="http://www.w3.org/2005/07/scxml"; then
ref="executable" finds it. Either way, a reference without the right
namespace names nothing, and the schema doesn't compile. Built-in types
keep their xs: prefix.
elementFormDefault="qualified" puts the local elements, such as
state and transition, into the target namespace as well. Without
it, only global elements are in the namespace, and local elements are
in no namespace, which surprises almost everyone; most schemas say
qualified. Attributes are different: unprefixed attributes such as
id and event are in no namespace, and attributeFormDefault stays
at its default, unqualified.
Namespace mistakes
The same machine without its namespace has an element that the schema doesn't know at all:
$ sed 's| xmlns="http://www.w3.org/2005/07/scxml"||' flag.scxml | sxml validate --schema=scxml.xsd -
-:2:1: cvc-elt.1: no global declaration governs element 'scxml'
When cvc-elt.1 says no declaration governs an element that you can
see in the schema, check its namespace first.
A namespace declaration that is missing lower down moves elements into
the wrong namespace. Without its xmlns, the first state's div is in
the default namespace of its parent, SCXML's, and SCXML has no div:
$ sed '0,/<div xmlns="http:\/\/www.w3.org\/1999\/xhtml">/s//<div>/' flag.scxml \
| sxml validate --schema=scxml.xsd -
-:44:5: cvc-complex-type.2.4: element 'div' is not allowed here; expected 'onentry', 'onexit', 'transition', 'state', 'final', 'datamodel' or an element of another namespace
An element's namespace comes from the namespace declarations in scope where it is written, never from what it means.
Import
A schema can be split into several documents.
xs:includebrings in a schema document with the same target namespace (or none, in which case its components take the including schema's namespace). The draft'slevels.xsdincludesvane.xsdthis way.xs:importmakes the components of another namespace available. The vane annotations have their own schema,vane-data.xsd, with the target namespaceurn:example:vane, andscxml.xsdimports it:
<xs:import namespace="urn:example:vane" schemaLocation="vane-data.xsd"/>
vane-data.xsd declares the annotation attributes as global
attributes, so that they can be used on elements of other namespaces:
<xs:attribute name="type" type="xs:QName"/>
<xs:attribute name="payload" type="xs:QName"/>
<!-- The anchors of the requirements a state or transition carries out. -->
<xs:attribute name="implements">
<xs:simpleType>
<xs:list itemType="xs:NCName"/>
</xs:simpleType>
</xs:attribute>
schemaLocation is relative to the document that contains it. It's
only a hint on xs:import: the components can also come from another
schema document given on the command line.
Wildcards and lax processing
Nothing in scxml.xsd mentions XHTML or the vane attributes by name.
Instead, almost every type has wildcards:
<xs:any namespace="##other" processContents="lax"/>
<xs:anyAttribute namespace="##other" processContents="lax"/>
xs:any allows an element, and xs:anyAttribute an attribute, of the
namespaces it names. ##other means every namespace except the target
namespace (and except no namespace); others are ##any, ##local,
##targetNamespace, or a list of namespace names. processContents
says what happens to what the wildcard lets in:
strict: it must have a declaration, and be valid by it;lax: it is validated when the schema has a declaration for it, and let through when not;skip: it is not validated at all.
So the vane attributes, which vane-data.xsd declares, are
validated, and the XHTML, which no schema declares, is let through. A
machine with an anchor that is not a name, a type given without its
prefix, and an XHTML element that XHTML doesn't have, shows the
difference:
$ sed -e 's/vane:implements="req-5 req-8"/vane:implements="req-5 8"/' \
-e 's/vane:type="vane:seconds"/vane:type="seconds"/' \
-e 's|<p>The flag is archived.|<p><blink>The flag</blink> is archived.|' flag.scxml \
| sxml validate --schema=scxml.xsd -
-:80:22: cvc-datatype-valid.1.2.1: attribute 'vane:implements' has the value 'req-5 8', which is not valid for an anonymous type derived from 'xs:anySimpleType'
Only the anchor is reported. 8 is not an xs:NCName, so it can't be
an item of the list. seconds is a valid xs:QName, the name seconds
in the default namespace, SCXML's; that it names no type is a mistake
the schema can't see, and Checking What the Schema
Cannot finds it. blink is not checked at all.
Lax processing has a price: when a declaration is missing, nothing is
validated and nothing is said. Without the schemaLocation of the
import, scxml.xsd doesn't find vane-data.xsd, and the broken
anchor goes through:
$ sed 's| schemaLocation="vane-data.xsd"||' scxml.xsd > scxml-alone.xsd
$ sed 's/vane:implements="req-5 req-8"/vane:implements="req-5 8"/' flag.scxml > broken.scxml
$ sxml validate --schema=scxml-alone.xsd broken.scxml; echo "exit status $?"
exit status 0
$ sxml validate --schema=scxml-alone.xsd --schema=vane-data.xsd broken.scxml
broken.scxml:80:22: cvc-datatype-valid.1.2.1: attribute 'vane:implements' has the value 'req-5 8', which is not valid for an anonymous type derived from 'xs:anySimpleType'
Every --schema adds documents to one schema, which is how you combine
vocabularies that don't import each other.
Schema hints in documents
Documents often name their schema with xsi:schemaLocation (pairs of a
namespace and a location) or xsi:noNamespaceSchemaLocation (a location
for names in no namespace). Here is the broken machine with such a hint
on its document element:
$ sed 's|<scxml xmlns="http://www.w3.org/2005/07/scxml"|& xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.w3.org/2005/07/scxml scxml.xsd"|' broken.scxml > hinted.scxml
$ head -2 hinted.scxml
<?xml version="1.0" encoding="UTF-8"?>
<scxml xmlns="http://www.w3.org/2005/07/scxml" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.w3.org/2005/07/scxml scxml.xsd"
By default sxml validate doesn't follow these hints: a document
doesn't get to choose the schema it's checked against, so you name the
schema with --schema. Without one, there is nothing to validate
against:
$ sxml validate hinted.scxml
sxml validate: missing option '--schema' (or give --use-schema-location)
Usage: sxml validate [--schema=SCHEMA]... [--use-schema-location] [--mode=MODE] [--format=FORMAT] FILE...
Try 'sxml validate --help'.
--use-schema-location asks for the hints to be followed. The hinted
documents are local files, relative to the document that names them,
and they join the --schema documents, if you give any, in one schema
for that document:
$ sxml validate --use-schema-location hinted.scxml
hinted.scxml:80:22: cvc-datatype-valid.1.2.1: attribute 'vane:implements' has the value 'req-5 8', which is not valid for an anonymous type derived from 'xs:anySimpleType'
$ sxml validate --use-schema-location --schema=scxml.xsd hinted.scxml
hinted.scxml:80:22: cvc-datatype-valid.1.2.1: attribute 'vane:implements' has the value 'req-5 8', which is not valid for an anonymous type derived from 'xs:anySimpleType'
The hinted copy is the broken machine, so both runs find its anchor;
the second takes SCXML's schema from --schema. A hint only adds a
namespace the schema doesn't have yet: with
--schema=scxml.xsd, the hint for SCXML's namespace is ignored, so a
document can't swap the schema you chose for one of its own. A hinted
file that doesn't exist isn't an error by itself (XSD 1.1 Part 1,
4.3.2); the elements it would have declared are then simply undeclared.
Namespaces in queries
In XQuery, import schema binds a prefix to the schema's namespace, or
makes it the default element namespace. Then element names and type
names work as in the schema. The imported vane-data.xsd comes along
with scxml.xsd, so its declarations are known; declare namespace
only binds the prefix:
$ sxml xquery --method=adaptive --context-item=flag.scxml --expression='
import schema namespace sc = "http://www.w3.org/2005/07/scxml" at "scxml.xsd";
declare namespace vane = "urn:example:vane";
let $machine := validate { / }
return (
count($machine//sc:state[. instance of element(*, sc:state)]),
$machine//sc:state ! `{@id}: {count(data(@vane:implements))}`
)'
5
"stable: 1"
"checking: 1"
"rolling-out: 1"
"paused: 2"
"archived: 0"
data(@vane:implements) is a sequence of names, because the
attribute's type is a list. Without a prefix, a name in a path means no
namespace, and finds nothing in this document:
$ sxml xquery --context-item=flag.scxml --expression='count(//state)'
0