Проверьте XML с помощью LXML, используя schemaLocation - PullRequest
0 голосов
/ 06 ноября 2019

Я пытаюсь проверить следующий XML с помощью lxml

<?xml version='1.0' encoding='UTF-8'?>
<mets:mets xmlns:mets="http://www.loc.gov/METS/"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:xlink="http://www.w3.org/1999/xlink"
  xmlns:csip="https://DILCIS.eu/XML/METS/CSIPExtensionMETS"
  xsi:schemaLocation="http://www.loc.gov/METS/ http://www.loc.gov/standards/mets/mets.xsd https://DILCIS.eu/XML/METS/CSIPExtensionMETS https://earkcsip.dilcis.eu/schema/DILCISExtensionMETS.xsd">
  <mets:metsHdr>
    <mets:agent ROLE="ARCHIVIST" TYPE="ORGANIZATION">
      <mets:name>foo</mets:name>
      <mets:note csip:NOTETYPE="this is incorrect">bar</mets:note>
    </mets:agent>
  </mets:metsHdr>
  <mets:structMap>
    <mets:div/>
  </mets:structMap>
</mets:mets>

Я взял скрипт из здесь (и добавил некоторые незначительные улучшения CLIи исправления Python 3):

import sys

from lxml import etree

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


SCHEMA_TEMPLATE = b"""<?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: path to xml.
    :type xml: str
    """
    tree = etree.parse(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)
    print('Success!')


if __name__ == '__main__':
   validate_XML(sys.argv[1])

Теперь я ожидаю, что проверка не сможет сказать, что NOTETYPE содержит недопустимое значение (допустимо только значение SOFTWARE VERSION), но проверка завершается без каких-либо ошибок.

Использование одного и того же файла в таких инструментах, как Oxygen XML Editor, приводит к ожидаемой ошибке:

Значение «это неверно» недопустимо в отношении перечисления »[ПРОГРАММНАЯ ВЕРСИЯ]». Это должно быть значение из перечисления.

Сгенерированная схема:

<xs:schema xmlns="http://dummy.libxml2.validator" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" targetNamespace="http://dummy.libxml2.validator" version="1.0" elementFormDefault="qualified" attributeFormDefault="unqualified">
    <xs:import namespace="http://www.loc.gov/METS/" schemaLocation="http://www.loc.gov/standards/mets/mets.xsd"/>
    <xs:import namespace="https://DILCIS.eu/XML/METS/CSIPExtensionMETS" schemaLocation="https://earkcsip.dilcis.eu/schema/DILCISExtensionMETS.xsd"/>
</xs:schema>
...