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

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

4 голосов
/ 20 июля 2010

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

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

Ответы [ 5 ]

5 голосов
/ 29 октября 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
3 голосов
/ 21 июля 2010

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

2 голосов
/ 25 июля 2015

Для сериализации Python dict в 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>
2 голосов
/ 25 сентября 2013

Dicts в 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 голос
/ 21 июля 2010

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

...