Python:如何从平面数据结构创建嵌套XML

时间:2011-02-12 19:37:21

标签: python xml serialization

我想用python的dicts列表创建嵌套的XML(作为字符串):

toc = [
  {'entryno': 1, 'level': 1, 'pageno': 17, 'title': 'title a'},
  {'entryno': 2, 'level': 2, 'pageno': 19, 'title': 'title b'},
  {'entryno': 3, 'level': 1, 'pageno': 25, 'title': 'title c'},]

级别表示嵌套级别,我的dict中可能有超过2个级别。 toc具有固定的排序(通过entryno)。级别只能从一个条目增加到下一个条目,但可能会减少一个以上。 这是我想要创建的嵌套示例XML:

<entry id="1">
  <pageno>17</pageno>
  <title>title a</title>
  <entry id="2">
    <pageno>19</pageno>
    <title>title b</title>
  </entry>
</entry>
<entry id="3">
  <pageno>25</pageno>
  <title>title c</title>
</entry>

我尝试用string.Template()解决这个问题并迭代toc,但我仍然坚持创建XML的嵌套部分。我怀疑解决方案将是一些递归的东西。

作为编程初学者,我不仅对解决方案感兴趣,而且还在您的思路中解决这个问题!

3 个答案:

答案 0 :(得分:4)

使用ElementTree API的非丑陋解决方案。 Python包含一个实现,如xml.etree。[c] ElementTree。另一个是lxml.etree,它提供了更多功能,包括漂亮打印输出。

# import xml.etree.cElementTree as et
import lxml.etree as et
import sys

toc = [
  {'entryno': 1, 'level': 1, 'pageno': 17, 'title': 'title a'},
  {'entryno': 2, 'level': 2, 'pageno': 19, 'title': 'title b'},
  {'entryno': 3, 'level': 1, 'pageno': 25, 'title': 'Smith & Wesson'},
  {'entryno': 4, 'level': 2, 'pageno': 27, 'title': '<duct tape>'},
  {'entryno': 5, 'level': 2, 'pageno': 29, 'title': u'\u0404'},
  ]

root = et.Element("root")
tree = et.ElementTree(root)
parent = {0: root}
for entry in toc:
    level = entry['level']
    entryno = entry['entryno']
    # create the element and link it to its parent
    elem = et.SubElement(parent[level - 1], "entry", {'id': str(entryno)})
    # create children to hold the other data items
    for k, v in entry.iteritems():
        if k in ('entryno', 'level'): continue
        child = et.SubElement(elem, k)
        child.text = unicode(v)
    # record current element as a possible parent
    parent[level] = elem
# tree.write(sys.stdout)
tree.write(sys.stdout, pretty_print=True)

答案 1 :(得分:1)

假设您知道如何创建XML。

假设数据嵌套在前一个节点中,数据中的“级别”会增加,并且只会增加1.如果级别降低,这意味着您不再讨论当前节点,而是关于上面的一些节点; level == 1表示'在文档级附加'。

  • 要处理增加级别,您只需要跟踪上一个节点。如果级别增加1,则创建一个新节点并使其成为上一个节点的子节点。

  • 要处理相同的级别,您需要记住以前创建的节点的父级。您将新节点附加到该父节点,因为它是前一节点的对等节点。

  • 要处理降低级别,您需要从上一个节点退回几个步骤,以便您处于正确的级别。你能看到一种模式吗?

您真的需要记住从文档级到先前创建的节点的整个链。如果next_node.level == previous_node.level + 1,则将其附加到链的末尾。否则,您将链上的previous_node.level - next_node.level + 1项退回并使用该节点作为父节点。我们假设0级是文档级别。

一些代码来说明这一点:

def nest(input):
    ret = {'level': 0} # 'document level'
    path = [ret]
    for item in input:
        node = dict(item) # a copy of item, lest we alter input
        old_level = path[-1]['level'] # last element's
        new_level = node['level']
        delta = new_level - old_level - 1
        if delta < 0:
            path = path[:delta]
        children_list = path[-1].get('_children', None) or []
        children_list.append(node)
        path[-1]['_children'] = children_list
        path.append(node)
    return ret

from pprint import PrettyPrinter
pr = PrettyPrint(indent=2).pprint
pr(nest(toc))    

你看到了

{ '_children': [ { '_children': [ { 'entryno': 2,
                                'level': 2,
                                'pageno': 19,
                                'title': 'title b'}],
               'entryno': 1,
               'level': 1,
               'pageno': 17,
               'title': 'title a'},
             { 'entryno': 3, 'level': 1, 'pageno': 25, 'title': 'title c'}],
  'level': 0}

_children下,我们列出了嵌套节点。

答案 2 :(得分:0)

toc = [
  {'entryno': 1, 'level': 1, 'pageno': 17, 'title': 'title a'},
  {'entryno': 2, 'level': 2, 'pageno': 18, 'title': 'title d'},
  {'entryno': 3, 'level': 3, 'pageno': 19, 'title': 'title e'},
  {'entryno': 4, 'level': 4, 'pageno': 20, 'title': 'title b'},
  {'entryno': 5, 'level': 5, 'pageno': 25, 'title': 'title c'},]

blevel=0
ret=""
for i in toc:
  while blevel >= i['level']:
    ret += "%s</entry>\n" % (" " * blevel)
    blevel-=1
  blevel=i['level']
  ident=" " * i['level']
  ret += "%s<entry id=\"%i\">\n" % (ident, i['entryno'])
  ident+=" "
  for a in i:
    if not a in ['entryno','level']:
      ret += "%s<%s>%s</%s>\n" % (ident,a,i[a],a)
while blevel > 0:
  ret += "%s</entry>\n" % (" " * blevel)
  blevel-=1

print ret