为了账号安全,请及时绑定邮箱和手机立即绑定

将 XML 文件解析为 CSV,无需硬编码值

将 XML 文件解析为 CSV,无需硬编码值

慕的地8271018 2023-10-31 14:29:16
我想知道是否有一种方法可以解析 XML 并基本上获取所有标签(或尽可能多)并将它们放入列中而无需硬编码。例如我的 xml 中的 eventType 标记。我希望它最初创建一个名为“eventType”的列,并将值放在该列下方。它解析的每个“eventType”标签都会将其放入同一列中。一般来说,我试图让它看起来像这样:https://i.stack.imgur.com/z0L8z.png 这是 XML 示例:<?xml version="1.0" encoding="UTF-8"?><faults version="1" xmlns="urn:nortel:namespaces:mcp:faults" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:nortel:namespaces:mcp:faults NortelFaultSchema.xsd ">    <family longName="1OffMsgr" shortName="OOM"/>    <family longName="ACTAGENT" shortName="ACAT">        <logs>           <log>                <eventType>RES</eventType>                <number>1</number>                <severity>INFO</severity>                <descTemplate>                     <msg>Accounting is enabled upon this NE.</msg>               </descTemplate>               <note>This log is generated when setting a Session Manager's AM from &lt;none&gt; to a valid AM.</note>               <om>On all instances of this Session Manager, the &lt;NE_Inst&gt;:&lt;AM&gt;:STD:acct OM row in the  StdRecordStream group will appear and start counting the recording units sent to the configured AM.                   On the configured AM, the &lt;NE_inst&gt;:acct OM rows in RECSTRMCOLL group will appear and start counting the recording units received from this Session Manager's instances.               </om>            </log>           <log>                <eventType>RES</eventType>                <number>2</number>                <severity>ALERT</severity>                <descTemplate>                     <msg>Accounting is disabled upon this NE.</msg>               </descTemplate>
查看完整描述

1 回答

?
白衣染霜花

TA贡献1796条经验 获得超10个赞

您可以构建一个列表列表来表示表的行。每当需要新行时,都会使用默认的所有已知列构建一个新列表"",并将其附加到外部列表的底部。当需要插入新列时,只需旋转现有内部列表并附加默认""单元格即可。保留已知列名称的映射以在行中建立索引。现在,当您浏览事件时,您可以使用标记名称来查找行索引并将其值添加到表中的最新行。


看起来您想要“log”和“alarm”标签,但我编写了元素选择器来获取具有“eventType”子元素的任何元素。由于“longName”和“shortName”对于给定的所有事件都是通用的,因此有一个外部循环来获取这些事件并将其应用于表的每个新行。我切换到xpath这样我就可以设置命名空间并更简洁地编写选择器。个人喜好,但我认为它使 xpath 更具可读性。


import csv

import lxml.etree

from lxml.etree import QName

import operator


class ExpandingTable:

    """A 2 dimensional table where columns are exapanded as new column

    types are discovered"""


    def __init__(self):

        """Create table that can expand rows and columns"""

        self.name_to_col = {}

        self.table = []

    

    def add_column(self, name):

        """Add column named `name` unless already included"""

        if name not in self.name_to_col:

            self.name_to_col[name] = len(self.name_to_col)

            for row in self.table:

                row.append('')

    

    def add_cell(self, name, value):

        """Add value to named column in the current row"""

        if value:

            self.add_column(name)

            self.table[-1][self.name_to_col[name]] = value.strip().replace("\r\n", " ")

            

    def new_row(self):

        """Create a new row and make it current"""

        self.table.append([''] * len(self.name_to_col))


    def header(self):

        """Gather discovered column names into a header list"""

        idx_1 = operator.itemgetter(1)

        return [name for name, _ in sorted(self.name_to_col.items(), key=idx_1)]


    def prepend_header(self):

        """Gather discovered column names into a header and

        prepend it to the list"""

        self.table.insert(0, self.header())


def events_to_table(elem):

    """ Builds table from <family> child elements and their contained alarms and

    logs."""

    ns = {"f":"urn:nortel:namespaces:mcp:faults"}

    table = ExpandingTable()

    for family in elem.xpath("f:family", namespaces=ns):

        longName = family.get("longName")

        shortName = family.get("shortName")

        for event in family.xpath("*/*[f:eventType]", namespaces=ns):

            table.new_row()

            table.add_cell("longName", longName)

            table.add_cell("shortName", shortName)

            for cell in event:

                tag = QName(cell.tag).localname

                if tag == "severities":

                    tag = "severity"

                    text = ",".join(severity.text for severity in cell.xpath("*"))

                    print("severities", repr(text))

                else:

                    text = cell.text

                table.add_cell(tag, text)

    table.prepend_header()

    return table.table

    

def main(filename):

    doc = lxml.etree.parse(filename)

    table = events_to_table(doc.getroot())

    with open('test.csv', 'w', newline='', encoding='utf-8') as fileobj:

        csv.writer(fileobj).writerows(table)


main('test.xml')


查看完整回答
反对 回复 2023-10-31
  • 1 回答
  • 0 关注
  • 87 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信