Valdierung mit HaXml



[ Informatik-Seminar 2002 ] ... [ Thema XML-Validierung mit Haskell ] ... [ Ableitung regulärer Ausdrücke ] ...

Übersicht:


Einleitung

HaXml ist eine Sammlung von Tools, um XML mit Haskell zu verarbeiten. Die Sammlung umfasst einen Parser für XML, eine Kombinator-Bibliothek, die eine einfache Bearbeitung, Generierung und Transformation von XML-Dokumenten erlaubt, ein Modul zur Übersetzung von DTDs in äquivalente Haskell Datentypen sowie weitere nützliche Programme.


Bestandteile:
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



Verarbeitung von XML-Dokumenten mit HaXml

HaXmL bietet zwei Ansätze, um XML-Dokumente zu verarbeiten. Der erste Ansatz verwendet eine allgemeine Baumstruktur zur Repräsentation von XML-Dokumenten. Der zweite Ansatz arbeitet mit einer problemspezifischen Datenstruktur. Hierbei werden DTDs in algebraische Datentypen von Haskell übersetzt und entsprechende Ein- und Ausgabefunktionen erzeugt. Der Vorteil dieses Ansatzes liegt darin, dass das Strenge Typ-System von Haskell es unmöglich macht, während der Verarbeitung eines Dokuments einen invaliden Zustand zu erzeugen. Haskell-Code, der zu invaliden Dokumenten führen würde, wird bereits zur Compilezeit erkannt. Die Gültigkeit des XML-Dokuments wird bei diesem Ansatz somit vom Compiler erzwungen. Bei generischer Verarbeitung von XML-Dokumenten wie in Java, Perl oder Python müssten Dokument zwei mal validiert werden, nämlich bei der Ein- und Ausgabe, um sicherzustellen, dass die Anwendung ein gültiges XML-Dokument erzeugt.
Darüber hinaus wird die Dokumentation des Programmcodes erhöht, da nicht mit generischen Datenstrukturen gearbeitet wird, sondern die durch die DTD vorgegebene Dokumentstruktur als äquivalente Typen im Programmcode vorliegen.
Als Nebeneffekt dieses Ansatzes kann ebenfalls eine Validierung des eingelesenen Dokuments durch das Typsystem vorgenommen werden, falls die Einleseroutinen daraufhin ausgelegt wurden.

Es gibt jedoch auch Nachteile der problemspezifischen Datenstruktur. Es kann bei diesem Ansatz kein eventuell beabsichtigter invalider temporärer Zustand des Dokuments erzeugt werden. Sollte der Ansatz außerdem zum Validieren von XML-Dokumenten verwendet werden, könnten zur Laufzeit keine Dokumente mit unterschiedlichen DTDs validiert werden, da die problemspezifischen Typen nur für eine bestimmte DTD erzeugt wurden.

Das Modul DtdToHaskell von HaXml erzeugt aus einer DTD Haskell-Datentypen.

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
    ...




Übersetzung von Element-Deklarationen

Im folgenden Abschnitt soll betrachtet werden, wie die Element-Deklarationen einer DTD in Haskell-Datentypen übersetzt werden.


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



Einschub Maybe:

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)]




Übersetzung von Attribut-Deklarationen

Elemente mit Attributen werden auf drei Haskell-Typen abgebildet:

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}


Bewertung

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.


[ Informatik-Seminar 2002 ] ... [ Thema XML-Validierung mit Haskell ] ... [ Valdierung mit HaXml ] ... [ Ableitung regulärer Ausdrücke ] ...