命名空间(Namespace)是 XML 中避免元素名冲突的机制,而 XML 验证则确保文档符合预定义的结构规则。XML 常用验证方式有 DTD 和 XSD(XML Schema)。本章下面将介绍如何使用 Python 实现 XML 验证:
DTD 是 XML 的原始验证机制,可以直接定义在文档内部,也可以使用 dtd 外部文件存储。下面为了演示,直接在 XML 中定义:
from lxml import etree
def validate_xml_with_dtd(xml_content: str) -> tuple[bool, str]:
"""
校验XML文档语法 + 内部DTD约束是否合法
:param xml_content: 完整XML字符串(包含DOCTYPE DTD定义)
:return: (是否合法, 错误信息/成功提示)
"""
try:
# 1. 解析XML,开启DTD验证
parser = etree.XMLParser(dtd_validation=True)
tree = etree.fromstring(xml_content, parser=parser)
# 2. 检查解析器是否捕获校验错误
if parser.error_log:
error_msg = "\n".join([f"行{err.lineno}: {err.message}" for err in parser.error_log])
return False, f"XML/DTD校验失败:\n{error_msg}"
return True, "XML文档语法正确,且完全符合DTD约束,校验通过!"
except etree.XMLSyntaxError as e:
# XML标签语法错误(未闭合、标签非法等)
return False, f"XML语法解析错误:{e.msg} 行号:{e.lineno}"
except Exception as e:
# 其他未知异常
return False, f"校验发生未知异常:{str(e)}"
if __name__ == "__main__":
# 题目提供的完整XML内容
xml_str = """<?xml version="1.0"?>
<!DOCTYPE note [
<!ELEMENT note (to, from, heading, body)>
<!ELEMENT to (#PCDATA)>
<!ELEMENT from (#PCDATA)>
<!ELEMENT heading (#PCDATA)>
<!ELEMENT body (#PCDATA)>
]>
<note>
<to>张三</to>
<from>李四</from>
<heading>提醒</heading>
<body>记得开会</body>
</note>
"""
# 执行校验
is_valid, msg = validate_xml_with_dtd(xml_str)
print("===== XML校验结果 =====")
print(msg)运行示例,输出如下:
===== XML校验结果 =====
XML文档语法正确,且完全符合DTD约束,校验通过!我们也可以将 XML 文档提取到外部 XML 文件,如将 XML 保存到 note.xml 文件:
<?xml version="1.0"?>
<!DOCTYPE note [
<!ELEMENT note (to, from, heading, body)>
<!ELEMENT to (#PCDATA)>
<!ELEMENT from (#PCDATA)>
<!ELEMENT heading (#PCDATA)>
<!ELEMENT body (#PCDATA)>
]>
<note>
<to>张三</to>
<from>李四</from>
<heading>提醒</heading>
<body>记得开会</body>
</note>在 Python 代码中个,直接读取 note.xml 文件进行验证,如下:
from lxml import etree
def validate_xml_file(file_path: str) -> tuple[bool, str]:
"""
读取本地XML文件,校验XML语法与内部DTD约束
:param file_path: xml文件路径
:return: (是否合法, 提示信息)
"""
try:
# 开启DTD校验解析器
parser = etree.XMLParser(dtd_validation=True)
# 读取并解析文件
tree = etree.parse(file_path, parser=parser)
# 收集校验错误日志
if parser.error_log:
err_list = [f"第{err.lineno}行:{err.message}" for err in parser.error_log]
return False, "DTD校验不通过:\n" + "\n".join(err_list)
return True, "XML格式合法,完全符合DTD约束"
except etree.XMLSyntaxError as e:
return False, f"XML语法错误:行{e.lineno} {e.msg}"
except FileNotFoundError:
return False, f"文件不存在:{file_path}"
except Exception as e:
return False, f"未知错误:{str(e)}"
if __name__ == "__main__":
xml_file = "note.xml"
valid, message = validate_xml_file(xml_file)
print("==== XML校验结果 ====")
print(message)运行示例,输出如下:
==== XML校验结果 ====
XML格式合法,完全符合DTD约束XSD 使用 XML 语法定义结构,功能比 DTD 更强大在前面的 Python3 XML Schema 比 DTD 更强大的约束工具 已经详细介绍。假如,我们存在 schema.xsd:
<?xml version="1.0"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="product">
<xs:complexType>
<xs:sequence>
<xs:element name="name" type="xs:string"/>
<xs:element name="price" type="xs:decimal"/>
<xs:element name="quantity" type="xs:integer"/>
</xs:sequence>
<xs:attribute name="id" type="xs:string" use="required"/>
</xs:complexType>
</xs:element>
</xs:schema>以及 XML 文档 product.xml:
<?xml version="1.0"?>
<product id="P001" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="schema.xsd">
<name>机械键盘</name>
<price>299.00</price>
<quantity>100</quantity>
</product>下面使用 lxml 来验证该文档。
验证代码非常简介,需要在解析 XML 前,先使用 etree.XMLSchema() 加载 Schema 定义,代码如下:
from lxml import etree
# 加载 Schema
with open('schema.xsd', 'rb') as f:
schema_root = etree.XML(f.read())
schema = etree.XMLSchema(schema_root)
# 创建验证器,见 schema 配置选项
parser = etree.XMLParser(schema=schema)
# 验证并解析 XML
try:
with open('product.xml', 'rb') as f:
doc = etree.parse(f, parser)
print("验证通过!")
except etree.XMLSyntaxError as e:
print(f"验证失败: {e}")运行示例,输出:
验证通过!指应用启动运行后,在执行阶段动态校验数据、格式、规则,而非编译 / 静态阶段提前检查。
即代码跑起来后,才加载 XML 文本、用 XSD 规则实时校验字段类型(age 是否数字)、结构合规性。
主要特点:
动态:输入数据可变,每次执行都重新校验;
延迟校验:静态扫描看不出问题,只有运行传入数据才暴露错误;
用于外部不可控输入:XML、接口参数、用户输入等。
运行时校验可以拦截非法数据,避免脏数据导致逻辑崩溃,输出明确错误日志定位问题。例如:
from lxml import etree
def validate_xml(xml_string, xsd_string):
"""验证 XML 字符串是否符合 Schema"""
# 解析 Schema
schema_root = etree.fromstring(xsd_string.encode('utf-8'))
schema = etree.XMLSchema(schema_root)
# 解析 XML
xml_doc = etree.fromstring(xml_string.encode('utf-8'))
# 验证
is_valid = schema.validate(xml_doc)
if not is_valid:
print("验证错误:")
for error in schema.error_log:
print(f" 行 {error.line}: {error.message}")
return is_valid
# 定义 schema,直接定义到代码中
xsd_schema = '''
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="user">
<xs:complexType>
<xs:sequence>
<xs:element name="name" type="xs:string"/>
<xs:element name="age" type="xs:integer"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
'''
# 有效的 XML
valid_xml = '''
<user>
<name>张三</name>
<age>25</age>
</user>
'''
# 无效的 XML(age 不是整数)
invalid_xml = '''
<user>
<name>李四</name>
<age>abc</age>
</user>
'''
print("验证有效 XML:", validate_xml(valid_xml, xsd_schema))
print("\n验证无效 XML:", validate_xml(invalid_xml, xsd_schema))运行示例,输出如下:
验证有效 XML: True
验证错误:
行 4: Element 'age': 'abc' is not a valid value of the atomic type 'xs:integer'.
验证无效 XML: False创建一个 ConfigValidator 配置验证类,该类内部维护了一个 Schema,并封装了验证方法,方便操作。完整代码如下:
from lxml import etree
from typing import List, Dict
class ConfigValidator:
"""配置文件验证器"""
# 内嵌 Schema
CONFIG_SCHEMA = '''
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:simpleType name="LogLevel">
<xs:restriction base="xs:string">
<xs:enumeration value="DEBUG"/>
<xs:enumeration value="INFO"/>
<xs:enumeration value="WARNING"/>
<xs:enumeration value="ERROR"/>
</xs:restriction>
</xs:simpleType>
<xs:element name="configuration">
<xs:complexType>
<xs:all>
<xs:element name="database" minOccurs="1">
<xs:complexType>
<xs:sequence>
<xs:element name="host" type="xs:string"/>
<xs:element name="port" type="xs:integer"/>
<xs:element name="name" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="logging" minOccurs="0">
<xs:complexType>
<xs:sequence>
<xs:element name="level" type="LogLevel"/>
<xs:element name="file" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:all>
</xs:complexType>
</xs:element>
</xs:schema>
'''
def __init__(self):
schema_root = etree.fromstring(self.CONFIG_SCHEMA.encode('utf-8'))
self.schema = etree.XMLSchema(schema_root)
def validate(self, xml_source: str) -> tuple[bool, List[str]]:
"""验证 XML,返回 (是否有效, 错误列表)"""
try:
if xml_source.strip().startswith('<'):
doc = etree.fromstring(xml_source.encode('utf-8'))
else:
doc = etree.parse(xml_source).getroot()
self.schema.assertValid(doc)
return True, []
except etree.DocumentInvalid as e:
errors = [str(err) for err in self.schema.error_log]
return False, errors
except etree.XMLSyntaxError as e:
return False, [f"XML 语法错误: {e}"]
def validate_and_parse(self, xml_source: str) -> Dict:
"""验证并解析配置"""
is_valid, errors = self.validate(xml_source)
if not is_valid:
raise ValueError(f"配置验证失败: {errors}")
# 解析为字典
if xml_source.strip().startswith('<'):
root = etree.fromstring(xml_source.encode('utf-8'))
else:
root = etree.parse(xml_source).getroot()
result = {}
for section in root:
section_data = {}
for field in section:
section_data[field.tag] = field.text
result[section.tag] = section_data
return result
# 使用示例
if __name__ == '__main__':
validator = ConfigValidator()
# 有效配置
valid_config = '''
<configuration>
<database>
<host>localhost</host>
<port>5432</port>
<name>myapp</name>
</database>
<logging>
<level>INFO</level>
<file>app.log</file>
</logging>
</configuration>
'''
# 无效配置(错误的日志级别)
invalid_config = '''
<configuration>
<database>
<host>localhost</host>
<port>5432</port>
<name>myapp</name>
</database>
<logging>
<level>VERBOSE</level>
<file>app.log</file>
</logging>
</configuration>
'''
print("验证有效配置:")
is_valid, errors = validator.validate(valid_config)
print(f" 结果: {is_valid}")
if is_valid:
config = validator.validate_and_parse(valid_config)
print(f" 配置内容: {config}")
print("\n验证无效配置:")
is_valid, errors = validator.validate(invalid_config)
print(f" 结果: {is_valid}")
print(f" 错误: {errors}")运行示例,输出如下:
验证有效配置:
结果: True
配置内容: {'database': {'host': 'localhost', 'port': '5432', 'name': 'myapp'}, 'logging': {'level': 'INFO', 'file': 'app.log'}}
验证无效配置:
结果: False
错误: ["<string>:9:0:ERROR:SCHEMASV:SCHEMAV_CVC_ENUMERATION_VALID: Element 'level': [facet 'enumeration'] The value 'VERBOSE' is not an element of the set {'DEBUG', 'INFO', 'WARNING', 'ERROR'}."]到这里,关于 XML 部分的知识就介绍完了,更多知识,请查看后续章节。