使用递归解析Python中的XML,返回值问题
我对Python和编程还比较陌生,所以请多包涵。先谢谢大家的帮助。
我正在用Python 2.5、cElementTree和expat来解析一个xml文档(具体来说是kml格式,主要用于Google Earth)。我想从每个'placemark'节点中的'name'、'description'和'coordinates'节点提取所有文本,而且我希望把不同的几何类型(比如折线、多边形、点)分开处理。举个例子,我只想要属于'polygon'(也就是有'polygon'节点的)的'placemark'的'name'、'description'和'coordinates'文本。我也需要对'polylines'和'points'做同样的事情。我已经找到了一个方法来实现这个目标,但代码比较长,而且针对每种几何类型都写得很具体,这让我有些困惑。
理想情况下,我希望能用同一段代码处理每种几何类型,但问题是每种几何类型的节点结构不同(也就是节点名称和嵌套节点的数量不同)。所以我觉得这是一个很好的机会来使用/学习递归,深入到'placemark'节点的节点树中,获取我想要的信息。我看了很多关于Python递归的帖子,但在实现这些解决方案时仍然遇到问题。
一个'placemark'节点的示例xml是:
<Placemark>
<name>testPolygon</name>
<description>polygon text</description>
<styleUrl>#msn_ylw-pushpin</styleUrl>
<Polygon>
<tessellate>1</tessellate>
<outerBoundaryIs>
<LinearRing>
<coordinates>
-81.4065,31.5072,0 -81.41269,31.45992,0 -81.34490,31.459696,0
</coordinates>
</LinearRing>
</outerBoundaryIs>
</Polygon>
</Placemark>
我正在使用的递归函数是:
def getCoords( child, searchNode ):
# Get children of node
children = child.getchildren()
# If node has one or more child
if len( children ) >= 1 :
# Loop through all the children
for child in children:
# call to recursion function
getCoords( child, searchNode )
# If does not have children and is the 'searchNode'
elif len( children ) == 0 and child.tag == searchNode:
# Return the text inside the node. This is where it is not working
# Other posts recommended returning the function like
# return getCoords(child, searchNode), but I am getting an unending loop
return child.text
# Do nothing if node doesn't have children and does not match 'searchNode'
else:
print 'node does not have children and is not what we are looking for'
我调用递归函数的方式是:
searchNode = 'coordinates'
# loop through all 'Placemark nodes' in document
for mark in placemark:
# Get children of 'Placemark' node
children = mark.getchildren()
# Loop through children nodes
for child in children:
# if a 'Polygon' node is found
if child.tag == 'Polygon':
# call recursion function
getCoords( child, searchNode)
我意识到,至少部分问题出在返回值上。其他帖子建议返回函数,我理解为'return getCoords(child, searchNode)',但我遇到了无限循环的问题。此外,我知道这个问题也可以发到GIS网站上,但我觉得这更像是一个通用的编程问题。有没有什么想法?
2 个回答
当你找到要搜索的节点时,其实你并没有对递归调用的结果做任何处理。
你需要把对节点孩子的递归调用结果汇总起来,或者直接用 print child.text 来打印孩子的内容,而不是用 return child.text 来返回。
在使用递归的时候,你需要注意两个重要的部分:基础情况和递归情况。基础情况就是你递归的终点,递归情况则是你在不断调用自己函数的过程。无论你的基础情况是什么,如果你希望从递归中获取信息,它们必须返回一些数据,这些数据要能被递归情况使用(更重要的是,确实要被使用)。同样,你还需要确保递归情况返回的数据能够相互使用。
首先,确定你的基础情况和递归情况。基础情况就像树的“叶子”节点,没有子节点。在基础情况中,你只需要返回一些数据,而不再调用递归函数。这就能让你“回到上层”,避免无限递归的问题。递归情况则需要你保存从一系列递归调用中收集到的数据,这几乎和你在for
循环中做的事情是一样的。
我注意到你有
# Recursive case: node has one or more child
if len( children ) >= 1 :
# Loop through all the children
for child in children:
# call to recursion function
getCoords( child, searchNode )
但是你对你的getCoords调用的结果做了什么呢?
你要么想把结果保存在某种数据结构中,以便在for
循环结束时返回,或者如果你不想保存结果本身,当你达到基础情况1(成功找到)时,直接打印出来,而不是返回它。因为现在你的基础情况1只是把结果返回给一个没有处理结果的实例!所以试试:
# If node has one or more child
if len( children ) >= 1 :
# Data structure for your results
coords = []
# Loop through all the children
for child in children:
# call to recursion function
result = getCoords( child, searchNode )
# Add your new results together
coords.extend(result)
# Give the next instance up the stack your results!
return coords
现在,由于你的结果在一个列表中,并且你使用了extend()
方法,你的基础情况也必须返回列表!
# Base case 1: does not have children and is the 'searchNode'
elif len( children ) == 0 and child.tag == searchNode:
# Return the text from the node, inside a list
return [child.text]
# Base case 2: doesn't have children and does not match 'searchNode'
else:
# Return empty list so your extend() function knows what to do with the result
return []
这样最终应该只会给你一个单一的列表,你可能想把它存储在一个变量中。我在这里只是打印了结果:
searchNode = 'coordinates'
# loop through all 'Placemark nodes' in document
for mark in placemark:
# Get children of 'Placemark' node
children = mark.getchildren()
# I imagine that getchildren() might return None, so check it
# otherwise you'll get an error when trying to iterate on it
if children:
# Loop through children nodes
for child in children:
# if a 'Polygon' node is found
if child.tag == 'Polygon':
# call recursion function and print (or save) result
print getCoords( child, searchNode)