Již umíme vcelku rozumně zpracovat dokument, který od někud načteme,
co když ovšem potřebujeme vytvořit celý v paměti a nebo do existujícího
něco připsat?
15.6.2004 10:00 | Aleš Hakl | přečteno 17065×
Právě proto má třída Document
několik metod pro vytváření instancí tříd reprezentujících různe druhy
uzlů. Ovšem samotný uzel nám není moc platný, a tak ho pomocí metod appendChild(), insertBefore() nebo replaceChild() třídy Node
zařadíme někam do dokumentu.
Nejprve vytvoříme instanci (zatím prázdného) elementu, který chceme vložit:
>>> element = doc.createElement("prvni-element")
>>> element
<Element Node at 407c644c: Name='prvni-element' with 0 attributes and 0 children>
>>> xml.dom.ext.PrettyPrint(element)
<prvni-element/>
A ten poté vložíme někam do dokumentu, třeba na konec kořenového elementu:
>>> doc.documentElement.appendChild(element)
<Element Node at 407c644c: Name='prvni-element' with 0 attributes and 0 children>
>>> xml.dom.ext.PrettyPrint(doc)
<?xml version='1.0' encoding='UTF-8'?>
<korenovy-element atribut='nejaka hodnota'>
Výpis zkrácen ...
<prvni-element/>
</korenovy-element>
Zde narážíme na první místo, kde reálně hrozí, že způsobíme nějakou
výjimku. DOM zná jeden druh výjimky, třídu DOMException
.
Příčinu chyby bychom dle standardu měli zjistit podle chybového kódu v jejím atributu code. Ve skutečnosti tomu tak v Pythonu není (a je to vyřešeno rozumněji):
>>> doc2.documentElement.appendChild(element)
Traceback (most recent call last):
File "<stdin>", line 1, in ?
File "/usr/lib/python2.3/site-packages/_xmlplus/dom/FtNode.py", line 246, in appendChild
self._4dom_validateNode(newChild)
File "/usr/lib/python2.3/site-packages/_xmlplus/dom/FtNode.py", line 386, in _4dom_validateNode
raise WrongDocumentErr()
xml.dom.WrongDocumentErr: Node is from a different document
A zde hned vidíme, že elementy vytvořené voláním metod jedné instance třídy Document
nemůžeme vždy vkládat do jiného dokumentu. Operace může selhat a vyvolat výjimku (chybový kód WRONG_DOCUMENT_ERR
, číselná hodnota 4), v případě Pythonu dostaneme přímo výjimku WrongDocumentErr
. Jinak v Javě dostaneme přibližne to, co říká specifikace DOM:
Exception in thread "main" org.apache.crimson.tree.DomEx: WRONG_DOCUMENT_ERR: That node doesn't belong in this document.
neboli výjimku org.apache.crimson.tree.DomEx
(pokud tedy používáme tuto implementaci DOM) s chybovým kódem WRONG_DOCUMENT_ERR
.
Též můžeme vyvolat výjimku HIERARCHY_REQUEST_ERR
, pokud se snažíme vložit uzel někam, kam to nemá smysl (třeba vložit atribut do těla elementu, nebo vkládat cokoli do textového uzlu).
Poslední výjimkou ke které může dojít, je NO_MODIFICATION_ALLOWED_ERR
, znamená to že se snažíme měnit uzel který je určen pouze pro čtení. Takové uzly najdeme například uvnitř uzlů třídy EntityReference
.
Všechny tři tyto objekty jsou vlastně totéž - text v dokumentu a liší
se pouze syntaxí a dalším zpracováním. Je tedy logické, že jsou všechny
odvozeny od společného předka, třídy CharacterData
. Tato třída je vlastně pouhý obal kolem textového řetězce (význam: je odvozena od Node
a odstraňuje rozdíly v řešení řetězců v různých jazycích). Nový uzel tedy vytvoříme odpovídající metodou třídy Document
:
>>> comment = doc.createComment("Ja jsem komentar")
>>> xml.dom.ext.PrettyPrint(comment)
<!--Ja jsem komentar-->
>>> text = doc.createTextNode("Ja jsem text")
>>> xml.dom.ext.PrettyPrint(text)
Ja jsem text
>>> cdata = doc.createCDATASection("Ja jsem sekce CDATA")
>>> xml.dom.ext.PrettyPrint(cdata)
<![CDATA[Ja jsem sekce CDATA]]>
Musíme si ovšem dát pozor, co se snažíme do dokumentu vložit, obecně není dobře pokud se snažime vložit nějaké netisknutelné znaky mimo mezery, tabulátoru, nového řádku a podobně. Často se nám totiž může povést vyrobit dokument, který po uložení do souboru již nepůjde načíst.
Další zrada spočíva v tom, že zatímco v textových uzlech jsou případné výskyty speciálních znaků (>, <, " ...) za entity nahrazovány automaticky, v komentářích a sekcích CDATA se tak neděje, musíme si tedy dát pozor na výskyt sekvence "-->", respektive "]]>".
>>> bc = doc.createComment("Rozbity --> komentar")
>>> xml.dom.ext.PrettyPrint(bc)
<!--Rozbity --> komentar-->
>>> bd = doc.createCDATASection("Rozbita ]]> sekce CDATA")
>>> xml.dom.ext.PrettyPrint(bd)
<![CDATA[Rozbita ]]> sekce CDATA]]>
>>> xml.dom.ext.PrettyPrint(bd)
<![CDATA[Rozbita ]]> sekce CDATA]]>
>>> t = doc.createTextNode("A <tohle> projde")
>>> xml.dom.ext.PrettyPrint(t)
A <tohle> projde
Instrukce pro zpracování jsou vlastně také textová data, ale nejsou odvozeny od třídy CharacterData
. Instrukce pro zpracování jsou prostředek, jak do dokumentu vložit strojově čitelné "komentáře" určující, jak s dokumentem nakládat, ukázkou může být například jazyk PHP, který používa identickou syntaxi. Instrukce se skládají ze dvou řetězců: identifikátoru cíle (například "php") a vlastní instrukce.
>>> pi = doc.createProcessingInstruction("php","echo \"Hello World\";")
>>> xml.dom.ext.PrettyPrint(pi)
<?php echo "Hello World";?>
Ještě je možné vkládat odkazy na entity, ale toto téma si necháme na později, do případného dílu o entitách. Osobně totiž považuji zpracování entit za jednu z nejnepřehlednějších části Document Object Modelu. Tímto konstatováním bych dnešní díl zakončil a doufám, že se sejdeme u dalšího pokračování, kde se vrhneme na první poněkud souhrnější ukázku, jednoduchou databázi.