python - tutorial - ¿Cómo convertir XML a objetos?
django views (7)
Necesito cargar un archivo XML y poner los contenidos en una estructura orientada a objetos. Quiero tomar esto:
<main>
<object1 attr="name">content</object>
</main>
y conviértalo en algo como esto:
main
main.object1 = "content"
main.object1.attr = "name"
Tendrá una estructura más complicada que eso y no puedo codificar los nombres de los elementos. Los nombres deben recopilarse al analizar y utilizarse como las propiedades del objeto.
¿Alguna idea sobre cuál sería la mejor manera de hacerlo?
Qué tal esto
Si no funciona buscar en Google un generador de código, puede escribir uno que use XML como entrada y genere objetos en el idioma de su elección.
No es terriblemente difícil, sin embargo, el proceso de tres pasos de Parse XML, Generate Code, Compile / Execute Script hace que la depuración sea un poco más difícil.
Hay tres analizadores XML comunes para python: xml.dom.minidom, elementree y BeautifulSoup.
IMO, BeautifulSoup es de lejos el mejor.
Lo he recomendado más de una vez hoy, pero prueba Beautiful Soup (easy_install BeautifulSoup).
from BeautifulSoup import BeautifulSoup
xml = """
<main>
<object attr="name">content</object>
</main>
"""
soup = BeautifulSoup(xml)
# look in the main node for object''s with attr=name, optionally look up attrs with regex
my_objects = soup.main.findAll("object", attrs={''attr'':''name''})
for my_object in my_objects:
# this will print a list of the contents of the tag
print my_object.contents
# if only text is inside the tag you can use this
# print tag.string
Gnosis.xml.objectify de David Mertz parece hacer esto por usted. La documentación es un poco difícil de encontrar, pero hay algunos artículos de IBM, incluido este .
from gnosis.xml import objectify
xml = "<root><nodes><node>node 1</node><node>node 2</node></nodes></root>"
root = objectify.make_instance(xml)
print root.nodes.node[0].PCDATA # node 1
print root.nodes.node[1].PCDATA # node 2
Crear XML de objetos de esta manera es una cuestión diferente.
#@Stephen:
#"can''t hardcode the element names, so I need to collect them
#at parse and use them somehow as the object names."
#I don''t think thats possible. Instead you can do this.
#this will help you getting any object with a required name.
import BeautifulSoup
class Coll(object):
"""A class which can hold your Foo clas objects
and retrieve them easily when you want
abstracting the storage and retrieval logic
"""
def __init__(self):
self.foos={}
def add(self, fooobj):
self.foos[fooobj.name]=fooobj
def get(self, name):
return self.foos[name]
class Foo(object):
"""The required class
"""
def __init__(self, name, attr1=None, attr2=None):
self.name=name
self.attr1=attr1
self.attr2=attr2
s="""<main>
<object name="somename">
<attr name="attr1">value1</attr>
<attr name="attr2">value2</attr>
</object>
<object name="someothername">
<attr name="attr1">value3</attr>
<attr name="attr2">value4</attr>
</object>
</main>
"""
#
soup=BeautifulSoup.BeautifulSoup(s)
bars=Coll()
for each in soup.findAll(''object''):
bar=Foo(each[''name''])
attrs=each.findAll(''attr'')
for attr in attrs:
setattr(bar, attr[''name''], attr.renderContents())
bars.add(bar)
#retrieve objects by name
print bars.get(''somename'').__dict__
print ''/n/n'', bars.get(''someothername'').__dict__
salida
{''attr2'': ''value2'', ''name'': u''somename'', ''attr1'': ''value1''}
{''attr2'': ''value4'', ''name'': u''someothername'', ''attr1'': ''value3''}
Vale la pena echarle un vistazo a http://lxml.de/objectify.html
>>> xml = """<main>
... <object1 attr="name">content</object1>
... <object1 attr="foo">contenbar</object1>
... <test>me</test>
... </main>"""
>>> from lxml import objectify
>>> main = objectify.fromstring(xml)
>>> main.object1[0]
''content''
>>> main.object1[1]
''contenbar''
>>> main.object1[0].get("attr")
''name''
>>> main.test
''me''
O al revés para construir estructuras xml:
>>> item = objectify.Element("item")
>>> item.title = "Best of python"
>>> item.price = 17.98
>>> item.price.set("currency", "EUR")
>>> order = objectify.Element("order")
>>> order.append(item)
>>> order.item.quantity = 3
>>> order.price = sum(item.price * item.quantity
... for item in order.item)
>>> import lxml.etree
>>> print lxml.etree.tostring(order, pretty_print=True)
<order>
<item>
<title>Best of python</title>
<price currency="EUR">17.98</price>
<quantity>3</quantity>
</item>
<price>53.94</price>
</order>