使用ElementTree在Python中解析嵌套的XML数据

时间:2012-11-14 03:57:37

标签: python xml elementtree

更新:寻求解决方案

此代码:

tree = ET.parse(assetsfilename)
root = tree.getroot()
assets = {}

def find_rows(rowset, container):
    for row in rowset.findall("row"):
        singleton = int((row.get('singleton')))
        flag = int((row.get('flag')))
        quantity = int((row.get('quantity')))
        typeID = int((row.get('typeID')))
        locationID = int((row.get('locationID', '0')))
        itemID = int((row.get('itemID')))
        dkey = (singleton, flag, quantity, typeID, locationID, itemID)

        container[dkey] = {}
        child_rowset = row.find("rowset")
        if child_rowset is not None:
            find_rows(child_rowset, container[dkey])

first_rowset = root.find('.//rowset[@name="assets"]')
find_rows(first_rowset, assets)
#print singleton, flag, quantity, typeID, locationID, itemID
pp = pprint.PrettyPrinter(indent=4)
pp.pprint(assets)

给出这个输出:

{   (0, 4, 1, 3317, 61000419, 1000913922710L): {   },
    (0, 4, 1, 6159, 60003463, 1007025519384L): {   },
    (0, 4, 1, 7669, 60000361, 1007215573625L): {   },
    (0, 4, 1, 23566, 61000419, 1000992661686L): {   },
    (1, 4, 1, 51, 60001345, 1004073218074L): {   },
    (1, 4, 1, 51, 60001345, 1004073218075L): {   },
    (1, 4, 1, 596, 60003337, 1007908184113L): {   (0, 5, 1, 34, 0, 1007908184132
L): {   },
                                                  (1, 27, 1, 3634, 0, 1007908184
129L): {   },
                                                  (1, 28, 1, 3651, 0, 1007908184
130L): {   }},
    (1, 4, 1, 3766, 61000419, 1000973178550L): {   (0, 5, 25, 16273, 0, 10009731
88870L): {   },
                                                   (1, 27, 1, 21096, 0, 10006872
93796L): {   }}}

这基本上将嵌套的dict添加到我已经拥有的dict的末尾,并用来自子节点的数据填充它(如果存在的话)。但理想情况下,父数据和子数据都将位于主dict中,而dict末尾的额外字段将包含父项的itemID(如果该行是子行)或为空(如果该项目)是父行或没有任何子项的行。)

问题

我正在尝试将嵌套的.xml文件中的数据读入某种字典,以便我可以以其他格式输出它(我当前的目标是sqlite3和sqlite .db文件,但这不是我的问题。)我可以阅读所有主要的数据级别,但我无法弄清楚如何读取嵌套数据(如果存在)。

数据

以下是.xml文件示例:

<?xml version='1.0' encoding='UTF-8'?>
<eveapi version="2">
  <currentTime>2012-11-14 03:26:35</currentTime>
  <result>
    <rowset name="assets" key="itemID" columns="itemID,locationID,typeID,quantity,flag,singleton">
      <row itemID="1007215573625" locationID="60000361" typeID="7669" quantity="1" flag="4" singleton="0" />
      <row itemID="1004073218074" locationID="60001345" typeID="51" quantity="1" flag="4" singleton="1" rawQuantity="-1" />
      <row itemID="1004073218075" locationID="60001345" typeID="51" quantity="1" flag="4" singleton="1" rawQuantity="-1" />
      <row itemID="1007908184113" locationID="60003337" typeID="596" quantity="1" flag="4" singleton="1" rawQuantity="-1">
        <rowset name="contents" key="itemID" columns="itemID,typeID,quantity,flag,singleton">
          <row itemID="1007908184129" typeID="3634" quantity="1" flag="27" singleton="1" rawQuantity="-1" />
          <row itemID="1007908184130" typeID="3651" quantity="1" flag="28" singleton="1" rawQuantity="-1" />
          <row itemID="1007908184132" typeID="34" quantity="1" flag="5" singleton="0" />
        </rowset>
      </row>
      <row itemID="1007025519384" locationID="60003463" typeID="6159" quantity="1" flag="4" singleton="0" />
      <row itemID="1000913922710" locationID="61000419" typeID="3317" quantity="1" flag="4" singleton="0" />
      <row itemID="1000973178550" locationID="61000419" typeID="3766" quantity="1" flag="4" singleton="1" rawQuantity="-1">
        <rowset name="contents" key="itemID" columns="itemID,typeID,quantity,flag,singleton">
          <row itemID="1000687293796" typeID="21096" quantity="1" flag="27" singleton="1" rawQuantity="-1" />
          <row itemID="1000973188870" typeID="16273" quantity="25" flag="5" singleton="0" />
        </rowset>
      </row>
      <row itemID="1000992661686" locationID="61000419" typeID="23566" quantity="1" flag="4" singleton="0" />
    </rowset>
  </result>
  <cachedUntil>2012-11-14 07:05:29</cachedUntil>
