2010-07-20 2 views
4

Мне нужно использовать Python 2.4.4 для преобразования XML в словарь Python и из него. Все, что мне нужно, это имена узлов и значения, я не беспокоюсь об атрибутах, потому что XML-анализ не существует. Я не могу использовать ElementTree, потому что это недоступно для 2.4.4, и я не могу использовать сторонние библиотеки из-за моей рабочей среды. Что для меня проще всего сделать? Есть ли хорошие отрывки?XML в словарь Python

Кроме того, если нет простого способа сделать это, существуют ли альтернативные форматы сериализации, для которых Python 2.4.4 имеет встроенную поддержку?

ответ

3

Вопрос Serialize Python dictionary to XML перечисляет некоторые способы сериализации XML. Что касается альтернативных форматов сериализации, я думаю, что pickle модуль - отличный инструмент для этого.

+1

по этому вопросу 3/4 из них являются внешние библиотеки, которая не является вариантом для меня. Последний маршализует XML-документ, который на самом деле не тот, который я ищу, но он, кажется, сначала преобразует его в словарь, поэтому я посмотрю. – Liam

1

Ссылка Grey включает некоторые решения, которые выглядят довольно прочными. Если вы хотите перевернуть свой собственный, вы можете использовать элемент childNode xml.dom.node рекурсивно, заканчивая, когда node.childNode = None.

5

Недавно я написал код для перевода XML в структуру данных python, хотя мне приходилось обрабатывать атрибуты. Я использовал xml.dom.minidom, а не ElementTree, по той же причине. Я на самом деле не тестировал это на Python 2.4.4, но думаю, что это сработает. Я не писал обратный генератор XML, хотя вы, вероятно, можете использовать функцию lispy_string, которую я включил для этого.

Я также включил некоторые ярлыки, специфичные для приложения, которое я писал (объяснено в docstring), но вы можете найти эти ярлыки полезными также, из-за его звуков. По сути, XML-дерево технически переводится в словарь списков словарей списков словарей списков и т. Д. Я опускаю создание промежуточных списков, если они не нужны, поэтому вы можете ссылаться на элементы на dictname[element1][element2], а не на dictname[element1][0][element2][0] и так далее.

Атрибут обработки немного kludgy, я настоятельно рекомендую прочитать код, прежде чем делать что-либо с атрибутами.

import sys 
from xml.dom import minidom 

def dappend(dictionary, key, item): 
    """Append item to dictionary at key. Only create a list if there is more than one item for the given key. 
    dictionary[key]=item if key doesn't exist. 
    dictionary[key].append(item) if key exists.""" 
    if key in dictionary.keys(): 
     if not isinstance(dictionary[key], list): 
      lst=[] 
      lst.append(dictionary[key]) 
      lst.append(item) 
      dictionary[key]=lst 
     else: 
      dictionary[key].append(item) 
    else: 
     dictionary.setdefault(key, item) 

def node_attributes(node): 
    """Return an attribute dictionary """ 
    if node.hasAttributes(): 
     return dict([(str(attr), str(node.attributes[attr].value)) for attr in node.attributes.keys()]) 
    else: 
     return None 

def attr_str(node): 
    return "%s-attrs" % str(node.nodeName) 

def hasAttributes(node): 
    if node.nodeType == node.ELEMENT_NODE: 
     if node.hasAttributes(): 
      return True 
    return False 

def with_attributes(node, values): 
    if hasAttributes(node): 
     if isinstance(values, dict): 
      dappend(values, '#attributes', node_attributes(node)) 
      return { str(node.nodeName): values } 
     elif isinstance(values, str): 
      return { str(node.nodeName): values, 
        attr_str(node): node_attributes(node)} 
    else: 
     return { str(node.nodeName): values } 

def xmldom2dict(node): 
    """Given an xml dom node tree, 
    return a python dictionary corresponding to the tree structure of the XML. 
    This parser does not make lists unless they are needed. For example: 

    '<list><item>1</item><item>2</item></list>' becomes: 
    { 'list' : { 'item' : ['1', '2'] } } 
    BUT 
    '<list><item>1</item></list>' would be: 
    { 'list' : { 'item' : '1' } } 

    This is a shortcut for a particular problem and probably not a good long-term design. 
    """ 
    if not node.hasChildNodes(): 
     if node.nodeType == node.TEXT_NODE: 
      if node.data.strip() != '': 
       return str(node.data.strip()) 
      else: 
       return None 
     else: 
      return with_attributes(node, None) 
    else: 
     #recursively create the list of child nodes 
     childlist=[xmldom2dict(child) for child in node.childNodes if (xmldom2dict(child) != None and child.nodeType != child.COMMENT_NODE)] 
     if len(childlist)==1: 
      return with_attributes(node, childlist[0]) 
     else: 
      #if False not in [isinstance(child, dict) for child in childlist]: 
      new_dict={} 
      for child in childlist: 
       if isinstance(child, dict): 
        for k in child: 
         dappend(new_dict, k, child[k]) 
       elif isinstance(child, str): 
        dappend(new_dict, '#text', child) 
       else: 
        print "ERROR" 
      return with_attributes(node, new_dict) 

