【问题标题】:xml parse without the recursive search python没有递归搜索python的xml解析
【发布时间】:2011-02-05 18:28:14
【问题描述】:

这让我精神崩溃,而且我可能已经研究了很久,所以希望能得到一些帮助,以防止失去/恢复我的理智!基于食物的 xml 只是我希望实现的一个示例。

我有以下文件,我试图将其放入图表中,因此小麦和水果是深度为 0 的父母。印第安人是深度为 1 的小麦的孩子,依此类推。

每一层都有一些关键字。所以我想出来的是

layer, depth, parent, keywords
wheat, 1, ROOT, [bread, pita, narn, loaf]  
indian, 2, wheat [chapati]
mumbai, 3, indian, puri 
fruit, 1,ROOT, [apple, orange, pear, lemon]

这是一个示例文件 -

<keywords>
    <layer id="wheat">
        <layer id="indian">
            <keyword>chapati</keyword>
            <layer id="mumbai">
                <keyword>puri</keyword>
            </layer>
        </layer>
        <keyword>bread</keyword>
        <keyword>pita</keyword>
        <keyword>narn</keyword>
        <keyword>loaf</keyword>
    </layer>
    <layer id="fruit">
        <keyword>apple</keyword>
        <keyword>orange</keyword>
        <keyword>pear</keyword>
        <keyword>lemon</keyword>
    </layer>

</keywords>

所以这不是一个图表问题,我可以很容易地做到这一点。我正在努力解析 XML。

如果我做一个

xmldoc = minidom.parse(self.filename)

layers = xmldoc.getElementsByTagName('layer')

layers 只返回所有层元素,这太多了,据我所知,它没有深度/层次的概念,因为它进行递归搜索。

以下帖子很好,但没有提供我需要的概念。 XML Parsing with Python and minidom。任何人都可以帮助我解决这个问题吗?我可以发布我的代码,但它是如此被黑客攻击/从根本上被破坏,我认为它不会被人类或野兽使用!

干杯

戴夫

【问题讨论】:

  • 使用递归将是最合乎逻辑的方式来做到这一点恕我直言。你为什么不想使用它?

标签: python xml dom


【解决方案1】:

使用lxml。特别是 XPath。您可以通过"//layer" 和ID 为idlayer 通过"//layer[id='{}'][0]".format(id) 获取所有layer 元素,无论级别如何。 ".../keyword" 的一个元素(或多个元素)正下方的 keyword 元素(其中 ... 是一个查询,它产生应搜索其后代的节点)。

获取给定节点的深度并不是那么简单,但仍然很容易。我没有找到现有的函数(afaik,这是在 XPath 的域之外 - 虽然您可以在查询中检查深度,但您只能返回元素,即您可以返回具有特定深度但不能返回深度本身的节点) ,所以这里是一个手动的(没有递归,因为它不是必需的 - 但一般来说,使用 XML 意味着使用递归,无论喜欢与否!):

def depth(node):
    depth = 0
    while node.getparent() is not None:
        node = node.getParent()
        depth += 1
    return depth

如果你愚蠢到不使用现有最好的 Python XML 库,那么 DOM 可能会发生非常相似的事情;)

【讨论】:

    【解决方案2】:

    这是ElementTree的解决方案:

    from xml.etree import ElementTree as ET
    from io import StringIO
    from collections import defaultdict
    
    data = '''\
    <keywords>
        <layer id="wheat">
            <layer id="indian">
                <keyword>chapati</keyword>
                <layer id="mumbai">
                    <keyword>puri</keyword>
                </layer>
            </layer>
            <keyword>bread</keyword>
            <keyword>pita</keyword>
            <keyword>narn</keyword>
            <keyword>loaf</keyword>
        </layer>
        <layer id="fruit">
            <keyword>apple</keyword>
            <keyword>orange</keyword>
            <keyword>pear</keyword>
            <keyword>lemon</keyword>
        </layer>
    </keywords>
    '''
    
    path = ['ROOT']  # stack for layer names
    items = defaultdict(list)  # key=layer, value=list of items @ layer
    
    f = StringIO(data)
    for evt,e in ET.iterparse(f,('start','end')):
        if evt == 'start':
            if e.tag == 'layer':
                path.append(e.attrib['id']) # new layer added to path
            elif e.tag == 'keyword':
                items[path[-1]].append(e.text) # add item to last layer in path
        elif evt == 'end':
            if e.tag == 'layer':
                layer = path.pop()
                parent = path[-1]
                print layer,len(path),parent,items[layer]
    

    输出

    mumbai 3 indian ['puri']
    indian 2 wheat ['chapati']
    wheat 1 ROOT ['bread', 'pita', 'narn', 'loaf']
    fruit 1 ROOT ['apple', 'orange', 'pear', 'lemon']
    

    【讨论】:

      【解决方案3】:

      您可以递归遍历 DOM treje(请参阅 kelloti 的回答)或从找到的节点中确定信息:

      xmldoc = minidom.parse(filename)
      layers = xmldoc.getElementsByTagName("layer")
      
      def _getText(node):
          rc = []
          for n in node.childNodes:
              if n.nodeType == n.TEXT_NODE:
                  rc.append(n.data)
          return ''.join(rc)
      
      def _depth(n):
          res = -1
          while isinstance(n, minidom.Element):
              n = n.parentNode
              res += 1
          return res
      
      for l in layers:
          keywords = [_getText(k) for k in l.childNodes
                      if k.nodeType == k.ELEMENT_NODE and k.tagName == 'keyword']
          print("%s %s %s" % (l.getAttribute("id"), _depth(l), keywords))
      

      【讨论】:

        【解决方案4】:

        尝试在递归函数中遍历所有子节点,检查每个子节点的标签名称。即

        def findLayer(node):
            for n in node.childNodes:
                if n.localName == 'layer':
                    findLayer(n)
                    # do things here
        

        或者,尝试使用具有 XPath 功能的不同 XML 库,例如 Amaralxml。使用 XPath,您可以用很少的代码对搜索 DOM 树进行更多控制。

        【讨论】:

          猜你喜欢
          • 2019-12-20
          • 1970-01-01
          • 1970-01-01
          • 2022-11-14
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多