135

I have an XML file and an XML schema in another file and I'd like to validate that my XML file adheres to the schema. How do I do this in Python?

I'd prefer something using the standard library, but I can install a third-party package if necessary.

jonrsharpe
  • 115,751
  • 26
  • 228
  • 437
Eli Courtwright
  • 186,300
  • 67
  • 213
  • 256

8 Answers8

71

I am assuming you mean using XSD files. Surprisingly there aren't many python XML libraries that support this. lxml does however. Check Validation with lxml. The page also lists how to use lxml to validate with other schema types.

Community
  • 1
  • 1
40

An example of a simple validator in Python3 using the popular library lxml

Installation lxml

pip install lxml

If you get an error like "Could not find function xmlCheckVersion in library libxml2. Is libxml2 installed?", try to do this first:

# Debian/Ubuntu
apt-get install python-dev python3-dev libxml2-dev libxslt-dev

# Fedora 23+
dnf install python-devel python3-devel libxml2-devel libxslt-devel

The simplest validator

Let's create simplest validator.py

from lxml import etree

def validate(xml_path: str, xsd_path: str) -> bool:

    xmlschema_doc = etree.parse(xsd_path)
    xmlschema = etree.XMLSchema(xmlschema_doc)

    xml_doc = etree.parse(xml_path)
    result = xmlschema.validate(xml_doc)

    return result

then write and run main.py

from validator import validate

if validate("path/to/file.xml", "path/to/scheme.xsd"):
    print('Valid! :)')
else:
    print('Not valid! :(')

A little bit of OOP

In order to validate more than one file, there is no need to create an XMLSchema object every time, therefore:

validator.py

from lxml import etree

class Validator:

    def __init__(self, xsd_path: str):
        xmlschema_doc = etree.parse(xsd_path)
        self.xmlschema = etree.XMLSchema(xmlschema_doc)

    def validate(self, xml_path: str) -> bool:
        xml_doc = etree.parse(xml_path)
        result = self.xmlschema.validate(xml_doc)

        return result

Now we can validate all files in the directory as follows:

main.py

import os
from validator import Validator

validator = Validator("path/to/scheme.xsd")

# The directory with XML files
XML_DIR = "path/to/directory"

for file_name in os.listdir(XML_DIR):
    print('{}: '.format(file_name), end='')

    file_path = '{}/{}'.format(XML_DIR, file_name)

    if validator.validate(file_path):
        print('Valid! :)')
    else:
        print('Not valid! :(')

For more options read here: Validation with lxml

SergO
  • 2,703
  • 1
  • 30
  • 23
  • Use the `xmlschema` Python package instead, makes things easier, see the answer https://stackoverflow.com/a/52310735/11154841. – questionto42 Mar 25 '22 at 21:25
34

You can easily validate an XML file or tree against an XML Schema (XSD) with the xmlschema Python package. It's pure Python, available on PyPi and doesn't have many dependencies.

Example - validate a file:

import xmlschema
xmlschema.validate('doc.xml', 'some.xsd')

The method raises an exception if the file doesn't validate against the XSD. That exception then contains some violation details.

If you want to validate many files you only have to load the XSD once:

xsd = xmlschema.XMLSchema('some.xsd')
for filename in filenames:
    xsd.validate(filename)

If you don't need the exception you can validate like this:

if xsd.is_valid('doc.xml'):
    print('do something useful')

Alternatively, xmlschema directly works on file objects and in memory XML trees (either created with xml.etree.ElementTree or lxml). Example:

import xml.etree.ElementTree as ET
t = ET.parse('doc.xml')
result = xsd.is_valid(t)
print('Document is valid? {}'.format(result))
maxschlepzig
  • 35,645
  • 14
  • 145
  • 182
  • 2
    This should be the accepted answer. – questionto42 Mar 25 '22 at 21:26
  • Thanks @maxschlepzig I tried the below and worked like a charm. ' import xmlschema try: xsdPath = "/dbfs" + "/mnt/sample/mySchemaChecker.xsd" xmlPath = "/dbfs" + "/mnt/sample/myXMLData.xml" xsd_schema = xmlschema.XMLSchema(xsdPath) print(xsd_schema.is_valid(xmlPath)) except Exception as e: print(str(e)) raise Exception(e) ' – Sathya Ram Mar 30 '23 at 08:01
