# XML and XSD validation “Is this XML valid?” needs a qualification: valid against which rules, using which schema and language version? XML parsing, DTD validation, XSD assessment, and Python object binding answer different questions. This guide explains those distinctions and what a test-suite validity result tells you. ## Well-formed XML is the starting point An XML parser checks the document's syntax: legal characters, properly nested tags, quoted attribute values, entity-reference rules, and a single document element. These are **well-formedness** requirements, defined by [XML 1.0 §2.1][xml-well-formed]. A schema cannot make malformed XML well-formed. For example, `many` is well-formed XML. It is invalid against an XSD declaration requiring `quantity` to contain an `xs:integer`. `3` is not well-formed, so instance assessment cannot proceed normally at all. **DTD validation** checks another set of rules. A document type definition (DTD) declares element content models, attributes, entities, and notations. The XML specification uses “valid” for a document that has a document type declaration and satisfies the associated DTD's constraints ([XML 1.0 §2.8][xml-dtd]). Merely accepting a `DOCTYPE` does not mean a parser has performed DTD validation; XML defines both [validating and non-validating processors][xml-processors]. **XSD assessment** checks an XML information set against XML Schema Definition Language rules. XSD supplies namespace-aware declarations, types, datatype facets, content models, and identity constraints. A document can be XSD-valid without having a DTD. If it has both a DTD and an XSD, satisfying one does not imply satisfying the other. pyxsd is an XSD processor, not a DTD validator. ## DTDs can affect the information XSD sees XSD assessment works on information supplied by XML parsing, rather than on the original character sequence alone. The [XML Information Set][infoset] models that information as items and properties: elements, attributes, character data, namespace names, and declaration information. It is an abstract model, not a requirement to use a particular tree API. Processing DTD declarations can affect this input even without DTD validation: - An internal parsed entity's replacement text can contribute character data or markup. - An `ATTLIST` declaration can supply an omitted attribute's default value. - A declared attribute type can change attribute-value normalization before XSD checks the resulting value. These effects follow XML's rules for [attribute defaults][xml-defaults], [attribute-value normalization][xml-normalization], and [entity processing][xml-entities]. They are distinct from XSD defaults and datatype whitespace processing, which apply during schema assessment. There is also a direct DTD dependency in the XSD datatypes [`xs:ENTITY`][xsd11-entity] and [`xs:ENTITIES`][xsd11-entities]. An `ENTITY` value must name an **unparsed entity declared in the instance document's DTD**; `ENTITIES` is a nonempty list of such values. Checking only that each token is an `NCName` checks its lexical form, not this declaration-dependent requirement. An unparsed entity, declared with `NDATA`, is not a parsed entity reference to expand into XML text. Naming it does not require fetching its resource. pyxsd's default ElementTree/Expat backend expands internal parsed entities and can apply `ATTLIST` defaults. When the compiled schema can validate `ENTITY`/`ENTITIES`, pyxsd also lazily collects unparsed-entity declarations from the prolog/internal subset. Complete evidence lets it reject undeclared names, including values supplied by XSD defaults or checked through derived types. An absent DOCTYPE supplies an empty, complete declaration set. Internal parameter entities are expanded; external parameter entities are skipped without retrieval. Unrelated schemas skip this declaration-collection parse. No built-in profile retrieves external DTD subsets or entities. An external subset or skipped external parameter entity makes declaration evidence incomplete, so a missing name remains lexical-only with one advisory per document when a check needs the missing evidence. Caller-supplied trees without pyxsd source provenance and custom-policy parser results lack core declaration evidence. Non-seekable instance streams passed to top-level `pyxsd.parse()` also remain lexical-only, even with an explicit schema; compiled `Schema.parse()` can collect and replay their prolog. `Document.revalidate()` serializes the tree without its DOCTYPE, so it carries the original parse's declaration evidence into the reparse instead of recollecting: a declared value stays valid, complete evidence still rejects an undeclared replacement, and evidence the original parse lacked stays lexical-only. These are implementation limits, not alternative validity rules or security protections. See {doc}`validation` for source-provenance and reread limits. Parser policy is a separate decision from validity. The protected profile rejects entity declarations and external identifiers; the strict profile rejects every `DOCTYPE`. Those refusals are not DTD-validation modes or evidence that the refused document violates its XSD. Internal subsets remain untrusted input when permitted. See {doc}`xml-security` for the exact policies, limits, and Expat requirements; XSD validation is not a security boundary. ## Schema compilation and instance assessment An XSD schema document is itself XML, but well-formed XML is not necessarily a legal schema. XSD defines constraints both on the XML representation of schema components and on the components themselves. A schema can also draw components from several documents through inclusion and import. The Structures specifications define the [component model][xsd11-structures] and [schema composition][xsd11-composition]. It helps to separate two stages: 1. **Schema compilation:** read the schema documents, resolve and compose declarations and types, and check schema legality. A type-derivation cycle or an illegal content model is a schema problem, even before any instance is supplied. Checking a schema document against the schema for schema documents alone does not establish all component constraints. 2. **Instance assessment:** use the resulting schema components to assess elements and attributes. Missing required children, invalid datatype values, and broken key references are instance problems. A legal schema does not imply that any particular instance satisfies it. In pyxsd, `Schema.compile()` also constructs Python binding classes and records recoverable schema findings in `Schema.report`. `Schema.parse()` assesses and binds an instance; its `Document.report` merges schema findings with instance findings. Compilation returning a `Schema`, or parsing returning a `Document`, does not by itself establish validity. Check the reports or use `require_valid()`. Unreadable or malformed input can instead raise `PyXSDError` when processing cannot proceed. The reporting contract and its known limitations are documented in {doc}`validation`. In particular, loading a component and authorizing a reference to its namespace are separate concerns. pyxsd audits XSD-language component QNames against each referencing document's direct authored imports, effective target namespace, and built-in vocabularies. A component supplied transitively, by a caller mapping, or by an instance hint does not excuse a missing authored import (`import-required`). Wildcard `notQName` is intentionally not audited; annotation payloads, foreign markup, QName-valued defaults/fixed values, and XPath expressions are also excluded. A reference that resolves to no namespace is allowed regardless of the document's authored imports, matching pyxsd's lenient bare-name lookup, so no-namespace enforcement is incomplete. This audit leaves global component lookup in place; it does not establish complete composition conformance. Binding determines which Python objects and values an application receives. An invalid document can still yield a best-effort bound tree and a report containing errors. Strict versus lax **binding** choices control retention of invalid or unrecognized content, not permission to ignore its findings. Namespace handling is a separate policy field; the XSTS harness uses `ParseModes.NAMESPACED`. See {doc}`binding`. These binding choices are not the XSD wildcard `processContents="strict"`, `"lax"`, and `"skip"` rules, which control assessment of wildcard-matched content. ## XML versions and XSD versions are independent Two different languages have versions called “1.0” and “1.1”: | Version choice | What it governs | Where it is selected | | --- | --- | --- | | XML 1.0 or XML 1.1 | XML syntax, character rules, and parsing behavior | The document's XML declaration, such as ``, and the XML parser's capabilities | | XSD 1.0 or XSD 1.1 | Schema vocabulary, component constraints, datatypes, and assessment rules | The schema processor's version mode | XML 1.1 changes rules such as permitted control-character references and end-of-line normalization; see [XML 1.1 §1.3][xml11-changes]. XML 1.0's fifth edition also broadened name characters, so “newer name characters” alone is not a reliable way to identify an XML 1.1 document. An XSD 1.1 schema can be written as an XML 1.0 document and assess XML 1.0 instances. Changing the XML declaration to `version="1.1"` does not enable XSD 1.1 assertions. Conversely, selecting XSD 1.1 does not give the underlying XML parser XML 1.1 support. XSD 1.1 explicitly addresses its dependencies on XML and Namespaces versions; see [Structures, changes to related specifications][xsd11-xml-dependencies]. pyxsd defaults to XSD 1.1 and offers `xsd_version="1.0"` on `Schema.compile()` and `--xsd-version 1.0` on the CLI. Its ElementTree/Expat XML backend does not implement XML 1.1 parsing semantics. That lexer limitation is independent of pyxsd's XSD version mode. Neither an `xs:` prefix nor the shared `http://www.w3.org/2001/XMLSchema` namespace distinguishes XSD 1.0 from 1.1. See {doc}`supported` for version-mode behavior and remaining differences. ### What changes between XSD 1.0 and 1.1? XSD 1.1 adds vocabulary and changes some rules for existing constructs. The following examples explain why the same schema or instance can have different expected verdicts under the two versions; they are not a pyxsd support matrix. - **Relationships between values:** XSD 1.1 introduces complex-type [`xs:assert`][xsd11-assertions] and simple-type [`xs:assertion` facets][xsd11-assertion-facet], using XPath expressions to express constraints beyond individual datatype bounds. Conditional type assignment with [`xs:alternative`][xsd11-alternatives] selects a governing type according to conditions, rather than requiring the instance author to supply `xsi:type`. - **Extensible content models:** XSD 1.1 adds open content and richer wildcard exclusions, and relaxes restrictions on `xs:all` members. It also changes Unique Particle Attribution: competition between an element particle and a wildcard particle is allowed, with the element taking precedence. Competition between two element particles or two wildcard particles remains constrained. See [Structures, content-model changes][xsd11-model-changes]. - **Schema evolution and composition:** XSD 1.1 adds conditional inclusion through `vc:*` attributes and an `xs:override` composition mechanism. Conditional inclusion chooses which declarations to process based on version or capabilities; it is not the same as instance-time conditional type assignment. See [Structures §4.2][xsd11-composition]. - **Datatypes and value semantics:** XSD 1.1 adds types such as `xs:dateTimeStamp`, which requires an explicit timezone, and the `xs:yearMonthDuration` and `xs:dayTimeDuration` duration subtypes. It also changes existing lexical spaces: `+INF` is added for `float` and `double`, and year `0000` becomes permitted in the affected date/time types. See [Datatypes, changes since 1.0][xsd11-datatype-changes]. Compatibility was a design goal, not a guarantee of identical verdicts for every edge case. Version-specific expected results can therefore be intentional. An implementation's failure to match its selected version is still a failure, not something corrected by taking the better result from a different version. ## Reports, PSVI, and a Boolean verdict XSD's **post-schema-validation infoset** (PSVI) is the input infoset augmented with assessment information, such as governing declarations and types, typed values, and validity properties. Assessment is broader than a single yes/no answer. In particular, the specification's `[validity]` property can be `valid`, `invalid`, or `notKnown`; not every information item is necessarily strictly assessed. See [XSD 1.0 Structures §2.1][xsd10-psvi] and [XSD 1.1 Structures §2.1][xsd11-psvi]. A pyxsd `ValidationReport` is an application-facing collection of findings with severities, codes, phases, and optional location or constraint evidence. Its `has_errors` property answers whether the run recorded error-severity findings. It is not a complete PSVI, and the bound Python tree is not a promise to expose every PSVI property or preserve all source information. A report with no errors is evidence from the checks performed under that run's settings and known implementation limits, not proof that every possible XSD constraint was checked. The XSTS adapter reduces the relevant phase's findings to a top-level validity verdict for comparison with the test metadata. That comparison does not check whether binding retained a particular invalid value, whether every descendant has the correct PSVI properties, or whether a diagnostic identifies the right source occurrence. Those require separate tests. ## What XSTS establishes The W3C XML Schema Test Suite (XSTS) contains catalogued schema and instance cases with expected outcomes and version information. pyxsd pins the upstream corpus revision. Its [README, “Running the tests”][xsts-readme] explicitly limits the suite to top-level `valid`/`invalid` outcomes, not correct PSVI generation or conformity of error reporting. For cases with a checkable Boolean expectation, an agreement result means the adapter's verdict matches that expectation for the selected profile. Schema cases test schema acceptance or rejection; instance cases test assessment using the supplied schema context. Non-Boolean expectations are not forced into yes/no answers. A timeout, adapter limitation, or unexpected exception is not itself a schema-validity verdict. Agreement is useful regression and interoperability evidence, but it does not establish: - W3C certification or complete coverage of XSD features and their interactions; - correct or complete PSVI output, diagnostic quality, or API completeness; - performance, memory use, security, or suitability for untrusted input; - correctness on untested documents, or a universal ranking of schema engines. The selected corpus, version profile, adapter behavior, and excluded or uncheckable cases define the measurement's scope. Disagreements need analysis against the specifications and test metadata; another implementation's answer is useful evidence, not normative authority. Continue with {doc}`xsts` for case selection, outcome definitions, baseline provenance, current results, and residual failures. For engine-selection tradeoffs, see {doc}`comparison-xmlschema` and {doc}`comparison-libxml2`. ## Primary specifications The language definitions are the W3C Recommendations: - [XML 1.0, fifth edition][xml-well-formed] and [XML 1.1, second edition][xml11-changes]. - [XML Information Set, second edition][infoset]. - XSD 1.0, second edition: [Structures][xsd10-structures] and [Datatypes][xsd10-datatypes]. - XSD 1.1: [Structures][xsd11-structures] and [Datatypes][xsd11-datatypes]. The XSD 1.1 change summaries linked above are non-normative reading aids within the specifications; the component and validation rules define the requirements. The pinned [XSTS README][xsts-readme] defines the upstream suite's stated scope. For pyxsd's public contracts, continue with {doc}`validation`, {doc}`binding`, and {doc}`xml-security`. [xml-well-formed]: https://www.w3.org/TR/xml/#sec-well-formed [xml-dtd]: https://www.w3.org/TR/xml/#sec-prolog-dtd [xml-processors]: https://www.w3.org/TR/xml/#proc-types [xml-defaults]: https://www.w3.org/TR/xml/#sec-attr-defaults [xml-normalization]: https://www.w3.org/TR/xml/#AVNormalize [xml-entities]: https://www.w3.org/TR/xml/#entproc [xml11-changes]: https://www.w3.org/TR/xml11/#sec-xml11 [infoset]: https://www.w3.org/TR/xml-infoset/ [xsd10-structures]: https://www.w3.org/TR/xmlschema-1/ [xsd10-datatypes]: https://www.w3.org/TR/xmlschema-2/ [xsd10-psvi]: https://www.w3.org/TR/xmlschema-1/#key-psvi [xsd11-structures]: https://www.w3.org/TR/xmlschema11-1/ [xsd11-datatypes]: https://www.w3.org/TR/xmlschema11-2/ [xsd11-entity]: https://www.w3.org/TR/xmlschema11-2/#ENTITY [xsd11-entities]: https://www.w3.org/TR/xmlschema11-2/#ENTITIES [xsd11-composition]: https://www.w3.org/TR/xmlschema11-1/#composition [xsd11-xml-dependencies]: https://www.w3.org/TR/xmlschema11-1/#ch_specs [xsd11-assertions]: https://www.w3.org/TR/xmlschema11-1/#cAssertions [xsd11-assertion-facet]: https://www.w3.org/TR/xmlschema11-2/#rf-assertions [xsd11-alternatives]: https://www.w3.org/TR/xmlschema11-1/#cTypeAlternative [xsd11-model-changes]: https://www.w3.org/TR/xmlschema11-1/#ch_models [xsd11-datatype-changes]: https://www.w3.org/TR/xmlschema11-2/#changes [xsd11-psvi]: https://www.w3.org/TR/xmlschema11-1/#key-psvi [xsts-readme]: https://github.com/w3c/xsdtests/blob/7bc3365c652a322f3d762021b3879eb92dae7e30/README.md#running-the-tests