---
title: M06 Accés a dades. XML SAX.
tags: DAM, Python, M6
---
<div style="width: 30%; margin-left: auto;">

</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; <svg>!</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'}]
```