29

As for "pure python" solutions: the package index lists:

  • pyxsd, the description says it uses xml.etree.cElementTree, which is not "pure python" (but included in stdlib), but source code indicates that it falls back to xml.etree.ElementTree, so this would count as pure python. Haven't used it, but according to the docs, it does do schema validation.
  • minixsv: 'a lightweight XML schema validator written in "pure" Python'. However, the description says "currently a subset of the XML schema standard is supported", so this may not be enough.
  • XSV, which I think is used for the W3C's online xsd validator (it still seems to use the old pyxml package, which I think is no longer maintained)
Steven
  • 28,002
  • 5
  • 61
  • 51
  • 5
    I would take a look at PyXB over these. Looks like most of these state they are incomplete, and they seem somewhat "dead." pyxsd last updated in 2006, minixsv last updated in 2008, XSV in 2007 as far as I can tell. Not always the best reason to consider one package over another, but I think it is justified in this case. – oob Dec 31 '11 at 02:40
  • 2
    +1 for PyXB. I am using it in Django for validating raw XML inserted in the Admin section. Simple and easy to use. – tatlar Apr 03 '13 at 23:21
  • Use the `xmlschema` Python package, see the answer https://stackoverflow.com/a/52310735/11154841. – questionto42 Mar 25 '22 at 21:26
14

There are two ways(actually there are more) that you could do this.
1. using lxml
pip install lxml

from lxml import etree, objectify
from lxml.etree import XMLSyntaxError

def xml_validator(some_xml_string, xsd_file='/path/to/my_schema_file.xsd'):
    try:
        schema = etree.XMLSchema(file=xsd_file)
        parser = objectify.makeparser(schema=schema)
        objectify.fromstring(some_xml_string, parser)
        print "YEAH!, my xml file has validated"
    except XMLSyntaxError:
        #handle exception here
        print "Oh NO!, my xml file does not validate"
        pass

xml_file = open('my_xml_file.xml', 'r')
xml_string = xml_file.read()
xml_file.close()

xml_validator(xml_string, '/path/to/my_schema_file.xsd')
  1. Use xmllint from the commandline. xmllint comes installed in many linux distributions.

>> xmllint --format --pretty 1 --load-trace --debug --schema /path/to/my_schema_file.xsd /path/to/my_xml_file.xml

Komu
  • 14,174
  • 2
  • 28
  • 22
  • I have 3 xsd files, only when all 3 xsd are present I can validate a xml...can this be done with your method? – Naveen Jan 24 '19 at 17:31
13

The PyXB package at http://pyxb.sourceforge.net/ generates validating bindings for Python from XML schema documents. It handles almost every schema construct and supports multiple namespaces.

pabigot
  • 989
  • 7
  • 8
7

lxml provides etree.DTD

from the tests on http://lxml.de/api/lxml.tests.test_dtd-pysrc.html

...
root = etree.XML(_bytes("<b/>")) 
dtd = etree.DTD(BytesIO("<!ELEMENT b EMPTY>")) 
self.assert_(dtd.validate(root)) 
alecxe
  • 462,703
  • 120
  • 1,088
  • 1,195
altunyurt
  • 2,821
  • 3
  • 38
  • 53
1
import xmlschema


def get_validation_errors(xml_file, xsd_file):
    schema = xmlschema.XMLSchema(xsd_file)
    validation_error_iterator = schema.iter_errors(xml_file)
    errors = list()
    for idx, validation_error in enumerate(validation_error_iterator, start=1):
        err = validation_error.__str__()
        errors.append(err)
        print(err)
    return errors

errors = get_validation_errors('sample3.xml', 'sample_schema.xsd')
Vijay Anand Pandian
  • 1,027
  • 11
  • 23