Übung: Erstellung einer Document-Type-Definition für XML-Rechnungen
https://bildung.social/@oerinformatik/111980708596828078
https://oer-informatik.de/xml-dtd-uebung
tl/dr; (ca. 20-30 min Bearbeitungszeit): Für eine XML-Rechnung soll Schritt für Schritt eine Document-Type-Definition erstellt werden, die einen relativ großen Funktionsumfang der DTD enthält. Geeignet zur Vertiefung des XML-DTD-Wissens in Kombination mit dem DTD-Artikel. (Zuletzt geändert am 26.02.2024)
Dieser Artikel ist Bestandteil einer Serie zu den XML-Grundlagen:
Teil 1: XML-Grundlagen und Wohlgeformtheit: Hierarchische Daten mit XML speichern mit zugehörigen Übungsaufgaben
Teil 2: XML-Dokumente gegen eine Document-Type-Definition validieren mit zugehörigen Übungsaufgaben
Teil 4: Die Struktur von XML-Dokumente gegen ein XSD-Schema validieren
Teil 5: Datentypen-Definitionen eines XML-Schema zur Validierung nutzen
Sämtliche Aufgaben lassen sich mit einem Tool wie dem Online XML-Validator von truugo.com überprüfen.
Aufgabenstellung der Gesamtrechnung
Rechnungen sollen als XML-Datei exportiert werden. Hierzu liegt ein XML-Beispielauszug vor, für den in kleinen Schritten eine Document-Type-Definition (DTD) erstellt werden soll.
Die Gesamtdatei sieht folgendermaßen aus, wird aber gleich in kleineren Schritten realisiert:
<?xml version='1.0' encoding='UTF-8'?>
<rechnung nr="1234" datum="12.01.2024">
<absender>
<adresse>
<firmenname>Baschira GmbH</firmenname>
<postfach>Postfach 12344</postfach>
<plz>10248</plz>
<ort>Berlin</ort>
<telefon>0124 / 6567891</telefon>
<telefon>0234 / 7567894</telefon>
</adresse>
</absender>
<empfaenger>
<adresse kundennummer="1234">
<vorname>Max</vorname>
<nachname>Mustermann</nachname>
<namenszusatz>3. OG links</namenszusatz>
<strasse>Hauptstraße</strasse>
<hausnr>23</hausnr>
<plz>12345</plz>
<ort>Berlin</ort>
</adresse>
</empfaenger>
<positionen>
<position nr="pos1">
<anzahl>2</anzahl>
<artikelNr>12345</artikelNr>
<artikelName>Produkt1</artikelName>
<einzelpreis>12.50</einzelpreis>
<betragNetto>25.00</betragNetto>
</position>
<position nr="pos2">
<anzahl>1</anzahl>
<artikelNr>54321</artikelNr>
<artikelName>Produkt2</artikelName>
<einzelpreis>75.00</einzelpreis>
<betragNetto>75.00</betragNetto>
</position>
</positionen>
<summe>
<netto>100.00</netto>
<mwst>19.00</mwst>
<brutto>119.00</brutto>
</summe>
<status>
<statusmeldung datum="22.02.2024" statusart="unbekannt" />
<statusmeldung datum="24.02.2024" statusart="vorbereitet" />
</status>
<hinweise>
<notiz>Dies ist eine Korrektur der Rechnung 1234 über <netto>80.00</netto> Euro.</notiz>
<text>Dies ist eine Korrektur der Rechnung 1234 über 80.00 Euro.</text>
<weitereadresse>
Versandadresse
<adresse>
<firmenname>Baschira GmbH Filiale Hamburg</firmenname>
<postfach>Postfach 12344</postfach>
<plz>20248</plz>
<ort>Hamburg</ort>
</adresse>
</weitereadresse>
<text>Noch ein Text</text>
</hinweise>
</rechnung>
Wer das nicht in Einzelschritten lösen will, sondern in einem großen Rutsch, dem seinen noch die Randbedingungen genannt: Die DTD soll dafür sorgen, dass alle Elemente in der Art vorkommen, wie sie im Beispiel genannt sind. Alle Elemente bis auf telefon
, namenszusatz
und hinweise
müssen verpflichtend vorkommen, wenn Sie im Beispiel genannt sind. Adressen können von Privatpersonen (vorname
, nachname
) oder Firmen (firmenname
) angegeben werden. Die Adresse kann als Straße oder Postfach angegeben werden. Eine Rechnung muss mindestens eine Rechnungsposition haben. In hinweise
können die drei genannten Elemente beliebig häufig vorkommen, wobei weitereadresse
Text oder adresse
enthalten darf und notiz
beliebigen Inhalt. Die Positionsnummern sollen einzigartig vergeben werden. Aber langsam, Schritt für Schritt…
1. Die Grundstruktur für eine XML-Rechnung
Die Grundstruktur der Rechnung ist folgende. Das Element hinweise
soll optional sein, es wird zunächst angenommen, das alle Elemente unterhalb von rechnung
ausschließlich parsbaren Text enthalten. Erstelle hierfür eine gültige DTD:
<?xml version='1.0' encoding='UTF-8'?>
<rechnung>
<absender></absender>
<empfaenger></empfaenger>
<positionen></positionen>
<summe></summe>
<status></status>
<hinweise></hinweise>
</rechnung>
<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE rechnung [
<!ELEMENT rechnung (absender, empfaenger, positionen, summe, status, hinweise?)>
<!ELEMENT absender (#PCDATA)>
<!ELEMENT empfaenger (#PCDATA)>
<!ELEMENT positionen (#PCDATA)>
<!ELEMENT summe (#PCDATA)>
<!ELEMENT status (#PCDATA)>
<!ELEMENT hinweise (#PCDATA)>
]>
2. Absender ergänzen als Firmenadresse
Das Element absender
wird angepasst: hier soll eine Firmenadresse nach folgendem Vorbild enthalten sein. Das Element telefon
soll optional sein.
<absender>
<adresse>
<firmenname>Baschira GmbH</firmenname>
<postfach>Postfach 12344</postfach>
<plz>10248</plz>
<ort>Berlin</ort>
<telefon>0124 / 6567891</telefon>
<telefon>0234 / 7567894</telefon>
</adresse>
</absender>
3. Empfänger ergänzen als Privatadresse
Auch für das Element empfänger
soll eine Adresse hinterlegt werden. Diese ist aber anders aufgebaut, daher muss das oben definierte Element adresse
angepasst werden: Adressen können von Privatpersonen (vorname
, nachname
) oder Firmen (firmenname
) angegeben werden. Die Adresse kann als Straße oder Postfach angegeben werden. Außerdem soll es die Möglichkeit geben, nach dem Firmen- oder Privatnamen einen namenszusatz
zu erfassen. Die obigen Firmenadressen sollen weiterhin in der genannten Form unter absender
stehen:
<empfaenger>
<adresse>
<vorname>Max</vorname>
<nachname>Mustermann</nachname>
<namenszusatz>3. OG links</namenszusatz>
<strasse>Hauptstraße</strasse>
<hausnr>23</hausnr>
<plz>12345</plz>
<ort>Berlin</ort>
</adresse>
</empfaenger>
<!ELEMENT absender (adresse)>
<!ELEMENT empfaenger (adresse)>
<!ELEMENT adresse (((vorname, nachname)|firmenname), namenszusatz?, ((strasse, hausnr) | postfach), plz, ort, telefon*)>
<!ELEMENT vorname (#PCDATA)>
<!ELEMENT nachname (#PCDATA)>
<!ELEMENT namenszusatz (#PCDATA)>
<!ELEMENT firmenname (#PCDATA)>
<!ELEMENT strasse (#PCDATA)>
<!ELEMENT hausnr (#PCDATA)>
<!ELEMENT postfach (#PCDATA)>
<!ELEMENT plz (#PCDATA)>
<!ELEMENT ort (#PCDATA)>
<!ELEMENT telefon (#PCDATA)>
4. Rechnungspositionen erfassen
Es sollen die Rechnungspositionen erfasst werden, wobei sichergestellt sein muss, dass mindestens eine Position in jeder Rechnung gespeichert wird:
<positionen>
<position>
<anzahl>2</anzahl>
<artikelNr>12345</artikelNr>
<artikelName>Produkt1</artikelName>
<einzelpreis>12.50</einzelpreis>
<betragNetto>25.00</betragNetto>
</position>
<position>
<anzahl>1</anzahl>
<artikelNr>54321</artikelNr>
<artikelName>Produkt2</artikelName>
<einzelpreis>75.00</einzelpreis>
<betragNetto>75.00</betragNetto>
</position>
</positionen>
5. Rechnungssumme
Die Rechnungssumme soll zwingend in der Reihenfolge netto
, mwst
, brutto
erfasst werden:
6. Hinweise
Im Element Hinweise
können drei verschiedene Einträge stehen. Es muss ein Unterelement geben, es können aber beliebig viele sein. In einer notiz
darf alles stehen (beliebiger Text und Unterelemente), in einem text
nur Text und in einer weiterenadresse
zunächst auch nur Text.
<hinweise>
<notiz>Dies ist eine Korrektur der Rechnung 1234 über <netto>80.00</netto> Euro.</notiz>
<text>Dies ist eine Korrektur der Rechnung 1234 über 80.00 Euro.</text>
<weitereadresse>
Versandadresse
</weitereadresse>
<text>Noch ein Text</text>
</hinweise>
7. Hinweise
Das Element weitereadresse
darf Text enthalten und adresse
-Elemente.
<weitereadresse>
Versandadresse
<adresse>
<firmenname>Baschira GmbH Filiale Hamburg</firmenname>
<postfach>Postfach 12344</postfach>
<plz>20248</plz>
<ort>Hamburg</ort>
</adresse>
</weitereadresse>
Realisierung als mixed content - eine Bestimmung der Reihenfolge ist nicht möglich, #PCDATA
muss als erstes genannt werden.
8. Rechnungsnummer und Datum
Für jede Rechnung soll im Root-Element die Rechnungsnummer und das Rechnungsdatum erfasst werden. Welche Gründe sprechen dafür, das als Attribut zu realisieren, was spricht dafür, das als Unterelement zu realisieren?
Für Elemente spricht:
später leichter durch weitere Unterelemente erweiterbar
mehrere gleichartige Werte speicherbar (z.B. mehrere Datumsangaben oder mehrere Rechnungsnummern)
leichter über Baumstruktur erreichbar / einheitlicher als gemischt Attribut/Element
Für Attribute spricht:
kompakt in der Schreibweise
Reihenfolge muss nicht / kann nicht festgelegt werden
mehr Einschränkungen im Rahmen der DTD möglich (Einzigartigkeit, Eingabepflicht)
Es wird die Realisierung über Attribute beschlossen, diese sollen zwingend Werte enthalten. Erstelle die entsprechende DTD:
9. Kunden
Die Kundenummer soll als optionales Attribut erfasst werden, :a sie nur beim Empfänger, nicht aber beim Absender in der adresse
auftritt.
10. Status
Für jeder Rechnung soll ein status
erfasst werden aus beliebig vielen Statusmeldungen, die jeweils nur Datum und eine Statusart (gültig ist nur: vorbereitet, erstellt, versendet, bezahlt, angemahnt oder unbekannt) enthalten. Elementeinhalt darf statusmeldung
nicht haben.
<status>
<statusmeldung datum="22.02.2024" statusart="unbekannt" />
<statusmeldung datum="24.02.2024" statusart="vorbereitet" />
</status>
11. Positionsnummer
Für die einzelnen Rechnungspositionen soll jeweils eine zwingend eindeutige ID vergeben werden:
<positionen>
<position nr="pos1">
...
</position>
<position nr="pos2">
...
</position>
</positionen>
Warum kann das Attribut nr
als Attributtyp ID
nicht einfach laufende Nummern enthalten?
Der Attributtyp ID
schreibt fest, dass der Wert des Attributs dieselben Regeln erfüllen muss wie ein XML-Elementenamen (z.B. keine Zahlen am Beginn). Man muss daher die Werte anpassen, z.B. durch ein führendes “pos”. Wenn der Attributtyp zu CDATA
oder NMTOKEN
geändert wird, können laufende Nummern genutzt werden. Allerdings entfällt dann die Möglichkeit, zu überprüfen, ob die Werte eindeutig sind.
12. Die Gesamtlösung - das Big Picture
<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE rechnung [
<!ELEMENT rechnung (absender, empfaenger, positionen, summe, status, hinweise?)>
<!ELEMENT empfaenger (adresse)>
<!ELEMENT absender (adresse)>
<!ELEMENT adresse (((vorname, nachname)|firmenname), namenszusatz?, ((strasse, hausnr) | postfach), plz, ort, telefon*)>
<!ELEMENT vorname (#PCDATA)>
<!ELEMENT nachname (#PCDATA)>
<!ELEMENT namenszusatz (#PCDATA)>
<!ELEMENT firmenname (#PCDATA)>
<!ELEMENT strasse (#PCDATA)>
<!ELEMENT hausnr (#PCDATA)>
<!ELEMENT postfach (#PCDATA)>
<!ELEMENT plz (#PCDATA)>
<!ELEMENT ort (#PCDATA)>
<!ELEMENT telefon (#PCDATA)>
<!ELEMENT positionen (position)+>
<!ELEMENT position (anzahl, artikelNr, artikelName, einzelpreis, betragNetto)>
<!ELEMENT nr (#PCDATA)>
<!ELEMENT anzahl (#PCDATA)>
<!ELEMENT artikelNr (#PCDATA)>
<!ELEMENT artikelName (#PCDATA)>
<!ELEMENT einzelpreis (#PCDATA)>
<!ELEMENT betragNetto (#PCDATA)>
<!ELEMENT summe (netto, mwst, brutto)>
<!ELEMENT netto (#PCDATA)>
<!ELEMENT mwst (#PCDATA)>
<!ELEMENT brutto (#PCDATA)>
<!ELEMENT status (statusmeldung)*>
<!ELEMENT statusmeldung EMPTY>
<!ELEMENT hinweise (notiz | text | weitereadresse)+>
<!ELEMENT notiz ANY>
<!ELEMENT text (#PCDATA)>
<!ELEMENT weitereadresse (#PCDATA |adresse)*>
<!ATTLIST rechnung
nr CDATA #REQUIRED
datum CDATA #REQUIRED >
<!ATTLIST adresse
kundennummer CDATA #IMPLIED >
<!ATTLIST position
nr ID #REQUIRED >
<!ATTLIST statusmeldung
datum CDATA #REQUIRED
statusart (vorbereitet | erstellt | versendet | bezahlt | angemahnt | unbekannt) #REQUIRED>
]>
<rechnung nr="1234" datum="12.01.2024">
<absender>
<adresse>
<firmenname>Baschira GmbH</firmenname>
<postfach>Postfach 12344</postfach>
<plz>10248</plz>
<ort>Berlin</ort>
<telefon>0124 / 6567891</telefon>
<telefon>0234 / 7567894</telefon>
</adresse>
</absender>
<empfaenger>
<adresse kundennummer="1234">
<vorname>Max</vorname>
<nachname>Mustermann</nachname>
<namenszusatz>3. OG links</namenszusatz>
<strasse>Hauptstraße</strasse>
<hausnr>23</hausnr>
<plz>12345</plz>
<ort>Berlin</ort>
</adresse>
</empfaenger>
<positionen>
<position nr="pos1">
<anzahl>2</anzahl>
<artikelNr>12345</artikelNr>
<artikelName>Produkt1</artikelName>
<einzelpreis>12.50</einzelpreis>
<betragNetto>25.00</betragNetto>
</position>
<position nr="pos2">
<anzahl>1</anzahl>
<artikelNr>54321</artikelNr>
<artikelName>Produkt2</artikelName>
<einzelpreis>75.00</einzelpreis>
<betragNetto>75.00</betragNetto>
</position>
</positionen>
<summe>
<netto>100.00</netto>
<mwst>19.00</mwst>
<brutto>119.00</brutto>
</summe>
<status>
<statusmeldung datum="22.02.2024" statusart="unbekannt" />
<statusmeldung datum="24.02.2024" statusart="vorbereitet" />
</status>
<hinweise>
<notiz>Dies ist eine Korrektur der Rechnung 1234 über <netto>80.00</netto> Euro.</notiz>
<text>Dies ist eine Korrektur der Rechnung 1234 über 80.00 Euro.</text>
<weitereadresse>
Versandadresse
<adresse>
<firmenname>Baschira GmbH Filiale Hamburg</firmenname>
<postfach>Postfach 12344</postfach>
<plz>20248</plz>
<ort>Hamburg</ort>
</adresse>
</weitereadresse>
<text>Noch ein Text</text>
</hinweise>
</rechnung>
Links und weitere Informationen
Infos von w3schools zu XML
Hinweis zur Nachnutzung als Open Educational Resource (OER)
Dieser Artikel und seine Texte, Bilder, Grafiken, Code und sonstiger Inhalt sind - sofern nicht anders angegeben - lizenziert unter CC BY 4.0. Nennung gemäß TULLU-Regel bitte wie folgt: “Übung: Erstellung einer Document-Type-Definition für XML-Rechnungen” von oer-informatik.de (H. Stein), Lizenz: CC BY 4.0. Der Artikel wurde unter https://oer-informatik.de/xml-dtd-uebung veröffentlicht, die Quelltexte sind in weiterverarbeitbarer Form verfügbar im Repository unter https://gitlab.com/oer-informatik/datenformate/xml. Stand: 26.02.2024.
[Kommentare zum Artikel lesen, schreiben] / [Artikel teilen] / [gitlab-Issue zum Artikel schreiben]