SPB Git

spb/ultra-sharp-agent-skills Public

Ultra-Sharp Agent Skills — a research-first skill-authoring system + 72 production-ready skills for AI agents.

Python 100%
5.0 KB · 140 lines markdown
Rendered Raw Blame History
1<!--2Author: Simon-Pierre Boucher3Contact: contact@spboucher.ai4-->56# XML Recipes78## Contents9- Create a document with namespaces10- Read and extract (XPath)11- Modify: insert, remove, rename, move12- Validate against an XSD13- Streaming huge files14- Convert XML ↔ dict/JSON15- Gotchas1617## Create a document with namespaces1819```python20import xml.etree.ElementTree as ET2122NS = "http://example.com/catalog"23ET.register_namespace("", NS)  # default namespace, no prefix in output2425root = ET.Element(f"{{{NS}}}catalog")26book = ET.SubElement(root, f"{{{NS}}}book", {"id": "bk101"})27ET.SubElement(book, f"{{{NS}}}title").text = "XML Developer's Guide"28ET.SubElement(book, f"{{{NS}}}price").text = "44.95"2930ET.indent(root)                     # 2-space pretty print (3.9+)31ET.ElementTree(root).write("catalog.xml",32                           xml_declaration=True, encoding="utf-8")33```3435## Read and extract (XPath)3637ElementTree supports a *subset* of XPath — child paths, `//`, `[@attr]`, `[tag='text']`, positional `[1]`:3839```python40tree = ET.parse("catalog.xml")41root = tree.getroot()42ns = {"c": "http://example.com/catalog"}4344first = root.find("c:book[1]", ns)                    # first book45cheap = root.findall(".//c:book[c:price='44.95']", ns)46ids = [b.get("id") for b in root.findall(".//c:book", ns)]47text = root.findtext(".//c:book/c:title", default="", namespaces=ns)48```4950Full XPath 1.0 (functions, `contains()`, axes) needs lxml:5152```python53from lxml import etree54root = etree.parse("catalog.xml").getroot()55titles = root.xpath("//c:book[contains(c:title,'Guide')]/c:title/text()",56                    namespaces={"c": "http://example.com/catalog"})57```5859## Modify: insert, remove, rename, move6061```python62tree = ET.parse("catalog.xml")63root = tree.getroot()64ns = {"c": "http://example.com/catalog"}6566# Insert after an existing child (ElementTree has no insert-after: use index)67books = root.findall("c:book", ns)68new = ET.Element(f"{{http://example.com/catalog}}book", {"id": "bk102"})69root.insert(list(root).index(books[-1]) + 1, new)7071# Remove — you must remove from the PARENT72for bad in root.findall("c:book[@id='bk101']", ns):73    root.remove(bad)7475# Rename a tag76for el in root.iter(f"{{http://example.com/catalog}}price"):77    el.tag = f"{{http://example.com/catalog}}cost"7879tree.write("catalog.xml", xml_declaration=True, encoding="utf-8")80```8182To find a parent when you only matched the child (stdlib has no `getparent()`):8384```python85parents = {c: p for p in root.iter() for c in p}86parents[child].remove(child)87```8889## Validate against an XSD9091Requires lxml (`pip install lxml`):9293```python94from lxml import etree9596schema = etree.XMLSchema(etree.parse("catalog.xsd"))97doc = etree.parse("catalog.xml")98if not schema.validate(doc):99    for err in schema.error_log:100        print(f"line {err.line}, col {err.column}: {err.message}")101```102103Report every error with line/column; fix the tree, re-serialize, re-validate.104105## Streaming huge files106107Load-then-clear keeps memory flat regardless of file size:108109```python110import xml.etree.ElementTree as ET111112for event, elem in ET.iterparse("huge.xml", events=("end",)):113    if elem.tag.endswith("record"):114        process(elem)115        elem.clear()   # release children; keeps memory bounded116```117118## Convert XML ↔ dict/JSON119120No stdlib one-liner exists; for flat, repetitive data write it explicitly:121122```python123rows = [{"id": b.get("id"), "title": b.findtext("c:title", "", ns)}124        for b in root.findall(".//c:book", ns)]125import json; json.dump(rows, open("books.json", "w"), indent=2)126```127128For arbitrary nesting, `pip install xmltodict` and `xmltodict.parse(open("f.xml", "rb"))` — but beware: single children become dicts, repeated children become lists, so downstream code must handle both shapes.129130## Gotchas131132- **`ns0:` prefix pollution.** ElementTree invents `ns0:` prefixes on write unless you call `ET.register_namespace(prefix, uri)` *before* parsing/writing. Register `""` for a default namespace.133- **Truthiness trap.** An element with no children is falsy: `if elem:` is False even when the element exists. Always use `if elem is not None:`.134- **`find()` vs `findall()` with no namespace map** silently return nothing on namespaced documents — the tag you search must be `{uri}tag` or use the `ns` map. "It finds nothing" almost always means a missing namespace map.135- **No parent pointers** in stdlib ElementTree — removal requires the parent (see recipe above); lxml elements have `.getparent()`.136- **Comments and processing instructions are dropped** by the default ElementTree parser. If they must survive a round-trip, use lxml, which preserves them.137- **`ET.tostring()` omits the XML declaration** unless `xml_declaration=True` — and returns bytes when an encoding is given.138- **Entity attacks (billion laughs, external entities).** Never parse untrusted XML with plain ElementTree/lxml defaults; use defusedxml drop-ins (`defusedxml.ElementTree.parse`).139- **`ET.indent()` mutates text nodes** — don't use it on documents where whitespace inside elements is significant (mixed content).140