Schematron validation with lxml in Python: how to get validation errors?
I am trying to perform a Schematron validation using lxml. For the particular application I'm working on, it's important that any tests that fail validation are returned. The lxml documentation mentions having a property object validation_report
. I think this should contain the information I'm looking for, but I just can't figure out how to work with it. Here is some sample code that demonstrates my problem (adapted from http://lxml.de/validation.html#id2 , tested with Python 2.7.4):
import StringIO
from lxml import isoschematron
from lxml import etree
def main():
# Schema
f = StringIO.StringIO('''\
<schema xmlns="http://purl.oclc.org/dsdl/schematron" >
<pattern id="sum_equals_100_percent">
<title>Sum equals 100%.</title>
<rule context="Total">
<assert test="sum(//Percent)=100">Sum is not 100%.</assert>
</rule>
</pattern>
</schema>
''')
# Parse schema
sct_doc = etree.parse(f)
schematron = isoschematron.Schematron(sct_doc, store_report = True)
# XML to validate - validation will fail because sum of numbers
# not equal to 100
notValid = StringIO.StringIO('''\
<Total>
<Percent>30</Percent>
<Percent>30</Percent>
<Percent>50</Percent>
</Total>
''')
# Parse xml
doc = etree.parse(notValid)
# Validate against schema
validationResult = schematron.validate(doc)
# Validation report (assuming here this is where reason
# for validation failure is stored, but perhaps I'm wrong?)
report = isoschematron.Schematron.validation_report
print("is valid: " + str(validationResult))
print(dir(report.__doc__))
main()
Now, from the value validationResult
, I can see that the validation failed (as expected), so the following I would like to know why. The result of the second print statement gives me:
['__add__', '__class__', '__contains__', '__delattr__', '__doc__', '__eq__', '__ format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__get slice__', '__gt__', '__hash__', '__init__', '__le__', '__len__', '__lt__', '__mo d__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmod__', '__rmul__', '__setattr__', '__sizeof__', '__str__', '__subclasshook __', '_formatter_field_name_split', '_formatter_parser', 'capitalize', 'center', 'count', 'decode', 'encode', 'endswith', 'expandtabs', 'find', 'format', 'index ', 'isalnum', 'isalpha', 'isdigit', 'islower', 'isspace', 'istitle', 'isupper', 'join', 'ljust', 'lower', 'lstrip', 'partition', 'replace', 'rfind', 'rindex', ' rjust', 'rpartition', 'rsplit', 'rstrip', 'split', 'splitlines', 'startswith', ' strip', 'swapcase', 'title', 'translate', 'upper', 'zfill']
As far as I understand, based on the documentation and this related question . Maybe something really obvious that I don't notice?
source to share
Ok, so someone on Twitter gave me a suggestion that made me realize that I got a link to the schematron class by mistake, everything is wrong. Since there don't seem to be clear examples, I'll share my working solution below:
import StringIO
from lxml import isoschematron
from lxml import etree
def main():
# Example adapted from http://lxml.de/validation.html#id2
# Schema
f = StringIO.StringIO('''\
<schema xmlns="http://purl.oclc.org/dsdl/schematron" >
<pattern id="sum_equals_100_percent">
<title>Sum equals 100%.</title>
<rule context="Total">
<assert test="sum(//Percent)=100">Sum is not 100%.</assert>
</rule>
</pattern>
</schema>
''')
# Parse schema
sct_doc = etree.parse(f)
schematron = isoschematron.Schematron(sct_doc, store_report = True)
# XML to validate - validation will fail because sum of numbers
# not equal to 100
notValid = StringIO.StringIO('''\
<Total>
<Percent>30</Percent>
<Percent>30</Percent>
<Percent>50</Percent>
</Total>
''')
# Parse xml
doc = etree.parse(notValid)
# Validate against schema
validationResult = schematron.validate(doc)
# Validation report
report = schematron.validation_report
print("is valid: " + str(validationResult))
print(type(report))
print(report)
main()
The following data is now displayed in the report print application:
<?xml version="1.0" standalone="yes"?>
<svrl:schematron-output xmlns:svrl="http://purl.oclc.org/dsdl/svrl" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:schold="http://www.ascc.net/xml/schematron" xmlns:sch="http://www.ascc.net/xml/schematron" xmlns:iso="http://purl.oclc.org/dsdl/schematron" title="" schemaVersion="">
<!--
-->
<svrl:active-pattern id="sum_equals_100_percent" name="Sum equals 100%."/>
<svrl:fired-rule context="Total"/>
<svrl:failed-assert test="sum(//Percent)=100" location="/Total">
<svrl:text>Sum is not 100%.</svrl:text>
</svrl:failed-assert>
</svrl:schematron-output>
This is exactly what I was looking for!
source to share