Автоматическая проверка XSD - PullRequest
9 голосов
/ 23 марта 2012

Согласно документации lxml «DTD извлекается автоматически на основе DOCTYPE анализируемого документа. Все, что вам нужно сделать, - это использовать анализатор, у которого включена проверка DTD.»

http://lxml.de/validation.html#validation-at-parse-time

Однако, если вы хотите проверить соответствие XML-схеме, вам нужно явно сослаться на нее.

Мне интересно, почему это так, и я хотел бы знать, есть ли библиотека или функция, которая может сделать это. Или даже объяснение того, как это сделать самому. Проблема в том, что существует множество способов сослаться на XSD, и мне нужно поддерживать их все.

Проверка не является проблемой. Вопрос в том, как определить схемы для проверки. В идеале это также относится к встроенным схемам.

Обновление:

Вот пример.

simpletest.xsd:

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified">
  <xs:element name="name" type="xs:string"/>
</xs:schema>

simpletest.xml:

<?xml version="1.0" encoding="UTF-8" ?>
<name xmlns="http://www.example.org"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://www.example.org simpletest.xsd">foo</name>

Я бы хотел сделать что-то вроде следующего:

>>> parser = etree.XMLParser(xsd_validation=True)
>>> tree = etree.parse("simpletest.xml", parser)

Ответы [ 2 ]

2 голосов
/ 27 марта 2012

У меня есть проект, который имеет более 100 различных схем и деревьев XML. Чтобы управлять всеми ими и проверять их, я сделал несколько вещей.

1) Я создал файл (т.е. xmlTrees.py), в котором я создал словарь каждого xml и соответствующей схемы, связанной с ним, и путь xml. Это позволило мне иметь одно место для получения xml и схемы, используемой для проверки этого xml.

MY_XML = {'url':'/pathToTree/myTree.xml', 'schema':'myXSD.xsd'}

2) В проекте у нас одинаково много пространств имен (очень сложно управлять). Поэтому я снова создал один файл, который содержал все пространства имен в формате lxml. Тогда в моих тестах и ​​сценариях я просто всегда передавал расширенный набор пространств имен.

ALL_NAMESPACES = {
    'namespace1':  'http://www.example.org',
    'namespace2':  'http://www.example2.org'
}

3) Для базовой / общей проверки я в итоге создал базовую функцию, которую я мог бы вызвать:

    def validateXML(content, schemaContent):

    try:
        xmlSchema_doc = etree.parse(schemaContent);
        xmlSchema = etree.XMLSchema(xmlSchema_doc);
        xml = etree.parse(StringIO(content));
    except:
        logging.critical("Could not parse schema or content to validate xml");
        response['valid'] = False;
        response['errorlog'] = "Could not parse schema or content to validate xml";

    response = {}
    # Validate the content against the schema.
    try:
        xmlSchema.assertValid(xml)
        response['valid'] = True
        response['errorlog'] = None
    except etree.DocumentInvalid, info:
        response['valid'] = False
        response['errorlog'] = xmlSchema.error_log

    return response

В основном любая функция, которая хочет использовать это, должна отправлять содержимое xml и содержимое xsd в виде строк. Это дало мне большую гибкость. Затем я просто поместил эту функцию в файл, где у меня были все мои вспомогательные функции xml.

1 голос
/ 28 марта 2013

Вы можете извлечь схемы самостоятельно и импортировать их в корневую схему:

from lxml import etree

XSI = "http://www.w3.org/2001/XMLSchema-instance"
XS = '{http://www.w3.org/2001/XMLSchema}'


SCHEMA_TEMPLATE = """<?xml version = "1.0" encoding = "UTF-8"?>
<xs:schema xmlns="http://dummy.libxml2.validator"
targetNamespace="http://dummy.libxml2.validator"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
version="1.0"
elementFormDefault="qualified"
attributeFormDefault="unqualified">
</xs:schema>"""


def validate_XML(xml):
    """Validate an XML file represented as string. Follow all schemaLocations.

    :param xml: XML represented as string.
    :type xml: str
    """
    tree = etree.XML(xml)
    schema_tree = etree.XML(SCHEMA_TEMPLATE)
    # Find all unique instances of 'xsi:schemaLocation="<namespace> <path-to-schema.xsd> ..."'
    schema_locations = set(tree.xpath("//*/@xsi:schemaLocation", namespaces={'xsi': XSI}))
    for schema_location in schema_locations:
        # Split namespaces and schema locations ; use strip to remove leading
        # and trailing whitespace.
        namespaces_locations = schema_location.strip().split()
        # Import all found namspace/schema location pairs
        for namespace, location in zip(*[iter(namespaces_locations)] * 2):
            xs_import = etree.Element(XS + "import")
            xs_import.attrib['namespace'] = namespace
            xs_import.attrib['schemaLocation'] = location
            schema_tree.append(xs_import)
    # Contstruct the schema
    schema = etree.XMLSchema(schema_tree)
    # Validate!
    schema.assertValid(tree)

Кстати, в вашем simpletest.xsd отсутствует targetNamespace.

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://www.example.org" elementFormDefault="qualified">
    <xs:element name="name" type="xs:string"/>
</xs:schema>

С кодом выше, ваш пример документа проверяется по этой схеме.

...