使用递归解析Python中的XML,返回值问题

1 投票
2 回答
4733 浏览
提问于 2025-04-16 19:44

我对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 个回答

0

当你找到要搜索的节点时,其实你并没有对递归调用的结果做任何处理。

你需要把对节点孩子的递归调用结果汇总起来,或者直接用 print child.text 来打印孩子的内容,而不是用 return child.text 来返回。

6

在使用递归的时候,你需要注意两个重要的部分:基础情况和递归情况。基础情况就是你递归的终点,递归情况则是你在不断调用自己函数的过程。无论你的基础情况是什么,如果你希望从递归中获取信息,它们必须返回一些数据,这些数据要能被递归情况使用(更重要的是,确实要被使用)。同样,你还需要确保递归情况返回的数据能够相互使用。

首先,确定你的基础情况和递归情况。基础情况就像树的“叶子”节点,没有子节点。在基础情况中,你只需要返回一些数据,而不再调用递归函数。这就能让你“回到上层”,避免无限递归的问题。递归情况则需要你保存从一系列递归调用中收集到的数据,这几乎和你在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)

撰写回答