XML в/из словаря Python

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

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


person Liam    schedule 20.07.2010    source источник


Ответы (5)


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

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

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

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

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

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
person Doug F    schedule 29.10.2010
comment
Это отличный пример. Спасибо! - person rsmoorthy; 22.08.2013
comment
Я немного изменил его, добавив дополнительные функции, и он находится здесь по адресу pastebin.com/rtDQsR2j. - person rsmoorthy; 22.08.2013

Дикты в питоне не упорядочены, помните об этом. У меня очень простой код, он небольшой и не требует никаких внешних модулей. Плохо то, что он не поддерживает никаких атрибутов 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>
person Dmitry Horonitel    schedule 24.09.2013
comment
Для python3 вызов метода .iteritems() необходимо изменить на вызов .items() - person Jakub Pastuszuk; 29.11.2018

Для сериализации словаря Python в XML мне хорошо подходит следующий класс Python. По сравнению с некоторыми другими решениями у него есть то преимущество, что оно довольно простое и правильно кодирует XML. Сценарий основан на этом ответе. У него есть только одно расширение: передавая словарь 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>
person thomaskonrad    schedule 25.07.2015
comment
Уже существует пакет dicttoxml. Я предлагаю использовать его, так как он указан в указателе пакетов Python и хорошо задокументирован. - person philosopher; 09.05.2017

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

person Aaron Altman    schedule 20.07.2010