使用python的zip和list理解将xml转换为cs

2024-04-20 05:53:02 发布

您现在位置:Python中文网/ 问答频道 /正文

我在这里已经看到了关于zip和魔法*的其他问题,这些问题帮助我理解了它是如何工作的。例如:

尽管我还得想一想到底发生了什么,但我现在有了更好的理解。所以我要实现的是将xml文档转换成csv。上面的最后一个链接与我想做的非常接近,但是我的源xml没有最一致的结构,这就是我遇到的困难。下面是我的源xml的一个示例(为了这个示例而简化):

<?xml version="1.0" encoding="utf-8"?>
<root>
    <child>
        <Name>John</Name>
        <Surname>Doe</Surname>
        <Phone>123456</Phone>
        <Phone>654321</Phone>
        <Fax>111111</Fax>
    </child>
    <child>
        <Name>Tom</Name>
        <Surname>Cat</Surname>
        <Phone>98765</Phone>
        <Phone>56789</Phone>
        <Phone>00000</Phone>
    </child>
</root>

如您所见,<child>下可以有2个或更多相同的元素。另外,如果某个元素没有值,它甚至不存在(就像第二个<child>上没有<Fax>)的元素。在

这是我目前拥有的代码:

^{pr2}$

虽然这给了我一个可以用来写csv的格式,但它有两个问题:

  1. 它跳过第二个子元素,因为它没有<Fax>元素(我想)。如果只通过设置{{CD5}}来搜索两个孩子中存在的元素,那么我有2个列表返回(很棒!)

  2. 第一个孩子实际上有2个电话号码,但只返回一个

根据我的测试,当zip*开始发挥作用时,东西开始消失。。。我怎么可能设置一个默认值,这样我就可以保留空值了?在

更新:为了更清楚地说明我要做什么,下面是预期的输出格式(带有分号分隔符的CSV,其中每个字段中的多个值用逗号分隔):

John;Joe;123456,654321;111111;
Tom;Cat;98765,56789;00000;;

谢谢!在


Tags: csvnameinchild元素示例phoneroot
2条回答

我把这个砍掉了。阅读csv模块的文档,如果需要更具体的格式,请相应地进行更改。在

from csv import DictWriter
from StringIO import StringIO
import xml.etree
from xml.etree import ElementTree

xml_str = \
'''
<?xml version="1.0" encoding="utf-8"?>
<root>
    <child>
        <Name>John</Name>
        <Surname>Doe</Surname>
        <Phone>123456</Phone>
        <Phone>654321</Phone>
        <Fax>111111</Fax>
    </child>
    <child>
        <Name>Tom</Name>
        <Surname>Cat</Surname>
        <Phone>98765</Phone>
        <Phone>56789</Phone>
        <Phone>00000</Phone>
    </child>
</root>
'''

root = ElementTree.parse(StringIO(xml_str.strip()))
entry_list = []
for child_tag in root.iterfind("child"):
    child_tags = child_tag.getchildren()

    tag_count = {}
    [tag_count.__setitem__(tag.tag, tag_count.get(tag.tag, 0) + 1) for tag in child_tags]

    m_count = dict([(key, 0) for (key, val) in filter(lambda (x, y): y > 1, tag_count.items())])

    enum = lambda x: ("%s%s" % (x.tag, (" %d" % m_count.setdefault(x.tag, m_count.pop(x.tag) + 1)) if(tag_count[x.tag] > 1) else ""), x.text)
    tmp_dict = dict([enum(tag) for tag in child_tags])

    entry_list.append(tmp_dict)

field_order = ["Name", "Surname", "Phone 1", "Phone 2", "Phone 3", "Fax"]
field_check = lambda q: field_order.index(q) if(field_order.count(q)) else sys.maxint

all_fields = list(reduce(lambda x, y: x | set(y.keys()), entry_list, set([])))
all_fields.sort(cmp=lambda x, y: field_check(x) - field_check(y))

with open("test.csv", "w") as file_h:
    writer = DictWriter(file_h, all_fields, restval="", extrasaction="ignore", dialect="excel", lineterminator="\n")
    writer.writerow(dict(zip(all_fields, all_fields)))
    writer.writerows(entry_list)

你说,关于你的第一个问题,“如果我只搜索两个孩子中都存在的元素。。。我有2个列表,“这意味着第二个子节点的输出缺失与两个child节点之间的交互有关。事实并非如此。您似乎忽略了zip的行为方面是,zip在用尽最短参数后停止处理其参数。在

考虑以下简化代码的输出:

for child in data:
    print [child.findall(x) for x in tags]

输出将是(省略内存地址):

^{pr2}$

注意,第二个列表有一个空的子列表(因为第二个子列表没有Fax节点)。这意味着,当您将这些子列表压缩在一起时,进程将立即停止并返回一个空列表;在第一次传递时,它已经用完了其中一个子列表。这就是为什么在输出中省略第二个子元素;它与子元素之间共享没有任何关系。在

zip行为的相同原理解释了第二个问题。请注意,上面的第一个输出列表由四个元素组成:长度为1的三个标记的列表,以及长度为2的包含两个phone元素的列表。当您将这些压缩在一起时,在耗尽任何子列表之后,该过程再次停止。在本例中,最短子列表的长度为1,因此结果只从phone子列表中提取一个元素。在

我不确定您希望输出的确切外观,但如果您只是尝试为每个子节点构造一个包含该节点中每个元素文本的列表,可以执行以下操作:

for child in data:
    print [x.text for x in child]

这将产生:

['John', 'Doe', '123456', '654321', '111111']
['Tom', 'Cat', '98765', '56789', '00000']

相关问题 更多 >