我必须在这里做一些内在错误的事情,我所见过的每一个例子以及在SO上搜索似乎都表明这会起作用。
我尝试使用lxml etree库进行XPath搜索来解析garmin tcx文件:
<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
<TrainingCenterDatabase xmlns="http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2 http://www.garmin.com/xmlschemas/TrainingCenterDatabasev2.xsd">
<Workouts>
<Workout Sport="Biking">
<Name>3P2 WK16 - 3</Name>
<Step xsi:type="Step_t">
<StepId>1</StepId>
<Name>[MP19]6:28-6:38</Name>
<Duration xsi:type="Distance_t">
<Meters>13000</Meters>
</Duration>
<Intensity>Active</Intensity>
<Target xsi:type="Speed_t">
<SpeedZone xsi:type="PredefinedSpeedZone_t">
<Number>2</Number>
</SpeedZone>
</Target>
</Step>
......
</Workout>
</Workouts>
</TrainingCenterDatabase>
我想仅在类型为PredefinedSpeedZone_t的情况下返回SpeedZone元素。我以为我能做到:
root = ET.parse(open('file.tcx'))
xsi = {'xsi': 'http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2'}
for speed_zone in root.xpath(".//xsi:SpeedZone[@xsi:type='PredefinedSpeedZone_t']", namespaces=xsi):
print speed_zone
虽然情况似乎并非如此。我已经尝试了许多删除/添加命名空间的组合,但无济于事。如果我删除属性搜索并将其保留为".//xsi:SpeedZone"
,则会返回:
<Element {http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2}SpeedZone at 0x2595188>
正如我所期待的那样。
我想我可以在for循环中做到这一点,但感觉它应该可以在一行!
答案 0 :(得分:3)
我有点迟了,但其他答案让我感到困惑。
在问题的Python代码和其他两个答案中,xsi
前缀绑定到http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2
URI。但是在包含Garmin数据的XML文档中,xsi
绑定到http://www.w3.org/2001/XMLSchema-instance
。
由于这里有两个名称空间,我认为以下代码可以更清楚地了解正在发生的事情。与tcd
前缀关联的名称空间是默认名称空间。
from lxml import etree
NSMAP = {"tcd": "http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2",
"xsi": "http://www.w3.org/2001/XMLSchema-instance"}
root = etree.parse('file.tcx')
for speed_zone in root.xpath(".//tcd:SpeedZone[@xsi:type='PredefinedSpeedZone_t']",
namespaces=NSMAP):
print speed_zone
输出:
<Element {http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2}SpeedZone at 0x25b7e18>
答案 1 :(得分:1)
解决此问题的一种方法是避免指定属性名称并使用*
:
.//xsi:SpeedZone[@*='PredefinedSpeedZone_t']
另一个选项(不像前一个那样令人敬畏)是实际获取所有SpeedZone
标签并检查循环中的属性值:
attribute_name = '{%s}type' % root.nsmap['xsi']
for speed_zone in root.xpath(".//xsi:SpeedZone", namespaces=xsi):
if speed_zone.attrib.get(attribute_name) == 'PredefinedSpeedZone_t':
print speed_zone
希望有所帮助。
答案 2 :(得分:1)
如果其他所有方法都失败了,你仍然可以使用
".//xsi:SpeedZone[@*[name() = 'xsi:type' and . = 'PredefinedSpeedZone_t']]"
使用name()
并不像直接寻址命名空间属性那样好,但至少etree能理解它。