--- title: M06 Accés a dades. XML SAX. tags: DAM, Python, M6 --- <div style="width: 30%; margin-left: auto;"> ![](https://hackmd.io/_uploads/HJiR4eGJT.png) </div> <a rel="license" href="http://creativecommons.org/licenses/by-sa/4.0/"><img alt="Llicència de Creative Commons" style="border-width:0" src="https://i.creativecommons.org/l/by-sa/4.0/88x31.png" /></a><br />Aquesta obra està subjecta a una llicència de <a rel="license" href="http://creativecommons.org/licenses/by-sa/4.0/">Reconeixement-CompartirIgual 4.0 Internacional de Creative Commons</a> [Link en MarkDown](https://hackmd.io/@JdaXaviQ/rkDNzWpZa) # M06 Accés a dades. UF01. XML SAX. ## Mètodes de '_parsejar_' documents XML: 1. [SAX](https://docs.python.org/es/3/library/xml.sax.html) (**S**imple **A**pi for **X**ML): SAX consisteix en una API sequencial basada en events que permet processar tot el document XML sense haver de carregar tot el document a memòria. Especialment útil quan hem de manegar fitxers molt grans. * Els elements són processats en el mateix ordre que apareixen al document. * El desenvolupador ha de definir *[callbacks](https://ca.wikipedia.org/wiki/Callback_(programaci%C3%B3))* que s'encarreguen de processar nodes específics cada cop que es troben al document. * SAX només permet processar el document com a lectura. * És molt eficient en termes d'ús de memòria i de temps de processat però afegeix dificultat si el comparem amb DOM. 2. [DOM](https://www.w3.org/DOM/DOMTR) (**D**ocument **O**bject **M**odel): Amb DOM es carrega a memòria una representació abstracta de tot el document XML oferint accés aleatori a qualsevol node de l'arbre. * DOM necessita parsejar tot el document abans de poder accedir a la informació desada, cosa que el pot fer lent en algunes ocasions. * Tota la informació s'ha de carregar sencera a memòria, en documents extensos pot suposar un greu problema. * Permet modificar el document. ## Treballant amb SAX. La biblioteca de python que utilitzarem en aquest curs per parsejar de manera sequencial documents XML serà [xml.sax](https://docs.python.org/es/3/library/xml.sax.html). La forma habitual de començar a treballar és crear-nos la nostra pròpia classe que hereti de xml.sax.ContentHandler i sobrescriure els mètodes startElement(self, name, attrs), endElement(self, name) i characters(self, content) Com a exemple utilitzarem el següent contingut XML i el desarem al fitxer smiley.svg ```xml= <?xml version="1.0" encoding="UTF-8" standalone="no"?> <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" [ <!ENTITY custom_entity "Hello"> ]> <svg xmlns="http://www.w3.org/2000/svg" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" viewBox="-105 -100 210 270" width="210" height="270"> <inkscape:custom x="42" inkscape:z="555">Some value</inkscape:custom> <defs> <linearGradient id="skin" x1="0" x2="0" y1="0" y2="1"> <stop offset="0%" stop-color="yellow" stop-opacity="1.0"/> <stop offset="75%" stop-color="gold" stop-opacity="1.0"/> <stop offset="100%" stop-color="orange" stop-opacity="1"/> </linearGradient> </defs> <g id="smiley" inkscape:groupmode="layer" inkscape:label="Smiley"> <!-- Head --> <circle cx="0" cy="0" r="50" fill="url(#skin)" stroke="orange" stroke-width="2"/> <!-- Eyes --> <ellipse cx="-20" cy="-10" rx="6" ry="8" fill="black" stroke="none"/> <ellipse cx="20" cy="-10" rx="6" ry="8" fill="black" stroke="none"/> <!-- Mouth --> <path d="M-20 20 A25 25 0 0 0 20 20" fill="white" stroke="black" stroke-width="3"/> </g> <text x="-40" y="75">&custom_entity; &lt;svg&gt;!</text> <script> <![CDATA[ console.log("CDATA disables XML parsing: <svg>") const smiley = document.getElementById("smiley") const eyes = document.querySelectorAll("ellipse") const setRadius = r => e => eyes.forEach(x => x.setAttribute("ry", r)) smiley.addEventListener("mouseenter", setRadius(2)) smiley.addEventListener("mouseleave", setRadius(8)) ]]> </script> </svg> ``` Podem observar el contingut del fitxer obrint-lo directament amb el nostre navegador i també amb qualsevol programari d'edició o visualització de fitxers d'imatges vectorials. A continuació trobeu un exemple de com extreure la informació d'aquest document utilitzant python xml.sax: ```python= #!/usr/bin/python3 # -*- coding: utf-8 -*- """ svg_handler.py Exemple d'utilització de la biblioteca xml.sax """ import xml.sax def main(): controlador_svg = SVGHandler() xml.sax.parse("./smiley.svg", controlador_svg) class SVGHandler(xml.sax.ContentHandler): def startElement(self, name, attrs): print(f"BEGIN: <{name}>, {attrs.keys()}") for key in attrs.keys(): print(f"\t{key}: {attrs[key]}") def endElement(self, name): print(f"END: </{name}>") def characters(self, content): if content.strip() != "": print("CONTENT:", repr(content)) if __name__ == "__main__": main() ``` I ara un altre exemple d'utilització de la mateixa biblioteca: ```python= #!/usr/bin/python3 # -*- coding: utf-8 -*- import xml.sax class AlumnesHandler(xml.sax.ContentHandler): def __init__(self): self.llistat_alumnes = [] self.llistat_etiquetes = [] self.etiqueta_actual = "" self.nom = "" self.cognom = "" self.grup = "" def startElement(self, name, attrs): self.llistat_etiquetes.append(name) self.etiqueta_actual = name if name == "alumne": print("[v] Acabem d'entrar a un alumne nou.") self.nom = "" self.cognom = "" self.grup = "" def endElement(self, name): sortim_de = self.llistat_etiquetes.pop() if len(self.llistat_etiquetes) > 0: self.etiqueta_actual = self.llistat_etiquetes[-1] else: self.etiqueta_actual = "" if name == "alumne": print("[v] Acabem de sortir de l'alumne: ", self.nom, self.cognom) self.llistat_alumnes.append({ "nom": self.nom, "cognom": self.cognom, "grup": self.grup }) def characters(self, content): if self.etiqueta_actual == "nom": self.nom = content elif self.etiqueta_actual == "cognom": self.cognom = content elif self.etiqueta_actual == "grup": self.grup = content def main(): content = """<class> <alumne> <nom>Xavi</nom> <cognom>Quesada</cognom> <grup>DAM 2</grup> </alumne> <alumne> <nom>Pere</nom> <cognom>Llull</cognom> <grup>DAM 2</grup> </alumne> <alumne> <nom>Alicia</nom> <cognom>Galan</cognom> <grup>DAM 2</grup> </alumne> </class> """ with open("./class_dam2.xml","w") as dam2: dam2.write(content) if __name__ == "__main__": main() parser = xml.sax.make_parser() handler = AlumnesHandler() parser.setContentHandler(handler) parser.parse("./class_dam2.xml") print("Hem acabat de processar el fitxer XML.") print(handler.llistat_alumnes) ``` Amb la seva sortida a consola: ```= [v] Acabem d'entrar a un alumne nou. [v] Acabem de sortir de l'alumne: Xavi Quesada [v] Acabem d'entrar a un alumne nou. [v] Acabem de sortir de l'alumne: Pere Llull [v] Acabem d'entrar a un alumne nou. [v] Acabem de sortir de l'alumne: Alicia Galan Hem acabat de processar el fitxer XML. [{'nom': 'Xavi', 'cognom': 'Quesada', 'grup': 'DAM 2'}, {'nom': 'Pere', 'cognom': 'Llull', 'grup': 'DAM 2'}, {'nom': 'Alicia', 'cognom': 'Galan', 'grup': 'DAM 2'}] ```