【问题标题】:Iterate through XML to find absolute path遍历 XML 以找到绝对路径
【发布时间】:2020-04-20 16:10:16
【问题描述】:

我正在寻找一种遍历 XML 文件并将元素标签/文本组合成字符串的方法。 XML 文件是一个配置导出。我试过使用 iter() 方法,但它并没有完全按照我需要的方式运行。这是原始的 XML。为了便于阅读,示例被截断。

<Configuration product="Cisco Codec" version="ce9.8.0.be9359915d0" apiVersion="4">
  <Audio>
    <DefaultVolume valueSpaceRef="/Valuespace/INT_0_100">70</DefaultVolume>
    <Input>
      <HDMI item="3" maxOccurrence="1">
        <Level valueSpaceRef="/Valuespace/INT_-24_0">-12</Level>
        <Mode valueSpaceRef="/Valuespace/TTPAR_OnOff">On</Mode>
        <VideoAssociation>
          <MuteOnInactiveVideo valueSpaceRef="/Valuespace/TTPAR_OnOff">On</MuteOnInactiveVideo>
        </VideoAssociation>
      </HDMI>
      ....

这些值最终会被转换成字符串

Audio DefaultVolume: 70
Audio Input HDMI 3 Level: -12
Audio Input HDMI 3 Mode: On
Audio Input HDMI 3 VideoAssociation MuteOnInactiveVideo: On
...

XML 有多个类别的设置。例如Video Input 2 RGB: On 所以我不能硬编码搜索字符串。在一天结束时,我希望遍历每个元素并打印出父元素标签以及最终元素文本。父元素不包含值的模式一致,则最后一个元素包含设置值。

【问题讨论】:

    标签: python-3.x elementtree


    【解决方案1】:

    这可以使用 lxml 和 xpath 来完成:

    audio = """[your xml above, fixed"""] #the xml in your example was invalid because closing tags were missing
    
    from lxml import etree
    doc = etree.XML(audio.encode('utf-8'))
    items = doc.xpath('//Audio//*')
    for item in items:
        ancs = []
        if len(item.text.strip())>0:        
            for anc in item.iterancestors():            
                ancs.append(anc.tag)
                if anc.tag == "HDMI":
                    ancs.append(anc.xpath('./@item')[0])
            ancs = ancs [:-1]
            ancs.reverse()        
            print(' '.join(ancs),item.tag,':',item.text.strip())
    

    输出:

    Audio DefaultVolume : 70
    Audio Input 3 HDMI Level : -12
    Audio Input 3 HDMI Mode : On
    Audio Input 3 HDMI VideoAssociation MuteOnInactiveVideo : On
    

    【讨论】:

    • 这种方法在实现我想要做的事情方面似乎更可行。我可能应该解释说,XML 文件将具有大量其他属性,而不仅仅是 HDMI。我更新了原始问题以适当地反映任务。归根结底,我只需要遍历每个可用元素,然后打印它的完整路径。
    【解决方案2】:

    这里最好使用lxml

    定义如下函数:

    def getParentNames(nd):
        res = []
        while True:
            itm = nd.attrib.get('item')
            if itm is not None:
                res.append(itm)
            res.append(nd.tag)    # Current tag name
            nd = nd.getparent()   # Go 1 level up
            if nd is nd.getroottree().getroot():  # Stop before the root node
                res.reverse()
                return ' '.join(res)
    

    它返回所有父节点的名称,作为一个空格分隔的列表,但是如果 某些节点有 item 属性,添加该属性的值 到这个列表。

    我注意到您只是以这种“特殊方式”来处理 item 属性, 所以这就是我选择上述解决方案的原因。

    但是,如果您对某些特殊处理有其他“例外” 属性,相应地改变上面的函数。

    然后打印所有叶子节点的上述“路径”和文本值:

    for nd in root.iter():
        if len(nd.getchildren()) == 0:  # Leaf nodes only
            print(f'{getParentNames(nd)}: {nd.text}')
    

    结果是:

    Audio DefaultVolume: 70
    Audio Input HDMI 3 Level: -12
    Audio Input HDMI 3 Mode: On
    Audio Input HDMI 3 VideoAssociation MuteOnInactiveVideo: On
    

    【讨论】:

      【解决方案3】:

      根据 Jack 的回答,我继续并完全实现了我相信您正在寻找的东西。遗憾的是,将 configuration.xml 文件转换为文本仍然会丢失备份中包含的一些条目,例如蓝牙设置和实验性功能。尽管如此,这个脚本仍然是从 configuration.xml 文件中创建许多 xCommand 的好方法。

      此脚本正确解释了 Cisco 用来区分不同接口的项目属性。当文本属性需要它们时,它还会通过在 valueSpaceRef 属性中查找“STR”来添加引号。希望这会有所帮助!

      from lxml import etree
      
      tree = etree.parse("configuration.xml")
      root = tree.getroot()
      for node in root.iter():
          if(len(node) == 0):
              isString = False
              if 'STR' in node.attrib['valueSpaceRef']:
                  isString = True
              path = tree.getpath(node)
              path = path.split('/')[2:]
              newpath = list()
              for item in path:
                  if('[' in item):
                      startindex = item.index('[') + 1
                      endindex = item.index(']')
                      number = item[startindex:endindex]
                      item = item[:-3] + " " + number
                  newpath.append(item)
              path = newpath
              path = ' '.join(path)
              if(node.text is None):
                  node.text = '""'
              elif(isString):
                  node.text = '"'+node.text+'"'
              path = path + ': ' + node.text
              print(path)
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2012-10-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-03-31
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多