XSD 1.1 xs:override and instance-document rollback: can a pipeline validate against two schema generations safely?
0 reputation · 17 Dec 2020, 19:56 UTC
0 reputation · 17 Dec 2020, 19:56 UTC
We run a validation pipeline where a base schema (base.xsd) and a derived XSD 1.1 schema (derived.xsd) coexist. The derived schema uses xs:override to tighten a complexType with an xs:assert, and it also adds a new member to an existing substitution group. Instance documents are produced against derived.xsd, but on a failed deployment we need to re-validate stored instances against base.xsd as a rollback path.
The uncertainty is at the boundary between the two schema generations: instances that exercise the new assertion or the new substitution group member appear to have no defined downgrade behavior in the XSD 1.1 specification, and I cannot find any normative compatibility metadata that a processor (Xerces-J or Saxon-EE) could consult to decide whether a given instance is safe to re-validate against the older schema.
Constraints: we cannot change the targetNamespace (that would force a full migration of all stored documents), and we want to avoid xs:redefine since it is deprecated in XSD 1.1. Wildcards with processContents="lax" exist in some types but do not cover the overridden type.
Specifically:
31275 reputation · 18 Dec 2020, 01:43 UTC
No. XSD 1.1 defines no mechanism — metadata, annotation hook, or processor API — by which a validator can detect that an instance depends on an overridden construct before you attempt rollback validation. Validation always runs against a single assembled schema set; there is no version negotiation in the specification, and neither Xerces-J nor Saxon-EE exposes a "which overridden components were exercised" query. Safe rollback therefore has to be engineered out-of-band: either by constraining how you use xs:override, or by an explicit downgrade transform. In practice you need both.
Separate the two changes, because they behave very differently on rollback.
An xs:assert only ever removes valid instances; it never adds any. So an instance that passed the derived schema's assertion also satisfies the base schema's looser type. Rollback validation of those instances succeeds. The danger case is the opposite direction: if the override also relaxes the content model — say minOccurs="1" becomes minOccurs="0", or a narrower type is replaced by a wider one — then derived-valid instances can fail base validation without touching anything else. xs:override permits this; nothing stops you. Audit the override for any relaxation, not just the assertion.
This is unsafe only for instances that actually use the new member. The head element's declaration and content model are unchanged in the base schema; instances that still use the head (or pre-existing members) validate fine against base.xsd. Only documents containing the new member element fail, with an undeclared-element error. So the failure mode is instance-dependent, not schema-global — which is precisely why you cannot decide safety by inspecting the schemas alone.
Watch for instances that explicitly carry xsi:type naming a type that only exists (or only exists with that definition) in the derived schema. Base validation fails on the unknown type name even when the element content itself would be fine. This is the most common surprise in rollback testing.
Your processContents="lax" wildcards do not rescue you: lax applies only to elements with no declaration in the validating schema. The overridden type is explicitly declared in base.xsd, so its content is validated strictly against the base definition.
xsi:type references, then validate against base.xsd. Schema-design discipline (never relax in an override, keep new members optional in practice) shrinks the transform's job but cannot eliminate it if you want a zero-manual-intervention guarantee.Build a minimal reproduction: base.xsd with the type and substitution head; derived.xsd with the override plus new member. Generate three instances — (a) head element only, (b) new member, (c) head with xsi:type pointing at the overridden type — and validate each against both schemas. Expect (a) to pass both, (b) and (c) to fail base. Then apply your downgrade XSLT and confirm (b) and (c) pass base afterward. That matrix, run in CI on every schema change, is your rollback safety net — the schema itself will never give you one.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.