</eveapi>

请注意有些物品是否有嵌套在它们下面的儿童物品,但有些物品却没有,儿童数量(如果有的话)没有固定(所以一个物品可以有3个孩子,另外2个孩子,而其他物品则没有孩子。)

(对于那些好奇的人来说,这些数据来自于名为EVE Online的在线游戏的完整id键资产列表API。)

我能得到什么

我可以得到这段代码:

import xml.etree.ElementTree as ET

tree = ET.parse(assetsfilename)
root = tree.getroot()

singleton = []
flag = []
quantity = []
typeID = []
locationID = []
itemID = []
assets = {}
for row in root.findall(".//*[@name='assets']/row"):
    singleton.append (int((row.get('singleton'))))
    flag.append (int((row.get('flag'))))
    quantity.append (int((row.get('quantity'))))
    typeID.append (int((row.get('typeID'))))
    locationID.append (int((row.get('locationID'))))
    itemID.append (int((row.get('itemID'))))
assets = zip(singleton, flag, quantity, typeID, locationID, itemID)
print singleton, flag, quantity, typeID, locationID, itemID
print assets

要在屏幕上输出:

[0, 1, 1, 1, 0, 0, 1, 0] [4, 4, 4, 4, 4, 4, 4, 4] [1, 1, 1, 1, 1, 1, 1, 1] [7669
, 51, 51, 596, 6159, 3317, 3766, 23566] [60000361, 60001345, 60001345, 60003337,
 60003463, 61000419, 61000419, 61000419] [1007215573625L, 1004073218074L, 100407
3218075L, 1007908184113L, 1007025519384L, 1000913922710L, 1000973178550L, 100099
2661686L]
[(0, 4, 1, 7669, 60000361, 1007215573625L), (1, 4, 1, 51, 60001345, 100407321807
4L), (1, 4, 1, 51, 60001345, 1004073218075L), (1, 4, 1, 596, 60003337, 100790818
4113L), (0, 4, 1, 6159, 60003463, 1007025519384L), (0, 4, 1, 3317, 61000419, 100
0913922710L), (1, 4, 1, 3766, 61000419, 1000973178550L), (0, 4, 1, 23566, 610004
19, 1000992661686L)]

请注意这是如何读取所有以<row itemID=开头的主级别行,但它没有得到嵌套行(我最好还要显示它与上面的父项ID相关联。 )

期望输出

我对这里的建议持开放态度,但这是一个选择。我可以将主级别行解析为dict(就像我已经拥有的那样)然后创建另一个包含来自次级行的数据的dict,并添加一个额外的部分来记录它是哪个itemID。另一种选择是将子级行中的数据添加到我已经可以制作的主要词典中,只需添加一个额外的字段,例如NullNone。我有一个父母,并为有父母的项目提供父母的itemID。

1 个答案:

答案 0 :(得分:1)

这个片段(稍大)递归地将xml结构解析为嵌套字典,就像你描述了一个可能的解决方案。它适用于您提供的示例,但我认为无论如何它都适用于实时数据。如果没有别的,你可以使用这个想法。

UPDATE:好的,这个更新版本将itemID存储为键,并添加parent_id作为附加的dict属性,如果这是所需的行为,请检查它:

import xml.etree.ElementTree as ET

from StringIO import StringIO
tree = ET.parse(StringIO(xml_data))
root = tree.getroot()

assets = {}

def find_rows(rowset, parent_id):
    for row in rowset.findall("row"):
        singleton = int((row.get('singleton')))
        flag = int((row.get('flag')))
        quantity = int((row.get('quantity')))
        typeID = int((row.get('typeID')))
        locationID = int((row.get('locationID', '0')))
        itemID = int((row.get('itemID')))

        assets[itemID] = {'singleton': singleton,
                          'flag': flag,
                          'quantity': quantity,
                          'typeID': typeID,
                          'locationID': locationID,
                          'parentID': parent_id}
        child_rowset = row.find("rowset")
        if child_rowset is not None:
            find_rows(child_rowset, itemID)

first_rowset = root.find('.//rowset[@name="assets"]')
find_rows(first_rowset, None)