【问题标题】:influence data structure for python xml parser影响python xml解析器的数据结构
【发布时间】:2023-01-13 01:13:48
【问题描述】:

我今天要把剩下的头发拔掉。

我正在使用 NETCONF 和 Juniper Junos 设备并努力了解如何实现某些目标。

问题是 XML 配置输出以解析器不将它们与其节点相关联的方式格式化注释。

以下是使用命令show configuration snmp | display xml 的设备中的一些示例 xml,删除了垃圾以使其易于理解。

<rpc-reply xmlns:junos="http://xml.juniper.net/junos/21.2R0/junos">
    <configuration>
        <snmp>
            <client-list>
                <name>SNMP-POLLER-LIST</name>
                <junos:comment>/* snmp-poller-1 */</junos:comment>
                <client-address-list>
                    <name>1.1.1.1/32</name>
                </client-address-list>
                <client-address-list>
                    <name>1.1.1.2/32</name>
                </client-address-list>
                <junos:comment>/* snmp-poller-2 */</junos:comment>
                <client-address-list>
                    <name>2.2.2.2/32</name>
                </client-address-list>
            </client-list>
        </snmp>
    </configuration>
</rpc-reply>

这基本上是一个 SNMP 访问的访问列表,不是所有的都有注释,这是 Juniper 上的配置的样子

client-list CF-SNMP-POLLER-LIST {
    /* snmp-poller-1 */
    1.1.1.1/32;
    1.1.1.2/32;
    /* snmp-poller-2 */
    2.2.2.2/32;
    }

当我在 Python 3.8 中使用 lxml 或 xmltodict 解析 XML 时,它会生成如下所示的字典,它将 cmet 添加到与客户端列表主机没有关联的单独列表中。

{
    "name": "SNMP-POLLER-LIST",
    "comment": [
        "/* snmp-poller-1 */",
        "/* snmp-poller-2 */"
    ],
    "client-address-list": [
        {
            "name": "1.1.1.1/32"
        },
        {
            "name": "3.3.3.3/32"
        },
        {
            "name": "2.2.2.2/32"
        }
    ]
}

我的问题是,有没有办法影响解析器将注释加入到客户端地址列表项中?或者扩展解析器的简单方法?

例如:

{
    "name": "SNMP-POLLER-LIST",
    "client-address-list": [
        {
            "name": "1.1.1.1/32",
            "comment": "/* snmp-poller-1 */"
        },
        ...
    ]
}

我希望这是有道理的

编辑:

这是我在 python repl 控制台中找到的 lxml 代码示例 这可能是某些事情的开始,现在我已经离开并回到它。

from lxml import etree
with open("test.xml", "rb") as fh:
    tree = etree.parse(fh)

root = tree.getroot()
rootchildren = root.iter()
for i in rootchildren:
    print(f"tag: {i.tag} text: {i.text}")

我所有的其他代码都是在load xml from file 然后是send xml string to xmltodict 上的版本

xmltodict 可能是我的问题!

【问题讨论】:

  • 您可以编辑您的问题并添加用于获取错误输出的 lxml 代码吗?
  • @JackFleeting 我认为我误会了 lxml!这是其中的一天,我一直在 python 控制台中进行所有测试并且它有点混乱,不过我会添加一些代码

标签: python xml juniper netconf


【解决方案1】:

干得好。 (不需要 lxml - 只需核心 python)

import xml.etree.ElementTree as ET
from dataclasses import dataclass
from typing import List, Optional

xml = '''<rpc-reply xmlns:junos="http://xml.juniper.net/junos/21.2R0/junos">
    <configuration>
        <snmp>
            <client-list>
                <name>SNMP-POLLER-LIST</name>
                <junos:comment>/* snmp-poller-1 */</junos:comment>
                <client-address-list>
                    <name>1.1.1.1/32</name>
                </client-address-list>
                <client-address-list>
                    <name>1.1.1.2/32</name>
                    <name>12.1.145.2/64</name>
                </client-address-list>
                <junos:comment>/* snmp-poller-2 */</junos:comment>
                <client-address-list>
                    <name>2.2.2.2/32</name>
                </client-address-list>
            </client-list>
        </snmp>
    </configuration>
</rpc-reply>'''


@dataclass
class Entry:
    address_list: List[str]
    comment: Optional[str]


@dataclass
class Config:
    name: str
    entries: List[Entry]


root = ET.fromstring(xml)
client_list = root.find('.//client-list')
name = client_list.find('name').text

temp = []
for entry in client_list:
    if entry.tag not in ['client-address-list', '{http://xml.juniper.net/junos/21.2R0/junos}comment']:
        continue
    else:
        create_new_entry = False
        if entry.tag == '{http://xml.juniper.net/junos/21.2R0/junos}comment':
            comment = entry.text
        else:
            address_list = [a.text for a in entry.findall('name')]
            create_new_entry = True
        if create_new_entry:
            temp.append(Entry(address_list, comment))
            create_new_entry = False
            comment = None

config: Config = Config(name, temp)
print(config)

输出

Config(name='SNMP-POLLER-LIST', entries=[Entry(address_list=['1.1.1.1/32'], comment='/* snmp-poller-1 */'), Entry(address_list=['1.1.1.2/32', '12.1.145.2/64'], comment=None), Entry(address_list=['2.2.2.2/32'], comment='/* snmp-poller-2 */')])

【讨论】:

    猜你喜欢
    • 2017-08-01
    • 2013-08-23
    • 1970-01-01
    • 2011-02-24
    • 1970-01-01
    • 2013-08-12
    • 2015-01-15
    • 2017-04-12
    • 1970-01-01
    相关资源
    最近更新 更多