如何在XSD文件中设置多个目标命名空间?Python XML校验场景
问题背景
刚接触Python和XML,正在开发公司的XML校验器。传入的XML文件因发送方软件版本不同,命名空间会变化(如http://www.XXXXX.dk/xml/schemas/XXXXXX/20120102和http://www.XXXXX.dk/xml/schemas/XXXXX/20140701),且无法修改原XML文件,当前使用Python的XMLSchema模块校验,之前的单XSD方案已失效。想知道能否在XSD中设置多个targetNamespace,或是有其他可行方案。
传入XML示例
示例1
<?xml version="1.0" encoding="utf-8"?> <KnudeGroup xmlns="http://www.XXXXX.dk/xml/schemas/XXXXXX/20120102"> <Referencesys>
示例2
<?xml version="1.0" encoding="ISO-8859-1"?> <LedningGroup xmlns="http://www.XXXXX.dk/xml/schemas/XXXXX/20140701"> <Referencesys>
当前临时XSD片段
<?xml version="1.0" encoding="UTF-8"?> <xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.XXXXX.dk/xml/schemas/XXXXX/20140701"> <xs:element name="LedningGroup">
核心结论:单XSD无法设置多个targetNamespace
XSD标准规定单个schema文件只能对应一个targetNamespace,无法在一个XSD中定义多个目标命名空间,所以直接在现有XSD中添加多个命名空间的方案不可行。
可行解决方案
方案1:多XSD文件逐个校验
针对每个可能的命名空间版本,编写对应的XSD文件,在校验时依次尝试用每个XSD去验证XML,只要其中一个校验通过即判定合法。
Python代码示例:
import xmlschema # 存储所有版本的XSD路径 xsd_files = [ "schema_20120102.xsd", "schema_20140701.xsd" ] target_xml = "待校验的文件.xml" is_valid = False for xsd_path in xsd_files: try: schema = xmlschema.XMLSchema(xsd_path) schema.validate(target_xml) is_valid = True break except xmlschema.XMLSchemaValidationError: # 当前XSD校验失败,尝试下一个 continue if is_valid: print("XML符合校验规范") else: print("XML不符合任何版本的XSD规范")
方案2:拆分公共结构+多版本XSD导入
如果不同版本的XML结构差异极小,可把公共元素(如Referencesys)抽成独立的公共XSD,再针对每个命名空间的根元素编写单独的XSD,通过xs:import引入公共定义,减少重复代码。
公共XSD示例(common.xsd):
<?xml version="1.0" encoding="UTF-8"?> <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> <!-- 定义所有版本通用的元素结构 --> <xs:element name="Referencesys"> <!-- 填充具体的元素结构定义 --> </xs:element> </xs:schema>
2012版本XSD示例(schema_20120102.xsd):
<?xml version="1.0" encoding="UTF-8"?> <xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.XXXXX.dk/xml/schemas/XXXXXX/20120102"> <xs:import namespace="" schemaLocation="common.xsd"/> <xs:element name="KnudeGroup"> <xs:complexType> <xs:sequence> <xs:element ref="Referencesys"/> <!-- 其他专属元素定义 --> </xs:sequence> </xs:complexType> </xs:element> </xs:schema>
2014版本XSD只需替换targetNamespace和根元素名称为LedningGroup即可,之后用方案1的代码逻辑校验。
方案3:内存中动态替换命名空间
若不同版本的XML元素结构完全一致,仅命名空间和根元素名称不同,可在内存中加载XML后临时替换命名空间/根元素,再用统一的XSD校验(不修改原文件)。
Python代码示例(依赖lxml库):
import xmlschema from lxml import etree target_xml = "待校验的文件.xml" # 统一使用的目标XSD路径 target_xsd = "schema_20140701.xsd" # 目标命名空间和根元素 target_ns = "http://www.XXXXX.dk/xml/schemas/XXXXX/20140701" target_root = "LedningGroup" # 加载XML到内存 tree = etree.parse(target_xml) root = tree.getroot() # 获取当前根元素的命名空间和名称 current_ns = root.nsmap[None] current_root = root.tag.split("}")[-1] # 替换命名空间和根元素(如果需要) if current_ns != target_ns: # 替换所有元素的命名空间 for elem in tree.iter(): if elem.tag.startswith(f"{{{current_ns}}}"): elem.tag = elem.tag.replace(f"{{{current_ns}}}", f"{{{target_ns}}}") # 替换根元素名称(如果需要) if current_root != target_root: root.tag = f"{{{target_ns}}}{target_root}" # 转换为内存中的XML字符串 modified_xml = etree.tostring(tree, encoding="utf-8") # 执行校验 try: schema = xmlschema.XMLSchema(target_xsd) schema.validate(modified_xml) print("XML校验通过") except xmlschema.XMLSchemaValidationError: print("XML不符合规范")
注意:此方案仅适用于不同版本XML结构完全一致的场景,否则替换后校验会失败。
内容的提问来源于stack exchange,提问作者Frederikke Kappelhøj

