2012-03-23 5 views
7

In base alla documentazione lxml "La DTD viene recuperata automaticamente in base al DOCTYPE del documento analizzato. Tutto ciò che devi fare è utilizzare un parser con la convalida DTD abilitata."Convalida XSD automatica

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

Tuttavia, se si desidera convalidare contro uno schema XML, è necessario fare riferimento in modo esplicito uno.

Mi chiedo perché questo sia e vorrei sapere se esiste una libreria o una funzione in grado di farlo. O anche una spiegazione su come farlo accadere da solo. Il problema è che ci sono molti modi per fare riferimento a un XSD e devo supportarli tutti.

La convalida non è il problema. Il problema è come determinare gli schemi da validare. Idealmente questo gestirà anche schemi in linea.

Aggiornamento:

Ecco un esempio.

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> 

Vorrei fare qualcosa di simile al seguente:

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

Non possiamo dirvi come gestire i vostri formati. – Marcin

+0

Marcin, non capisco il tuo commento. Forse non capisco come funziona la convalida dello schema. – Jono

+0

Stai facendo questo su Windows? AFAIK Microsoft è l'unico a supportare schemi in linea. –

risposta

3

Ho un progetto che ha più 100 diversi schemi e xml alberi. Per gestirli tutti e convalidarli ho fatto alcune cose.

1) Ho creato un file (ad esempio xmlTrees.py) in cui ho creato un dizionario di ogni xml e lo schema corrispondente ad esso associato e il percorso xml. Questo mi ha permesso di avere un unico posto per ottenere sia xml & lo schema utilizzato per convalidare quel xml.

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

2) Nel progetto abbiamo altrettanto molti namespace (molto difficili da gestire). Quindi quello che ho fatto è stato di nuovo creato un singolo file che conteneva tutti gli spazi dei nomi nel formato di Mi piace. Poi nei miei test e script passerei sempre il superset di namespace.

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

3) Per la convalida di base/generico ho finito per creare una funzione di base ho potuto chiamare:

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 

pratica qualsiasi funzione che vuole utilizzare questo ha bisogno di inviare il contenuto XML e il contenuto XSD come stringhe. Ciò mi ha fornito la massima flessibilità. Ho quindi inserito questa funzione in un file in cui avevo tutte le mie funzioni di aiuto xml.

+0

Questo non risponde alla mia domanda perché stai definendo una mappatura esplicita di documenti XML per gli schemi. Il punto della mia domanda è come si possono dedurre i mapping. – Jono

+0

L'unico modo per inferire realmente la mappatura è creare sfortunatamente qualche tipo di mappatura. a meno che nella definizione degli schemi non sia possibile ottenere quell'URL e recuperare effettivamente il file xsd, o in ogni schema si aggiunga un commento della posizione dello schema, che sta ancora creando una mappatura e non inferendo, in pratica non si può. – Jtello

+0

Sì, nel mio esempio sopra sto usando schemaLocation per fare riferimento. Ma questo è solo un modo per farvi riferimento in linea. Ci sono molti altri modi per farlo in linea (cioè - sotto il nodo radice), ma non riesco a trovare una libreria che analizzerà e convaliderà tutti questi casi. – Jono

1

Si potrebbe estrarre gli schemi stessi e importarli in uno schema radice:

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) 

BTW, il tuo simpletest.xsd manca il 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> 

Con il codice sopra, il documento di esempio è valido contro questo schema.