Pregunta

Estoy intentando generar archivos xml personalizados a partir de un archivo xml de plantilla en python.

Conceptualmente, quiero leer el xml de la plantilla, eliminar algunos elementos, cambiar algunos atributos de texto y escribir el nuevo xml en un archivo. Quería que funcionara algo como esto:

conf_base = ConvertXmlToDict('config-template.xml')
conf_base_dict = conf_base.UnWrap()
del conf_base_dict['root-name']['level1-name']['leaf1']
del conf_base_dict['root-name']['level1-name']['leaf2']

conf_new = ConvertDictToXml(conf_base_dict)

ahora quiero escribir en el archivo, pero no veo cómo llegar a ElementTree.ElementTree.write ()

conf_new.write('config-new.xml') 

¿Hay alguna manera de hacer esto o alguien puede sugerir hacerlo de otra manera?

¿Fue útil?

Solución

Para una fácil manipulación de XML en python, me gusta la biblioteca Beautiful Soup . Funciona algo como esto:

Archivo XML de muestra:

<root>
  <level1>leaf1</level1>
  <level2>leaf2</level2>
</root>

Código Python:

from BeautifulSoup import BeautifulStoneSoup, Tag, NavigableString

soup = BeautifulStoneSoup('config-template.xml') # get the parser for the xml file
soup.contents[0].name
# u'root'

Puede usar los nombres de los nodos como métodos:

soup.root.contents[0].name
# u'level1'

También es posible utilizar expresiones regulares:

import re
tags_starting_with_level = soup.findAll(re.compile('^level'))
for tag in tags_starting_with_level: print tag.name
# level1
# level2

Agregar e insertar nuevos nodos es bastante sencillo:

# build and insert a new level with a new leaf
level3 = Tag(soup, 'level3')
level3.insert(0, NavigableString('leaf3')
soup.root.insert(2, level3)

print soup.prettify()
# <root>
#  <level1>
#   leaf1
#  </level1>
#  <level2>
#   leaf2
#  </level2>
#  <level3>
#   leaf3
#  </level3>
# </root>

Otros consejos

Esto le proporcionará un atributo menos dict ... no sé si esto es útil para alguien. Estaba buscando un xml para dictarme una solución cuando se me ocurrió esto.



import xml.etree.ElementTree as etree

tree = etree.parse('test.xml')
root = tree.getroot()

def xml_to_dict(el):
  d={}
  if el.text:
    d[el.tag] = el.text
  else:
    d[el.tag] = {}
  children = el.getchildren()
  if children:
    d[el.tag] = map(xml_to_dict, children)
  return d

Esto: http://www.w3schools.com/XML/note.xml

<note>
 <to>Tove</to>
 <from>Jani</from>
 <heading>Reminder</heading>
 <body>Don't forget me this weekend!</body>
</note>

Seria igual a esto:


{'note': [{'to': 'Tove'},
          {'from': 'Jani'},
          {'heading': 'Reminder'},
          {'body': "Don't forget me this weekend!"}]}

No estoy seguro de que la conversión de la información establecida a los dictados anidados sea más fácil. Usando ElementTree, puedes hacer esto:

import xml.etree.ElementTree as ET
doc = ET.parse("template.xml")
lvl1 = doc.findall("level1-name")[0]
lvl1.remove(lvl1.find("leaf1")
lvl1.remove(lvl1.find("leaf2")
# or use del lvl1[idx]
doc.write("config-new.xml")

ElementTree fue diseñado para que no tenga que convertir sus árboles XML a listas y atributos primero, ya que usa exactamente eso internamente.

También es compatible con un pequeño subconjunto de XPath .

Mi modificación de la respuesta de Daniel, para dar un diccionario marginalmente más limpio:

def xml_to_dictionary(element):
    l = len(namespace)
    dictionary={}
    tag = element.tag[l:]
    if element.text:
        if (element.text == ' '):
            dictionary[tag] = {}
        else:
            dictionary[tag] = element.text
    children = element.getchildren()
    if children:
        subdictionary = {}
        for child in children:
            for k,v in xml_to_dictionary(child).items():
                if k in subdictionary:
                    if ( isinstance(subdictionary[k], list)):
                        subdictionary[k].append(v)
                    else:
                        subdictionary[k] = [subdictionary[k], v]
                else:
                    subdictionary[k] = v
        if (dictionary[tag] == {}):
            dictionary[tag] = subdictionary
        else:
            dictionary[tag] = [dictionary[tag], subdictionary]
    if element.attrib:
        attribs = {}
        for k,v in element.attrib.items():
            attribs[k] = v
        if (dictionary[tag] == {}):
            dictionary[tag] = attribs
        else:
            dictionary[tag] = [dictionary[tag], attribs]
    return dictionary

namespace es la cadena xmlns, incluidas las llaves, que ElementTree complementa a todas las etiquetas, por lo que aquí la he borrado ya que hay un espacio de nombres para todo el documento

NB: también ajusté el xml sin formato, de modo que las etiquetas 'vacías' produzcan como máximo una propiedad de texto en la representación de ElementTree

spacepattern = re.compile(r'\s+')
mydictionary = xml_to_dictionary(ElementTree.XML(spacepattern.sub(' ', content)))

daría por ejemplo

{'note': {'to': 'Tove',
         'from': 'Jani',
         'heading': 'Reminder',
         'body': "Don't forget me this weekend!"}}

está diseñado para un xml específico que es básicamente equivalente a json, debe manejar atributos de elementos como

<elementName attributeName='attributeContent'>elementContent</elementName>

también

existe la posibilidad de fusionar el diccionario de atributos / diccionario de subetiquetas de manera similar a como se combinan las subetiquetas de repetición, aunque el anidamiento de las listas parece apropiado :-)

Agregando esta línea

d.update(('@' + k, v) for k, v in el.attrib.iteritems())

en el código de user247686 también puede tener atributos de nodo.

Lo encontré en esta publicación https://stackoverflow.com/a/7684581/1395962

Ejemplo :

import xml.etree.ElementTree as etree
from urllib import urlopen

xml_file = "http://your_xml_url"
tree = etree.parse(urlopen(xml_file))
root = tree.getroot()

def xml_to_dict(el):
    d={}
    if el.text:
        d[el.tag] = el.text
    else:
        d[el.tag] = {}
    children = el.getchildren()
    if children:
        d[el.tag] = map(xml_to_dict, children)

    d.update(('@' + k, v) for k, v in el.attrib.iteritems())

    return d

Llamar como

xml_to_dict(root)

¿Has probado esto?

print xml.etree.ElementTree.tostring( conf_new )

forma más directa para mí:

root        = ET.parse(xh)
data        = root.getroot()
xdic        = {}
if data > None:
    for part in data.getchildren():
        xdic[part.tag] = part.text

XML tiene un rico conjunto de información, y se necesitan algunos trucos especiales para representarlo en un diccionario de Python. Los elementos están ordenados, los atributos se distinguen de los cuerpos de los elementos, etc.

Un proyecto para manejar viajes de ida y vuelta entre XML y los diccionarios de Python, con algunas opciones de configuración para manejar las compensaciones de diferentes maneras es Soporte XML en herramientas de decapado . Se requiere la versión 1.3 y más nueva. No es Python puro (y, de hecho, está diseñado para facilitar la interacción C ++ / Python), pero podría ser apropiado para varios casos de uso.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top