XmlLib | Kombinator Bibliothek zum Generieren, Editieren und Transformieren von XML Dokumenten |
DtdToHaskell / Xml2Haskell |
Übersetzung von DTDs in äquivalente Haskell Datentypen |
Haskell2XML | Transformation von Haskell Datentypen in gültige XML Dokumente Generierung von entsprechenden DTDs |
Xtract | Abfragesprache für XML Dokumente, stark angelehnt an XQL |
Erzeugen der Haskell-Datentypen aus einer DTD:
1. DtdToHaskell MyFild.dtd DTD_MyFile.hs
Einbinden der Haskell-Datentypen in eine Haskell-Anwendung:
2. import Xml2Haskell (readXML) import DTD_MyFile ...
1. Ein leeres Element wird in einem "leeren" Datentyp übersetzt. Sein Typ-Konstruktor und Datenkonstruktor lauten auf den Elementnamen. Zu beachten ist, dass in Haskell diese Konstuktoren immer mit einem Großbuchstaben beginnen müssen. In XML sind die Elemente Farbe und farbe verschieden, beider würden aber in den Haskell-Datentyp Farbe übersetzt werden.
<!ELEMENT farbe EMPTY> data Farbe = Farbe
2. Ein Element, welches ein weiteres Element beinhaltet, wird in einem Produkttyp übersetzt. Der Datenkonstruktor wird um die Typvariable des Kind-Elements erweitert.
<!ELEMENT farbe rot> <!ELEMENT rot EMPTY> newtype Farbe = Farbe Rot data Rot = Rot
3. Ein Element, welches eine Sequenz von Elementen beinhaltet, wird analog in einem Produkttyp übersetzt. Der Datenkonstruktor wird um die Typvariablen aller Kind-Elemente erweitert.
<!ELEMENT farbe (rot, blau)> <!ELEMENT rot EMPTY> <!ELEMENT blau EMPTY> data Farbe = Farbe Rot Blau data Rot = Rot data Blau = Blau
4. Ein Element, welches eine Alternative von Elementen beinhaltet, wird in einem Aufzählungstyp übersetzt. Der Name des Datenkonstruktors wird aus dem Element-Namen und dem Kind-Element-Namen zusammengesetzt.
<!ELEMENT farbe (rot | blau)> <!ELEMENT rot EMPTY> <!ELEMENT blau EMPTY> data Farbe = FarbeRot Rot | FarbeBlau Blau data Rot = Rot data Blau = Blau
5. Ein Element, welches optional ein weiteres Element beinhaltet, wird in einem Produkttyp übersetzt, dessen Typvariable vom Datentyp Maybe Elementname ist.
<!ELEMENT farbe rot?> <!ELEMENT rot EMPTY> newtype Farbe = Farbe (Maybe Rot) data Rot = Rot
Maybe ist ein Monad zur Fehlerbehandlung. Es wird im allgemeinen zur Anzeige verwendet, ob ein Wert berechnet werden konnte. War eine Berechnung erfolgreich, hat es den Wert Just, ansonsten den Wert Nothing.
Das Maybe Monad ist wie folgt definiert:
data Maybe a = Nothing | Just a instance Monad Maybe where Just x >>= k = k x Nothing >>= k = Nothing return = Just fail s = Nothing
Beispiel zur Verwendung des Maybe Monads:
errDiv :: Int -> Int -> Maybe Int errDiv x y | (y /= 0) = Just (x 'div' y) | otherwise = Nothing
6. Ein Element, welches ein Kind-Element kein Mal oder beliebig oft (*) enthält, wird in einem Datentyp übersetzt, welcher eine Liste des Kind-Elements aufnimmt.
<!ELEMENT farbe rot*> <!ELEMENT rot EMPTY> newtype Farbe = Farbe [Rot] data Rot = Rot
7. Ein Element, welches ein anderes Element mindestens ein Mal oder beliebig oft (+) enthält, wird ebenfalls in einem Datentyp übersetzt, welcher eine Liste des Kind-Elements aufnimmt. Da der Haskell-Datentyp Liste nicht vorschreibt, dass er mindestens ein Element enthalten muss, besteht an dieser Stelle eine Diskrepanz zwischen der DTD und dem Haskell-Typsystem.
<!ELEMENT farbe rot+> <!ELEMENT rot EMPTY> newtype Farbe = Farbe [Rot] data Rot = Rot
8. Enthält ein Element Textdaten, wird für diese der Haskell-Datentyp String verwendet.
<!ELEMENT farbe (#PCDATA)> newtype Farbe = Farbe String
9. Enthält ein Element Mixed-Content, d.h. Textdaten und Elemente, wird das Inhaltsmodell in eine Liste überführt. Die Liste selbst ist ein Aufzählungstyp von Textdaten, d.h. einem String, und den Datentypen der Kind-Elemente.
<!ELEMENT farbe (#PCDATA | rot | blau)*> newtype Farbe = Farbe [Farbe_] data Farbe_ = Farbe_Str String | Farbe_Rot Rot | Farbe_Blau Blau
10. Enthält ein Element beliebigen deklarierten Inhalt, kann HaXml dieses nicht in das Typsystem von Haskell überführen.
<!ELEMENT farbe ANY> Program error: NYI
11. Für komplexere Ausdrücke im Inhaltsmodell verfügt HaXml über einige Abkürzungen, wie in diesem Beispiel der OneOf2 Typ.
<!ELEMENT farbe (rot, (blau | gelb)*)+> newtype Farbe = Farbe [Farbe_] data Farbe_ = Farbe_ Rot [(OneOf2 Blau Gelb)]
1. Im folgenden Beispiel sind Attribut-Deklarationen angegeben, welche als Werte Textdaten enthalten. Ist ein Attribut notwendig (#REQUIRED), wird der Datentyp String verwendet. Ist ein Attribut optional wird der bereits bekannte Typ Maybe String verwendet. Ist ein Default-Wert vorgegeben, wird der Typ Defaultable String von HaXml verwendet.
<!ELEMENT farbe (#PCDATA)> <!ATTLIST farbe rot CDATA #REQUIRED blau CDATA #IMPLIED gelb CDATA #FIXED "42" gruen CDATA "42"> data Farbe = Farbe Farbe_Attrs String data Farbe_Attrs = Farbe_Attrs { farbeRot :: String , farbeBlau :: (Maybe String) , farbeGelb :: (Defaultable String) , farbeGruen :: (Defaultable String) }
2. Attribut-Deklarationen, deren Typ durch eine Liste vorgegeben ist, werden in die bereits bekannten Aufzählungstypen übersetzt.
<!ELEMENT farbe (#PCDATA)> <!ATTLIST farbe rot (hell | mittel | dunkel) #REQUIRED blau (ja | nein) #IMPLIED> data Farbe = Farbe Farbe_Attrs String data Farbe_Attrs = Farbe_Attrs { farbeRot :: Farbe_Rot , farbeBlau :: (Maybe Farbe_Blau) } data Farbe_Rot = Farbe_Rot_Hell | Farbe_Rot_Mittel | Farbe_Rot_Dunkel data Farbe_Blau = Farbe_Blau_Ja | Farbe_Blau_Nein
3. Attribut-Deklarationen von Typ ID, IDREF und IDREFS werden von HaXml nur in den Haskell Typ String übersetzt. Solche semantischen Abhängigkeiten können nicht durch ein statisches Typsystem modelliert werden.
<!ELEMENT linie EMPTY> <!ATTLIST linie nummer ID #REQUIRED> <!ELEMENT farbe EMPTY> <!ATTLIST farbe linie IDREF #REQUIRED> <!ELEMENT figur EMPTY> <!ATTLIST figur linien IDREFS #REQUIRED> data Linie = Linie { linieNummer :: String } data Farbe = Farbe { farbeLinie :: String } data Figur = Figur { figurLinien :: String}
HaXml ist nicht zur Validierung von XML-Dokumenten gedacht. Das Programm DtdToHaskell
zielt vielmehr darauf ab, innerhalb einer Haskell-Anwendung ausschließlich gültige XML-Dokumente zu erzeugen.
Die Einleseroutinen von Xml2Haskell
sind leider recht rudimentär. Sie lesen nur die Elemente ein, welche von den vorgegebenen Typen erwartet werden. Folgt nach einem erwarteten Element aber noch ein weiteres, welches an dieser Stelle nicht auftreten darf, wird es einfach ignoriert. Nur wenn anstatt eines erwarteten Elements ein anderes eingelesen wird, meldet HaXml einen Fehler. Aus diesem Grund eignet sich HaXml nicht zur Validierung von XML-Dokumenten.
Das Typsystem von Haskell ist wesentlich Mächtiger als die Modellierungsmöglichkeiten einer DTD. Leider gibt es dennoch einige Konstrukte in einer DTD, die nicht durch das Typsystem von Haskell abgebildet werden können. So kann bei der Abbildung der Wiederholung (+) auf Listen nicht mehr garantiert werden, dass die Liste mindestens ein Element enthält. Ebenso ist es nicht möglich, die Attribut-Typen ID, IDREF und IDREFS durch ein statisches Typsystem abzubilden.