ElementTree 缺少获取第一个子节点的函数

0 投票
2 回答
3710 浏览
提问于 2025-04-19 19:26

我正在用Python写一个解析器,用来解析一个XML文件。为了做到这一点,我需要获取标签名,但在获取标签名的时候,除了标签名,我还得到了命名空间。同时,我还需要在很多地方获取第一个子节点,但在这个库(Elementtree)中我找不到相关的属性。我搜索了很多,但还是找不到怎么只获取标签名而不带命名空间的值,以及如何获取第一个子节点。试用的input.xml文件如下:

<?xml version="1.0"?>
<nf:rpc-reply xmlns:nf="urn:ietf:params:xml:ns:netconf:base:1.0" xmlns="http://www.cisco.com/nxos:1.0:ip">
 <nf:data>
  <show>
   <ip>
    <interface>
     <__XML__BLK_Cmd_ip_show_interface_command_brief>
      <__XML__OPT_Cmd_ip_show_interface_command_operational>
       <__XML__OPT_Cmd_ip_show_interface_command_vrf>
        <__XML__OPT_Cmd_ip_show_interface_command___readonly__>
         <__readonly__>
    <TABLE_intf>

        <ROW_intf>
                <vrf-name-out>default</vrf-name-out>
                <intf-name>Vlan10</intf-name>
                <prefix>9.1.1.1</prefix>
                <ip-disabled>FALSE</ip-disabled>
                <iod>104</iod>
                <proto-state>TRUE</proto-state>
                <link-state>TRUE</link-state>
                <admin-state>TRUE</admin-state>
               </ROW_intf>
        <ROW_intf>
                <vrf-name-out>default</vrf-name-out>
                <intf-name>Vlan23</intf-name>
                <prefix>23.1.1.9</prefix>
                <ip-disabled>FALSE</ip-disabled>
                <iod>103</iod>
                <proto-state>TRUE</proto-state>
                <link-state>TRUE</link-state>
                <admin-state>TRUE</admin-state>
             </ROW_intf>
             <ROW_intf>
                <vrf-name-out>default</vrf-name-out>
                <intf-name>Po1</intf-name>
                <prefix>4.9.1.2</prefix>
                <ip-disabled>FALSE</ip-disabled>
                <iod>111</iod>
                <proto-state>TRUE</proto-state>
                <link-state>TRUE</link-state>
                <admin-state>TRUE</admin-state>
             </ROW_intf>
             <ROW_intf>
                <vrf-name-out>default</vrf-name-out>
                <intf-name>Po2</intf-name>
                <prefix>5.9.1.2</prefix>
                <ip-disabled>FALSE</ip-disabled>
                <iod>112</iod>
                <proto-state>TRUE</proto-state>
                <link-state>TRUE</link-state>
                <admin-state>TRUE</admin-state>
             </ROW_intf>
             <ROW_intf>
                <vrf-name-out>default</vrf-name-out>
                <intf-name>Po3</intf-name>
                <prefix>6.9.1.2</prefix>
                <ip-disabled>FALSE</ip-disabled>
                <iod>113</iod>
                <proto-state>TRUE</proto-state>
                <link-state>TRUE</link-state>
                <admin-state>TRUE</admin-state>
             </ROW_intf>
    </TABLE_intf>
    </__readonly__>
        </__XML__OPT_Cmd_ip_show_interface_command___readonly__>
       </__XML__OPT_Cmd_ip_show_interface_command_vrf>
      </__XML__OPT_Cmd_ip_show_interface_command_operational>
     </__XML__BLK_Cmd_ip_show_interface_command_brief>
    </interface>
   </ip>
  </show>
 </nf:data>
</nf:rpc-reply>

我正在写的代码(还在完成中,给出这个代码只是为了让你了解我想要得到的输出):

import xml.etree.ElementTree as ET

print "before skip()"
def skip(root):

    print "rootname.tag in skip == %s" %(root.tag)
        if(root.tag == "readonly"):  // here if I compare with the namespace then returns the value
        print "skip -> if"
        return root
    else:
        print "skip-> else"
        root = root[0]
        print "new root %s" %root
        return skip(root)

xmlDoc = ET.parse("trialinput.xml")  
dict = {}
print "accessing the root"
root = xmlDoc.getroot()

print "rootname == %s" %root.tag
pointerOfReadonly = skip(root)

print "pointerOfReadonly.tag %s" %pointerOfReadonly.tag

print "accessing child of readonly"
tableInitiationPointer = pointerOfReadonly[0]   // Here how to get the first child of readonly tag?


#print "accessing children of table"
#allRows = tableInitiationPointer.childNodes

Print "no of rows in the table = %s" %tableInitiationPointer.len("ROW_intf")// Returning none due to above non ability to find the tag
for row in tableInitiationPointer:
    for subrows in row:
        key = subrows.tag
        value = subrows.value
                dict[key]=value

2 个回答

0

你缺少的是正确使用命名空间的知识。

因为你的xml文件里有命名空间,所以节点会带上这些命名空间,ET(ElementTree)会连同tag一起返回命名空间。

试试下面的方式来提取正确的节点:

xmlDoc = ET.parse("trialinput.xml")  
dict = {}
print "accessing the root"
root = xmlDoc.getroot()

print "rootname == %s" %root.tag
pointerOfReadonly = root.findall(".//{http://www.cisco.com/nxos:1.0:ip}__readonly__")
print pointerOfReadonly

前两个节点,rpc-replydata,会包含命名空间"urn:ietf:params:xml:ns:netconf:base:1.0",其他的节点会包含"http://www.cisco.com/nxos:1.0:ip"

想了解xpath的语法和用法,可以查看这份文档

所以,当你搜索节点__readonly__时,需要在findall()方法中明确指定命名空间,像这样:

pointerOfReadonly = root.findall(".//{http://www.cisco.com/nxos:1.0:ip}__readonly__")
print pointerOfReadonly

这会打印出所有找到的节点列表(在我们的例子中只有一个):

[<Element '{http://www.cisco.com/nxos:1.0:ip}__readonly__' at 0x20b4990>]

接下来,你需要用类似的方法去搜索其他节点。比如,如果你想找节点ROW_intf,可以这样做:

pointerOfRowIntf = root.findall(".//{http://www.cisco.com/nxos:1.0:ip}ROW_intf")
print pointerOfRowIntf

这样就会打印出找到的所有5个节点:

[<Element '{http://www.cisco.com/nxos:1.0:ip}ROW_intf' at 0x20d4990>, <Element '{http://www.cisco.com/nxos:1.0:ip}ROW_intf' at 0x20d4bb0>, <Element '{http://www.cisco.com/nxos:1.0:ip}ROW_intf' at 0x20d4db0>, <Element '{http://www.cisco.com/nxos:1.0:ip}ROW_intf' at 0x20d4f90>, <Element '{http://www.cisco.com/nxos:1.0:ip}ROW_intf' at 0x20dc190>]
1

要去掉标签上的命名空间:

import re
def GetBareTag(tag): return re.sub(r'{.*?}', '', tag)

要找到第一个子元素:

def GetFirstChild(e): return e[0] if len(e) else None

撰写回答