【问题标题】:Python XML validation with SAX, pyexpat, ElementTree使用 SAX、pyexpat、ElementTree 进行 Python XML 验证
【发布时间】:2015-08-08 20:15:52
【问题描述】:

我正在尝试在 Python 3.4 中检查 XML 文件(针对 DTD、实体、处理指令、命名空间)的有效性。

查看 Python 文档,三个 Python XML 模块 pyexpat、ELementTree 和 SAX 的默认底层解析器是 expat。在 Pyexpat 页面 (https://docs.python.org/3.4/library/pyexpat.html?highlight=pyexpat#module-xml.parsers.expat) 上说使用了非验证版本的 expat 解析器: “xml.parsers.expat 模块是 Expat 非验证 XML 解析器的 Python 接口。”然而,与此同时,当您查看 Python 中的 SAX 文档时,您会看到所有这些用于启用 DTD 验证等的处理函数。您到底是如何让它们工作的?

但是,根据这篇帖子Parsing XML Entity with python xml.sax SAX 可以验证。显然用 expat 作为解析器。

我已经重用了这篇文章中的代码,但无法让它工作我收到错误说 expat 不支持验证: “文件“/usr/lib/python3.4/xml/sax/expatreader.py”,第 149 行,在 setFeature 中 “外籍人士不支持验证”) xml.sax._exceptions.SAXNotSupportedException: expat 不支持验证”。 在帖子中使用了 Python 2.5,所以也许 SAX 从那时起发生了变化......

这是代码:

import xml.sax
from xml.sax import handler, make_parser, parse
import os
import collections

class SaxParser():
    # initializer with directory part as argument
    def __init__(self, dir_path):
        self.dir_path = dir_path

    def test_each_file(self, file_path):
        # ensure full file name is shown
        rev = file_path[::-1]   # reverse string file_path to access position of "/"
        file = file_path[-rev.index("/"):]
        try:
            f = open(file_path, 'r', encoding="ISO-8859-1") # same as "latin-1" encoding
            # see this for enabling validation:
            # https://stackoverflow.com/questions/6349513/parsing-xml-entity-with-python-xml-sax

            parser = make_parser()  # default parser is expat
            parser.setContentHandler(handler.ContentHandler())
            parser.setFeature(handler.feature_namespaces,True)
            parser.setFeature(handler.feature_validation,True)
            parser.setFeature(handler.feature_external_ges, True)
            parser.parse(f)
            f.close()
            return (file, "OK")

        except xml.sax.SAXParseException as PE:
            column = PE.getColumnNumber()
            line = PE.getLineNumber()
            msg = PE.getMessage()
            value = msg + " " + str(line) + " " + str(column)
            return (file, value)

        except ValueError:
            return (file, "ValueError. DTD uri not found.") # that can happen



    def test_directory_sax(self, dir_path):
        tuples = []
        for ind, file in enumerate(os.listdir(dir_path), 1):
            if file.endswith('.xml'):
                tuples.append(self.test_each_file(dir_path + file))

        # convert into dict and sort it by key (file number)
        dict_of_errors = dict(tuples)
        dict_of_errors = collections.OrderedDict(sorted(dict_of_errors.items()))
        return dict_of_errors


# ========================================================================
# INVOKE TESTS FOR SINGLE SPECIFIED DIRECTORY THAT CONTAINS TEST FILES
# ========================================================================
path = # path to directory where xml file is. - not the filepath!
single_sax = SaxParser(path)
print('============================================================')
print('TEST FOR SAX parser FOR DIRECTORY ' + path)
print('============================================================\n')
print(single_sax.test_directory_sax(path))

和测试xml文件(应该产生验证错误):

<!DOCTYPE root [
    <!ATTLIST root
    id2 ID  "x23"
    >
]>

<!-- an ID attribute must have a declared default
    of #IMPLIED or #REQUIRED
-->

<root/>

如何检查有效性?对于三个 XML 模块之一? 一个简单的例子就可以了。

谢谢。

【问题讨论】:

    标签: python xml validation parsing


    【解决方案1】:

    如果您查看源文件,您会发现 xml.sax.handler.feature_validation 并没有真正做任何事情,只是引发了这个异常:

    def setFeature(self, name, state):
        # ...
        elif name == feature_validation:
            if state:
                raise SAXNotSupportedException(
                    "expat does not support validation")
        #...
    

    我建议使用lxml 来执行此操作。一个例子是这样的:

    from lxml import etree
    from cStringIO import StringIO
    # from io import StringIO (py3)
    
    f = StringIO('<!ATTLIST root id2 ID "x23">')
    
    dtd =  etree.DTD(f)
    
    root = etree.XML('<root/>')
    
    print(dtd.validate(root))
    print(dtd.error_log.filter_from_errors()[0])
    

    【讨论】:

    • 谢谢。这对我来说是个坏消息,但我想事情就是这样。 expat 不支持验证。我有 lxml 用于验证。我正在比较解析器,我希望我可以让 ElementTree、SAX 和 pyexpat 进行验证。
    • 我当时提到的那个帖子是什么?那里的人确实使用 SAX 进行了验证。
    • 我不会称之为“验证”。这更像是利用低级处理程序来控制解析过程并捕获任何可能的异常。
    猜你喜欢
    • 2011-01-01
    • 2013-05-26
    • 1970-01-01
    • 2021-12-30
    • 2010-12-05
    • 2015-06-28
    • 2012-08-29
    • 2010-09-16
    • 1970-01-01
    相关资源
    最近更新 更多