def load(fname): 
    return xmldom2dict(minidom.parse(fname)) 

def lispy_string(node, lst=None, level=0): 
    if lst==None: 
     lst=[] 
    if not isinstance(node, dict) and not isinstance(node, list): 
     lst.append(' "%s"' % node) 
    elif isinstance(node, dict): 
     for key in node.keys(): 
      lst.append("\n%s(%s" % (spaces(level), key)) 
      lispy_print(node[key], lst, level+2) 
      lst.append(")") 
    elif isinstance(node, list): 
     lst.append(" [") 
     for item in node: 
      lispy_print(item, lst, level) 
     lst.append("]") 
    return lst 

if __name__=='__main__': 
    data = minidom.parse(sys.argv[1]) 

    d=xmldom2dict(data) 

    print d 
+0

Это был отличный пример. Благодаря! – rsmoorthy

+0

Я немного изменил это, добавив дополнительную функциональность, и он находится здесь, в http://pastebin.com/rtDQsR2j – rsmoorthy

+0

awesome! Благодаря! –

2

Dicts in python не заказываются, помните об этом. У меня очень простой код, который мал и не требует каких-либо внешних модулей. Плохо то, что он не поддерживает каких-либо атрибутов XML, но вы сказали

Я не беспокоюсь об атрибутах

, так вот она:

def d2x(d, root="root"): 

    op = lambda tag: '<' + tag + '>' 
    cl = lambda tag: '</' + tag + '>\n' 
    ml = lambda v,xml: xml + op(key) + str(v) + cl(key) 

    xml = op(root) + '\n' if root else "" 

    for key,vl in d.iteritems(): 
     vtype = type(vl) 
     if vtype is list: 
      for v in vl: 
       xml = ml(v,xml)   
     if vtype is dict: xml = ml('\n' + d2x(vl,None),xml)   
     if vtype is not list and vtype is not dict: xml = ml(vl,xml) 

    xml += cl(root) if root else "" 

    return xml 

Пример использования:

mydict = { 
"boolean":False, 
"integer":12, 
"float":3.1, 
"listitems":["item1","item2"], 
"string":"Hello world", 
"dictionary":{ 
    "key1":1, 
    "key2":2, 
    "dictindict":{ 
       "a":"aaa", 
       "b":"bbb" 
       } 
      } 
} 
print d2x (mydict,"superxml") 

Это будет печатать:

<superxml> 
<string>Hello world</string> 
<dictionary> 
<key2>2</key2> 
<key1>1</key1> 
<dictindict> 
<a>aaa</a> 
<b>bbb</b> 
</dictindict> 
</dictionary> 
<float>3.1</float> 
<listitems>item1</listitems> 
<listitems>item2</listitems> 
<boolean>False</boolean> 
<integer>12</integer> 
</superxml> 
1

Для сериализации Python dict для XML следующий класс Python хорошо работает для меня. В некоторых других решениях это имеет то преимущество, что оно довольно простое и что оно делает правильное кодирование XML. Сценарий основан на this answer. Он имеет только одно расширение: передавая конструктору конструктор list_mappings, вы можете указать, как один элемент списка (child внутри атрибута children в приведенном ниже примере) назван.

from xml.dom.minidom import Document 


class DictToXML(object): 
    default_list_item_name = "item" 

    def __init__(self, structure, list_mappings={}): 
     self.doc = Document() 

     if len(structure) == 1: 
      rootName = str(list(structure.keys())[0]) 
      self.root = self.doc.createElement(rootName) 

      self.list_mappings = list_mappings 

      self.doc.appendChild(self.root) 
      self.build(self.root, structure[rootName]) 

    def build(self, father, structure): 
     if type(structure) == dict: 
      for k in structure: 
       tag = self.doc.createElement(k) 
       father.appendChild(tag) 
       self.build(tag, structure[k]) 
     elif type(structure) == list: 
      tag_name = self.default_list_item_name 

      if father.tagName in self.list_mappings: 
       tag_name = self.list_mappings[father.tagName] 

      for l in structure: 
       tag = self.doc.createElement(tag_name) 
       self.build(tag, l) 
       father.appendChild(tag) 
     else: 
      data = str(structure) 
      tag = self.doc.createTextNode(data) 
      father.appendChild(tag) 

    def display(self): 
     print(self.doc.toprettyxml(indent=" ")) 

    def get_string(self): 
     return self.doc.toprettyxml(indent=" ") 


if __name__ == '__main__': 
    example = {'sibling': {'couple': {'mother': 'mom', 'father': 'dad', 'children': [{'child': 'foo'}, 
                         {'child': 'bar'}]}}} 
    xml = DictToXML(example) 
    xml.display() 

Это дает следующий результат:

<?xml version="1.0" ?> 
<sibling> 
    <couple> 
    <children> 
     <child> 
     <name>foo</name> 
     </child> 
     <child> 
     <name>bar</name> 
     </child> 
    </children> 
    <father>dad</father> 
    <mother>mom</mother> 
    </couple> 
</sibling> 
+0

Уже существует пакет [dicttoxml] (https://pypi.python.org/pypi/dicttoxml). Я предлагаю использовать это, поскольку он указан в Индексе пакета Python и хорошо документирован. – philosopher