################################
Validate against an XML schema
################################
.. currentmodule:: turbohtml.validate
Parse the XML with :func:`turbohtml.parse_xml`, compile the schema once, then validate. :class:`XMLSchema` takes XSD 1.0
text (or a parsed schema document) and :class:`RelaxNG` takes RELAX NG XML syntax; both expose the same
:meth:`~XMLSchema.validate`, :meth:`~XMLSchema.is_valid`, and :meth:`~XMLSchema.assert_valid` surface.
*****************
Get every error
*****************
:meth:`~XMLSchema.validate` returns a :class:`ValidationResult`. It is truthy when the document is valid, and its
``errors`` tuple holds one :class:`ValidationError` per violation -- each with a ``message``, the ``/root/child``
document ``path`` that located it, a ``line`` (``0`` when the source carries no positions), and a coarse ``type`` of
``"structure"``, ``"datatype"``, or ``"facet"``:
.. testcode::
from turbohtml import parse_xml
from turbohtml.validate import XMLSchema
schema = XMLSchema(
''
''
''
''
""
)
result = schema.validate(parse_xml("One"))
print(result.valid)
for error in result.errors:
print(error.type, error.path, "--", error.message)
.. testoutput::
False
structure /book -- required attribute 'isbn' is missing
***********
Fail fast
***********
When you only care whether the document conforms, :meth:`~XMLSchema.is_valid` returns the bool, and
:meth:`~XMLSchema.assert_valid` raises :class:`SchemaValidationError` (carrying the errors) on the first invalid
document, so it drops into a pipeline that expects an exception:
.. testcode::
from turbohtml import parse_xml
from turbohtml.validate import RelaxNG, SchemaValidationError
schema = RelaxNG(
''
''
)
print(schema.is_valid(parse_xml("hi")))
try:
schema.assert_valid(parse_xml(""))
except SchemaValidationError as error:
print("rejected:", error.errors[0].path)
.. testoutput::
True
rejected: /note