如何使用etree.ElementTree验证XML文件中每个<book>元素是否包含<target>子元素
如何用ElementTree验证每个元素都包含子元素
嘿,我来帮你搞定这个XML验证的问题!你的思路方向是对的,但现有代码有几个小问题,而且单纯对比数量的逻辑其实有漏洞——我给你梳理下更可靠的实现方式,包括如何处理XML命名空间这个容易踩坑的点。
首先,你的现有代码的问题:
- 拼写错误:
tree.findall(".//books")里的books应该是book,你的XML里是<book>元素 - 忽略了XML命名空间:你的XML根元素带有
xmlns="urn:oasis:names:tc:xliff:document:2.0",这意味着所有元素都属于这个命名空间,ElementTree默认不会识别不带前缀的元素,直接用findall(".//book")会找不到任何内容 - 数量对比逻辑不严谨:就算
book和target的数量相等,也不能保证每个book里都有一个target——比如某个book没有target,但另一个book里有两个target,总数还是相等,但实际存在错误;或者其他非book的元素里有target,也会干扰计数
接下来,正确的实现方式:
方式1:逐个遍历并检查
这种方式可以即时定位到具体哪个book缺失target,调试起来更方便:
import xml.etree.ElementTree as ET # 你的XML内容(如果是从文件读取,替换为ET.parse('your_file.xml')) xml_content = '''<xliff xmlns="urn:oasis:names:tc:xliff:document:2.0" version="2.0"><course id="cr1"><book id="bk1"><dek><source>ssf</source><target>ssf</target></dek></book> <book id="bk2"><dek><source>ssf</source><target>ssf</target></dek></book> <book id="bk3"><dek><source>ssf</source><target>ssf</target></dek></book> <book id="bk4"><dek><source>ssf</source><target>ssf</target></dek></book> </course> <course id="cr2"><book id="bk1"><dek><source>ssf</source><target>ssf</target></dek></book> <book id="bk2"><dek><source>ssf</source><target>ssf</target></dek></book> <book id="bk3"><dek><source>ssf</source><target>ssf</target></dek></book> <book id="bk4"><dek><source>ssf</source><target>ssf</target></dek></book> </course> </xliff>''' # 解析XML tree = ET.ElementTree(ET.fromstring(xml_content)) # 定义命名空间映射(必须指定,否则无法找到目标元素) ns = {'xliff': 'urn:oasis:names:tc:xliff:document:2.0'} # 遍历所有<book>元素 for book in tree.findall('.//xliff:book', ns): # 在当前book下查找<target>子元素 target = book.find('.//xliff:target', ns) if not target: # 抛出错误并带上book的id,方便快速定位问题 raise ValueError(f"Error: Book with id '{book.get('id')}' is missing the <target> sub-element!") print("All books have valid <target> elements!")
方式2:一次调用获取所有无效的
如果你想一次性找出所有缺失target的book再统一报错,可以用XPath的not()表达式实现“一次调用完成”的需求:
import xml.etree.ElementTree as ET xml_content = '''<xliff xmlns="urn:oasis:names:tc:xliff:document:2.0" version="2.0"><course id="cr1"><book id="bk1"><dek><source>ssf</source><target>ssf</target></dek></book> <book id="bk2"><dek><source>ssf</source><target>ssf</target></dek></book> <book id="bk3"><dek><source>ssf</source><target>ssf</target></dek></book> <book id="bk4"><dek><source>ssf</source><target>ssf</target></dek></book> </course> <course id="cr2"><book id="bk1"><dek><source>ssf</source><target>ssf</target></dek></book> <book id="bk2"><dek><source>ssf</source><target>ssf</target></dek></book> <book id="bk3"><dek><source>ssf</source><target>ssf</target></dek></book> <book id="bk4"><dek><source>ssf</source><target>ssf</target></dek></book> </course> </xliff>''' # 解析XML tree = ET.ElementTree(ET.fromstring(xml_content)) ns = {'xliff': 'urn:oasis:names:tc:xliff:document:2.0'} # 一次调用找出所有没有<target>子元素的<book> invalid_books = tree.findall(".//xliff:book[not(.//xliff:target)]", ns) if invalid_books: # 收集无效book的id,统一抛出错误 invalid_ids = [book.get('id') for book in invalid_books] raise ValueError(f"Error: The following books are missing <target> elements: {', '.join(invalid_ids)}") print("All books are valid!")
关于“一次调用完成”的说明
第二种方式就是通过一次findall调用完成筛选,比单纯对比数量要可靠得多——它直接针对每个book的结构进行检查,不会被其他元素的target或者重复的target干扰。
内容的提问来源于stack exchange,提问作者Inudnas
相关产品推荐
相关产品推荐

