Block 06 · Schnittstellen & Datenformate – Lösungen

Lösungssatz zu 06_Schnittstellen_Datenformate.html · Pseudocode im IHK-Stil, dazu lauffähige Java-Dateien (nur JDK) im Ordner java\06_Schnittstellen_Datenformate\ (Start mit java Aufgabe_SD_NN_k.java)
Hinweise zur Bewertung Wie in den IHK-Lösungshinweisen gilt: Andere sach- und fachgerechte Lösungen werden mit voller Punktzahl bewertet. Bei „nennen“ genügen Stichworte, bei „erläutern“ und „beschreiben“ wird ein ganzer Satz mit Begründung oder Bezug zur Situation erwartet. In Pseudocode kosten Syntaxfehler in der Regel keine Punkte, Logikfehler schon. Alle XML-, JSON- und Java-Lösungen wurden maschinell geprüft (JDK 21 bzw. Python); die gezeigten Ausgaben stammen aus echten Programmläufen.

Lösungen

SD-01 · XML

Aufgabe SD-01.1 · 6 Punkte

a) Bestandteile (2 P)

BestandteilBezeichnung
<?xml version="1.0" encoding="UTF-8"?>XML-Deklaration (Prolog) mit Version und Zeichenkodierung
Element lieferung (Zeilen 2–6)Wurzelelement (Root-Element), umschließt alle anderen Elemente
nr="88120"Attribut des Elements lieferung (Name nr, Wert 88120)
<ablage hinweis="Garage"/>leeres Element (ohne Inhalt, Kurzschreibweise ohne End-Tag) mit einem Attribut

b) Wohlgeformt und gültig (4 P)

EigenschaftErläuterungStatusmeldung
wohlgeformtDas Dokument hält die Syntaxregeln von XML ein: genau ein Wurzelelement, zu jedem Start-Tag ein passendes End-Tag (oder leeres Element), korrekte Verschachtelung, Attributwerte in Anführungszeichen, Groß-/Kleinschreibung beachtet. Das prüft jeder XML-Parser.ja – alle Regeln sind eingehalten, auch das leere Element ist korrekt geschlossen.
gültigDas Dokument ist wohlgeformt und entspricht zusätzlich einer Strukturbeschreibung (DTD oder XML-Schema): nur erlaubte Elemente und Attribute, richtige Reihenfolge und Anzahl, Pflichtangaben vorhanden.nein / nicht prüfbar – ohne DTD bzw. XSD gibt es keine Grammatik, gegen die geprüft werden kann.
Bewertung: a) je Bestandteil 0,5 P („Prolog“ für Zeile 1 und „Element mit Attribut“ für die letzte Zeile werden akzeptiert). b) je Erläuterung 1,5 P, je richtige Einordnung 0,5 P. „Gültig: ja“ ist falsch – Gültigkeit setzt eine DTD/XSD voraus.

Erklärung: Die Falle steckt in b): Viele schreiben „gültig, weil kein Fehler drin ist“. Gültigkeit ist aber immer relativ zu einer Grammatik. Ohne DTD/XSD ist ein Dokument höchstens wohlgeformt.

Aufgabe SD-01.2 · 10 Punkte

a) Deklarationen (4 P)

DeklarationBedeutung
<!ELEMENT erzeuger (name, telefon?, email+, hof)>Ein Erzeuger enthält in genau dieser Reihenfolge: genau einen Namen, optional eine Telefonnummer (0 oder 1), mindestens eine E-Mail-Adresse (1 bis beliebig viele) und genau einen Hof.
<!ELEMENT hof (adresse | koordinaten)>Ein Hof enthält entweder eine Adresse oder Koordinaten (Auswahl, genau eines von beiden).
<!ATTLIST erzeuger id ID #REQUIRED status (aktiv|pausiert) "aktiv">Das Element erzeuger hat das Pflichtattribut id, dessen Wert im Dokument eindeutig sein muss (Typ ID), und das optionale Attribut status mit den erlaubten Werten aktiv oder pausiert; fehlt es, gilt der Standardwert aktiv.
<xs:element name="zertifikat" type="xs:string" minOccurs="0" maxOccurs="unbounded"/>XML-Schema: Element zertifikat mit Textinhalt (Datentyp string), das fehlen oder beliebig oft vorkommen darf (0..n) – entspricht zertifikat* in einer DTD.

b) DTD für den Lieferschein (6 P)

 1  <!ELEMENT lieferschein (kunde, position+, fahrer?)>
 2  <!ATTLIST lieferschein nr    CDATA #REQUIRED
 3                         datum CDATA #REQUIRED>
 4  <!ELEMENT kunde (name, adresse)>
 5  <!ATTLIST kunde kundenNr CDATA #REQUIRED>
 6  <!ELEMENT name (#PCDATA)>
 7  <!ELEMENT adresse (#PCDATA)>
 8  <!ELEMENT position (kiste, anzahl, bemerkung*)>
 9  <!ELEMENT kiste (#PCDATA)>
10  <!ELEMENT anzahl (#PCDATA)>
11  <!ELEMENT bemerkung (#PCDATA)>
12  <!ELEMENT fahrer (#PCDATA)>

Test · Aufgabe_SD_01_2.java (JDK-Parser mit Validierung, DTD per EntityResolver eingebunden)

--- Lieferschein aus Anlage 6
    gültig (wohlgeformt und DTD-konform)
--- Negativtest 1: keine Position (position+ verlangt mindestens eine)
    ungültig, Zeile 9: Content des Elementtyps "lieferschein" muss "(kunde,position+,fahrer?)" entsprechen.
--- Negativtest 2: Pflichtattribut nr fehlt
    ungültig, Zeile 3: Attribut "nr" ist erforderlich und muss für Elementtyp "lieferschein" angegeben werden.
--- Negativtest 3: anzahl vor kiste (Reihenfolge der Sequenz)
    ungültig, Zeile 11: Content des Elementtyps "position" muss "(kiste,anzahl,bemerkung*)" entsprechen.
--- Negativtest 4: zwei Fahrer (fahrer? erlaubt höchstens einen)
    ungültig, Zeile 20: Content des Elementtyps "lieferschein" muss "(kunde,position+,fahrer?)" entsprechen.
--- Positivtest: ohne Fahrer (optional) und ohne Bemerkung
    gültig (wohlgeformt und DTD-konform)
Bewertung: a) je Zeile 1 P (Zeile 1: Reihenfolge und alle drei Kardinalitäten; Zeile 3: ID/Pflicht 0,5 P, Aufzählung mit Standardwert 0,5 P). b) Inhaltsmodell lieferschein mit position+ und fahrer? 1,5 P; ATTLIST lieferschein 1 P; kunde mit ATTLIST 1 P; position mit bemerkung* 1,5 P; restliche Elemente als (#PCDATA) 1 P. Die Reihenfolge der Deklarationen ist beliebig; je Attribut eine eigene ATTLIST ist ebenso richtig. nr als Typ ID wird akzeptiert (der Wert beginnt mit einem Buchstaben).

Erklärung: Jede Regel des Aufgabentexts wird zu genau einer Angabe: „genau ein“ = ohne Zeichen, „mindestens ein“ = +, „optional“ = ?, „beliebig viele, auch keine“ = *. Typischer Fehler: bemerkung+ (verlangt mindestens eine Bemerkung) oder Attribute als Kindelemente deklarieren.

Aufgabe SD-01.3 · 16 Punkte

a) Zweck der DTD (2 P)

Die DTD legt verbindlich fest, wie ein Wochenangebot aufgebaut sein muss: welche Elemente und Attribute erlaubt sind, in welcher Reihenfolge und Anzahl sie vorkommen und welche Angaben Pflicht sind. Sie ist damit die gemeinsame Schnittstellenvereinbarung zwischen den 28 Erzeugerbetrieben und dem KistenPortal. Mit ihr kann jede hochgeladene Datei vor dem Import automatisch validiert werden; fehlerhafte Dateien werden mit einer Meldung abgewiesen, statt falsche Daten zu importieren.

b) Fehler im Wochenangebot (8 P)

ZeileBeschreibungEigenschaft
3Pflichtattribut jahr fehlt (#REQUIRED).Gültigkeit
15End-Tag </Bezeichnung> passt nicht zum Start-Tag <bezeichnung> (Groß-/Kleinschreibung).Wohlgeformtheit
22–23preis steht vor menge; die DTD verlangt die Reihenfolge (bezeichnung, menge, preis, hinweis*).Gültigkeit
25Der Wert A-101 des ID-Attributs artNr ist bereits in Zeile 9 vergeben; ID-Werte müssen eindeutig sein.Gültigkeit
25einheit="Kilo" ist kein erlaubter Wert; zulässig sind nur kg, stueck, bund.Gültigkeit
27Das End-Tag von menge fehlt der Schrägstrich (<menge> statt </menge>); das Element wird nicht geschlossen.Wohlgeformtheit
29(vorgegeben) Element lieferant ist in der DTD nicht deklariert.Gültigkeit
31Attributwert A-105 steht nicht in Anführungszeichen.Wohlgeformtheit

Kontrolle mit dem JDK-Parser · Aufgabe_SD_01_3.java (Auszug der Ausgabe)

--- 1a) Originaldatei Anlage 5
    GÜLTIGKEIT     Zeile 3: Attribut "jahr" ist erforderlich und muss für Elementtyp "wochenangebot" angegeben werden.
    WOHLGEFORMTHEIT Zeile 15: Elementtyp "bezeichnung" muss mit dem entsprechenden Endtag "</bezeichnung>" beendet werden.  -> Parser bricht ab
--- 1b) nach Korrektur Zeile 15
    ... (Gültigkeitsfehler Zeilen 3, 24, 25, 25, 29)
    WOHLGEFORMTHEIT Zeile 30: Elementtyp "menge" muss mit dem entsprechenden Endtag "</menge>" beendet werden.  -> Parser bricht ab
--- 1c) nach Korrektur Zeile 27
    ...
    WOHLGEFORMTHEIT Zeile 31: Öffnendes Anführungszeichen wird für Attribut "artNr" erwartet, das mit Elementtyp "artikel" verknüpft ist.  -> Parser bricht ab
--- 2) nach Korrektur Zeile 31: wohlgeformt, Prüfung gegen die DTD
    GÜLTIGKEIT     Zeile 3: Attribut "jahr" ist erforderlich und muss für Elementtyp "wochenangebot" angegeben werden.
    GÜLTIGKEIT     Zeile 24: Content des Elementtyps "artikel" muss "(bezeichnung,menge,preis,hinweis*)" entsprechen.
    GÜLTIGKEIT     Zeile 25: Attributwert "A-101" mit dem Typ ID muss eindeutig im Dokument sein.
    GÜLTIGKEIT     Zeile 25: Attribut "einheit" mit Wert "Kilo" muss einen Wert aus der Liste "kg stueck bund " haben.
    GÜLTIGKEIT     Zeile 29: Elementtyp "lieferant" muss deklariert werden.
    GÜLTIGKEIT     Zeile 30: Content des Elementtyps "artikel" muss "(bezeichnung,menge,preis,hinweis*)" entsprechen.
--- 3) korrigierte Fassung
    gültig (wohlgeformt und DTD-konform)

Der Parser meldet Verstöße gegen das Inhaltsmodell erst beim End-Tag (Zeile 24 bzw. 30) und den fehlenden Schrägstrich aus Zeile 27 erst in Zeile 30, wo das falsche End-Tag </artikel> auftaucht. In der Prüfung gibst du die Zeile an, in der der Fehler entsteht. Die vollständig korrigierte Datei steht in Aufgabe_SD_01_3.java (Konstante KORRIGIERT); in Zeile 25 wurde dort die neue Artikelnummer A-104 und die Einheit kg gewählt.

c) Gültig, aber unsinnig (4 P)

Warum die Prüfung bestanden wird: In der DTD sind menge und preis als (#PCDATA) deklariert, also als beliebiger Text. Eine DTD kennt keine Datentypen und keine Wertebereiche; sie prüft nur, ob die Elemente vorhanden sind und in der richtigen Reihenfolge stehen. „ca. 80“ und „-1.90“ sind Text und damit zulässig.

Maßnahmen (zwei davon):

<xs:element name="artikel">
  <xs:complexType>
    <xs:sequence>
      <xs:element name="bezeichnung" type="xs:string"/>
      <xs:element name="menge" type="xs:positiveInteger"/>
      <xs:element name="preis">
        <xs:simpleType>
          <xs:restriction base="xs:decimal">
            <xs:minExclusive value="0"/>
            <xs:fractionDigits value="2"/>
          </xs:restriction>
        </xs:simpleType>
      </xs:element>
      <xs:element name="hinweis" type="xs:string" minOccurs="0" maxOccurs="unbounded"/>
    </xs:sequence>
    <xs:attribute name="artNr" type="xs:ID" use="required"/>
    <xs:attribute name="einheit" default="kg">
      <xs:simpleType>
        <xs:restriction base="xs:string">
          <xs:enumeration value="kg"/>
          <xs:enumeration value="stueck"/>
          <xs:enumeration value="bund"/>
        </xs:restriction>
      </xs:simpleType>
    </xs:attribute>
  </xs:complexType>
</xs:element>

Ausgabe des Tests mit diesem Schema

wochenangebot_korrigiert.xml: XSD-gueltig
wochenangebot_kw47.xml: Zeile 10 cvc-datatype-valid.1.2.1: 'ca. 80' ist kein gültiger Wert für 'integer'.

Ausgabe von Aufgabe_SD_01_3.java (eigene Plausibilitätsprüfung nach erfolgreicher DTD-Validierung)

--- 4) Datei KW 47 gegen die DTD
    gültig (wohlgeformt und DTD-konform)
    ... aber fachliche Prüfung:
    PLAUSIBILITÄT A-110: Menge ist keine ganze Zahl ("ca. 80")
    PLAUSIBILITÄT A-110: Preis muss positiv sein (-1.90)

d) Vorteile eines XML-Schemas (2 P)

Bewertung: a) 2 P (Struktur festlegen 1 P, automatische Prüfung/Zurückweisen vor dem Import 1 P). b) je Fehler 2 P (Zeile und Beschreibung 1,5 P, Eigenschaft 0,5 P), höchstens vier Fehler werden gewertet; Zeile 24 statt 22/23 bzw. 30 statt 27 wird akzeptiert, wenn die Beschreibung stimmt. c) Begründung (nur Text, keine Datentypen) 2 P, je Maßnahme 1 P. d) je Vorteil 1 P.

Erklärung: b) ist die Aufgabe aus W22 in neuem Gewand: erst alle Tags auf Wohlgeformtheit prüfen (Zeilen 15, 27, 31), dann Element für Element gegen die DTD (Zeilen 3, 22/23, 25, 29). c) und d) zeigen die Grenze der DTD, die in W25 bei der eRechnung abgefragt wurde.

SD-02 · JSON

Aufgabe SD-02.1 · 6 Punkte

a) Objekt und Array (2 P)

Ein Objekt steht in geschweiften Klammern { } und ist eine ungeordnete Menge von Schlüssel-Wert-Paaren; auf einen Wert greift man über seinen Namen (Schlüssel, immer ein String) zu, z. B. eine Kiste mit "kisteId" und "preis". Ein Array steht in eckigen Klammern [ ] und ist eine geordnete Liste von Werten ohne Schlüssel; auf einen Wert greift man über seine Position (Index) zu, z. B. eine Liste mehrerer Kisten.

b) Fehler (4 P)

ZeileFehlerKorrektur
2Schlüssel in einfachen Anführungszeichen"kisteId": "K-MIX-S",
3Dezimalkomma – das Komma trennt in JSON die Paare, 20 wäre ein Schlüssel ohne Anführungszeichen"preis": 14.20,
4False großgeschrieben – nur true/false sind erlaubt"bio": false,
5Komma nach dem letzten Element des Arrays["Äpfel", "Möhren", "Kartoffeln"]

Korrigiertes Dokument (mit Python json geprüft)

{
  "kisteId": "K-MIX-S",
  "preis": 14.20,
  "bio": false,
  "inhalt": ["Äpfel", "Möhren", "Kartoffeln"]
}
Bewertung: a) je Begriff 1 P (Klammern und Aufbau bzw. Zugriff). b) je Fehler mit Korrektur 1 P (0,5 P Fehler, 0,5 P Korrektur).
Aufgabe SD-02.2 · 10 Punkte

a) Tabelle Kiste als JSON (5 P)

{
  "kisten": [
    {
      "kisteId": "K-OBST-M",
      "bezeichnung": "Obstkiste mittel",
      "groesse": "M",
      "preis": 18.50,
      "bio": true,
      "gewichtKg": 4.5,
      "saisonEnde": null
    },
    {
      "kisteId": "K-GEM-L",
      "bezeichnung": "Gemüsekiste groß",
      "groesse": "L",
      "preis": 27.90,
      "bio": true,
      "gewichtKg": 8.0,
      "saisonEnde": "2027-03-31"
    },
    {
      "kisteId": "K-MIX-S",
      "bezeichnung": "Mischkiste klein",
      "groesse": "S",
      "preis": 14.20,
      "bio": false,
      "gewichtKg": 3.0,
      "saisonEnde": null
    }
  ]
}

b) Eingebettetes Array inhalt (3 P)

Das Objekt der Kiste K-OBST-M aus a) mit eingebettetem Inhalt:

{
  "kisteId": "K-OBST-M",
  "bezeichnung": "Obstkiste mittel",
  "groesse": "M",
  "preis": 18.50,
  "bio": true,
  "gewichtKg": 4.5,
  "saisonEnde": null,
  "inhalt": [
    { "artikel": "Äpfel Elstar", "menge": 1.5, "einheit": "kg" },
    { "artikel": "Birnen Conference", "menge": 1, "einheit": "kg" },
    { "artikel": "Bananen", "menge": 6, "einheit": "Stück" }
  ]
}

c) Gründe für JSON (2 P)

Bewertung: a) Struktur (Objekt mit Array kisten, drei Objekte, Klammern und Kommas) 2 P; Zahlen ohne Anführungszeichen 1 P; bio als true/false 1 P; leere Zellen als null (Weglassen des Schlüssels wird akzeptiert) und Datum als String 1 P. 18.5 statt 18.50 oder 8 statt 8.0 sind gleichwertig. b) Array mit drei Objekten 1 P, richtige Schlüssel und Werte 1 P, Datentypen (Menge als Zahl) 1 P. c) je Grund 1 P.

Erklärung: Die meisten Punktabzüge entstehen durch Datentypen: "ja" statt true, "18.50" als String, ein leerer String "" statt null. Alle Lösungen wurden mit Python json.load auf Syntax geprüft.

Aufgabe SD-02.3 · 20 Punkte

a) Kunden und Lieferadressen mit Referenz (5 P)

{
  "kunden": [
    {
      "kundenNr": 10427,
      "name": "Jana Petersen",
      "kundentyp": "privat",
      "email": "j.petersen@mail.example",
      "newsletter": true
    },
    {
      "kundenNr": 20015,
      "name": "Kita Sonnenblume",
      "kundentyp": "oeffentlich",
      "email": "verwaltung@kita-sonnenblume.example",
      "newsletter": false
    }
  ],
  "lieferadressen": [
    {
      "adrId": 1,
      "kundenNr": 10427,
      "strasse": "Lindenweg 4",
      "plz": "21391",
      "ort": "Heidmoor",
      "ablageort": "Garage"
    },
    {
      "adrId": 2,
      "kundenNr": 10427,
      "strasse": "Hafenstraße 18",
      "plz": "21423",
      "ort": "Kleinmoor",
      "ablageort": null
    },
    {
      "adrId": 3,
      "kundenNr": 20015,
      "strasse": "Am Mühlenbach 3",
      "plz": "21391",
      "ort": "Heidmoor",
      "ablageort": "Küche, Hintereingang"
    }
  ]
}

b) Lieferadressen eingebettet (3 P)

{
  "kundenNr": 10427,
  "name": "Jana Petersen",
  "kundentyp": "privat",
  "email": "j.petersen@mail.example",
  "newsletter": true,
  "lieferadressen": [
    {
      "adrId": 1,
      "strasse": "Lindenweg 4",
      "plz": "21391",
      "ort": "Heidmoor",
      "ablageort": "Garage"
    },
    {
      "adrId": 2,
      "strasse": "Hafenstraße 18",
      "plz": "21423",
      "ort": "Kleinmoor",
      "ablageort": null
    }
  ]
}

c) Embedding oder Referenz (4 P)

DatenbestandEntscheidungBegründung
(1) Bestellungen eines KundenReferenzDie Bestellungen wachsen unbegrenzt (mehrere Hundert je Kunde). Eingebettet würde das Kundendokument immer größer und langsamer zu laden, obwohl die App beim Öffnen des Kontos die Bestellungen gar nicht braucht. Bestellungen werden außerdem unabhängig abgefragt (z. B. nach Zeitraum). Daher eigene Sammlung bestellungen mit kundenNr.
(2) Positionen einer BestellungEmbeddingEs sind nur wenige (1–5), sie gehören ausschließlich zu dieser Bestellung, werden immer zusammen mit ihr angezeigt und ändern sich danach nicht mehr. Eingebettet liefert ein einziger Zugriff die vollständige Bestellung.

d) offeneStoppsAlsJson (8 P)

funktion offeneStoppsAlsJson(stopps : List<Stopp>) : string
  json : string := "["
  erster : boolean := true                    // wurde schon ein Objekt ausgegeben?
  zähle i von 0 bis stopps.size() - 1
    s : Stopp := stopps.get(i)
    wenn nicht s.isZugestellt() dann
      wenn nicht erster dann
        json := json + ", "                   // Komma nur ZWISCHEN den Objekten
      Ende wenn
      json := json + "{\"position\": " + s.getPosition()
                   + ", \"kundenNr\": " + s.getKundenNr()
                   + ", \"ort\": \"" + s.getOrt() + "\""
                   + ", \"kisten\": " + s.getAnzahlKisten() + "}"
      erster := false
    Ende wenn
  Ende zähle
  json := json + "]"
  rückgabe json
Ende funktion

Java · Aufgabe_SD_02_3.java (Auszug)

static String offeneStoppsAlsJson(List<Stopp> stopps) {
    String json = "[";
    boolean erster = true;
    for (int i = 0; i < stopps.size(); i++) {
        Stopp s = stopps.get(i);
        if (!s.isZugestellt()) {
            if (!erster) {
                json = json + ", ";                  // Trennzeichen nur ZWISCHEN Objekten
            }
            json = json + "{\"position\": " + s.getPosition()
                    + ", \"kundenNr\": " + s.getKundenNr()
                    + ", \"ort\": \"" + s.getOrt() + "\""   // Zeichenkette in Anführungszeichen
                    + ", \"kisten\": " + s.getAnzahlKisten() + "}";
            erster = false;
        }
    }
    json = json + "]";
    return json;
}

Ausgabe (die erste Zeile wurde zusätzlich mit Python json.loads als gültiges JSON bestätigt)

Tour T3 (Anlage 9):
[{"position": 2, "kundenNr": 20015, "ort": "Heidmoor", "kisten": 6}, {"position": 3, "kundenNr": 10533, "ort": "Wiesede", "kisten": 1}, {"position": 5, "kundenNr": 20022, "ort": "Wiesede", "kisten": 4}]
Leere Liste:            []
Alles zugestellt:       []
Nur letzter Stopp offen: [{"position": 2, "kundenNr": 20022, "ort": "Wiesede", "kisten": 4}]
Bewertung: a) Array kunden mit passenden Datentypen (newsletter als Boolean) 2 P; Array lieferadressen mit kundenNr als Verweis und null für den leeren Ablageort 2 P; Syntax 1 P. plz als Zahl ist syntaktisch korrekt und wird akzeptiert; fachlich besser ist ein String (Postleitzahlen können mit 0 beginnen). b) Kundendaten 1 P, Array mit zwei Adressobjekten 1 P, Syntax 1 P (die kundenNr in den eingebetteten Adressen ist überflüssig, aber kein Fehler). c) je Datenbestand 2 P (Entscheidung 0,5 P, Begründung 1,5 P); eine andere Entscheidung mit schlüssiger Begründung erhält Teilpunkte. d) Initialisierung ("[", Merker) 1 P; Schleife über alle Stopps 1 P; Bedingung „nicht zugestellt“ 1 P; Komma nur zwischen den Objekten 2 P; Objekt korrekt aufgebaut (Schlüssel und String-Werte in Anführungszeichen, Zahlen ohne) 2 P; "]" und Rückgabe 1 P. Gleichwertig: Teilstrings in einer Liste sammeln und mit ", " verbinden, oder am Ende ein überzähliges Komma entfernen (sofern der Fall „keine offenen Stopps“ richtig bleibt).

Erklärung: Der Klassiker ist die Kommaregel. Mit wenn i > 0 entstünde hier [, {"position": 2 …, weil Stopp 1 (Index 0) zugestellt ist und gar nicht ausgegeben wird – ungültiges JSON. Deshalb merkt sich die Lösung, ob schon ein Objekt geschrieben wurde. Zweite häufige Fehlerquelle: der Ort ohne maskierte Anführungszeichen.

SD-03 · REST-Grundlagen & HTTP-Methoden

Aufgabe SD-03.1 · 6 Punkte

a) CRUD-Tabelle (4 P)

OperationBeispiel im KistenPortalHTTP-Methode
C – Createneues Abo anlegen: /kunden/10427/abosPOST (PUT, wenn der Client die ID festlegt)
R – Readalle Kistenarten anzeigen: /kistenGET
U – UpdateLiefertag eines Abos ändern: /kunden/10427/abos/5531PUT (vollständig) oder PATCH (teilweise)
D – DeleteLieferadresse löschen: /kunden/10427/lieferadressen/2DELETE

b) Zustandslosigkeit (2 P)

Der Server speichert zwischen zwei Requests keinen Sitzungszustand der App. Jeder Request muss deshalb alle Informationen enthalten, die zu seiner Bearbeitung nötig sind: den API-Schlüssel bzw. das Token in jedem Request, die betroffene Ressource in der URI (z. B. Kundennummer) und alle Nutzdaten im Body. Der Server kann jeden Request unabhängig von vorherigen bearbeiten; dadurch kann z. B. jeder von mehreren Servern jeden Request beantworten (Lastverteilung).

Bewertung: a) je Zeile 1 P (Beispiel 0,5 P, Methode 0,5 P); für Create wird POST oder PUT, für Update PUT oder PATCH akzeptiert (wie in der IHK-Lösung S24). b) 2 P: kein gespeicherter Zustand auf dem Server 1 P, Folge für die Requests (alle Infos, Authentifizierung in jedem Request) 1 P.
Aufgabe SD-03.2 · 10 Punkte

a) Endpunkte (6 P)

Nr.MethodeEndpunktBemerkung
1GET/kisten?bio=true&groesse=LFilter als Query-Parameter
2PATCH/kunden/10427/abos/5531Body: {"status": "pausiert", "pausiertBis": "2026-11-24"}
3DELETE/kunden/10427/lieferadressen/2
4POST/bestellungen/88120/reklamationenBody: Daten der Reklamation
5PUT/kunden/10427/lieferadressen/1Body: vollständige neue Adresse
6GET/kunden/10427/bestellungen?von=2026-11-01&bis=2026-11-30auch: /bestellungen?kundenNr=10427&von=…&bis=…

b) Pfad- und Query-Parameter (2 P)

Pfadparameter sind Teil der Ressourcenadresse und identifizieren eine bestimmte Ressource, z. B. 10427 und 5531 in /kunden/10427/abos/5531 – ohne sie wäre nicht klar, welches Abo gemeint ist. Query-Parameter stehen nach dem ? als name=wert, sind meist optional und filtern, sortieren oder begrenzen eine Ergebnisliste, z. B. bio=true&groesse=L bei /kisten.

c) Zweck von /v1 (2 P)

/v1 kennzeichnet die Version der Schnittstelle. Wird die API später inkompatibel geändert (Felder umbenannt, Struktur geändert), wird sie als /v2 veröffentlicht, während /v1 weiterläuft. So funktionieren bereits installierte, nicht aktualisierte Apps und Partner-Anbindungen weiter und können schrittweise umgestellt werden.

Bewertung: a) je Zeile 1 P (Methode 0,5 P, Endpunkt 0,5 P); mit vorangestelltem /v1 ebenfalls richtig. Verben im Pfad (/abos/5531/pausieren) oder IDs als Query-Parameter (/abos?id=5531) erhalten für den Endpunkt keine Punkte. Zeile 2 mit PUT nur, wenn die vollständige Repräsentation gesendet wird. b), c) je 2 P (Erläuterung 1 P, Beispiel bzw. Folge für Clients 1 P).
Aufgabe SD-03.3 · 18 Punkte

a) Konzept einer REST-API (3 P)

Eine REST-API stellt die Daten des KistenPortals als Ressourcen bereit (Kisten, Kunden, Abos …), die jeweils über eine eindeutige URI adressiert werden. Clients greifen über die Standardmethoden von HTTP darauf zu (GET lesen, POST anlegen, PUT/PATCH ändern, DELETE löschen) und tauschen Repräsentationen der Ressourcen aus, meist im JSON-Format. Die Kommunikation ist zustandslos: Jede Anfrage enthält alle nötigen Informationen, der Server speichert keine Sitzung. Client und Server sind dadurch lose gekoppelt, sodass App, Webshop und Partner dieselbe Schnittstelle nutzen können.

b) Sicher und idempotent (5 P)

RequestsicheridempotentBegründung
GET /v1/kisten?bio=truejajaliest nur, ändert nichts auf dem Server
POST /v1/kunden/10427/abosneinneinjeder Aufruf legt ein weiteres Abo an
PUT …/abos/5531neinjaersetzt das Abo vollständig; nach jeder Wiederholung ist der Zustand derselbe
PATCH …/abos/5531 (status, pausiertBis)neinja (hier)setzt feste Werte; erneutes Senden ändert nichts mehr. PATCH ist im Allgemeinen nicht garantiert idempotent (z. B. relative Änderungen).
DELETE …/lieferadressen/2neinjanach dem ersten Aufruf ist die Adresse gelöscht; weitere Aufrufe ändern den Zustand nicht (Antwort ggf. 404)

c) Doppeltes Abo (4 P)

Ursache: „Abo abschließen“ sendet einen POST-Request, und POST ist nicht idempotent. Der erste Request ist beim Server angekommen und hat das Abo angelegt, nur die Antwort (201) ging bei der schlechten Verbindung verloren. Das erneute Tippen sendet einen zweiten POST, der ein zweites, identisches Abo anlegt.

Lösung: Die App erzeugt für jeden Bestellvorgang einen eindeutigen Idempotenzschlüssel (z. B. eine UUID) und sendet ihn in einem Header (z. B. Idempotency-Key) mit. Der Server merkt sich bereits verarbeitete Schlüssel und liefert bei einer Wiederholung die ursprüngliche Antwort zurück, statt ein neues Abo anzulegen. Weitere mögliche Lösungen: Die App vergibt die Abo-ID selbst und legt das Abo mit PUT /kunden/10427/abos/{uuid} an (idempotent); der Server prüft auf Dubletten (gleiche Kundin, gleiche Kiste innerhalb kurzer Zeit) und antwortet mit 409.

d) PUT und PATCH (4 P)

PUT /v1/kunden/10427/abos/5531 HTTP/1.1

{"aboId": 5531, "kundenNr": 10427, "kisteId": "K-OBST-M", "rhythmus": "woechentlich",
 "liefertag": "DO", "lieferadresseId": 1, "status": "aktiv", "pausiertBis": null}
PATCH /v1/kunden/10427/abos/5531 HTTP/1.1

{"liefertag": "DO"}

e) REST und SOAP (2 P)

Bewertung: a) je fachlich richtig erläutertem Aspekt 1 P, höchstens 3 P (Ressourcen/URI, HTTP-Methoden, Repräsentationen/JSON, zustandslos, lose Kopplung). b) je Zeile 1 P (beide Einträge richtig 0,5 P, Begründung 0,5 P); bei PATCH wird auch „nein“ mit Hinweis auf die allgemeine Nicht-Idempotenz voll bewertet. c) Ursache 2 P (POST nicht idempotent 1 P, Antwort verloren/zweiter Request 1 P), Lösung 2 P; „Button nach dem Tippen sperren“ allein 1 P, weil automatische Wiederholungen dadurch nicht verhindert werden. d) je Request 2 P (Request-Zeile mit richtiger Methode und URI 1 P, Body 1 P); beim PUT muss die vollständige Repräsentation gesendet werden, beim PATCH genügt der geänderte Wert. e) je Unterschied 1 P.

Erklärung: b) prüft Verständnis statt Auswendiglernen: Ob ein PATCH idempotent ist, hängt von der konkreten Änderung ab. Das Setzen fester Werte ist idempotent, „erhöhe um 1“ wäre es nicht. Der Body des PUT in d) wurde gegen die Repräsentation aus Anlage 2 geprüft (alle acht Felder, nur der Liefertag ist geändert).

SD-04 · HTTP-Request/Response & Statuscodes

Aufgabe SD-04.1 · 6 Punkte

a) Statuscode-Klassen (3 P)

CodeBedeutung
2xxErfolg: Der Request wurde empfangen, verstanden und erfolgreich verarbeitet (z. B. 200 OK, 201 Created).
4xxClient-Fehler: Die Anfrage des Clients ist fehlerhaft oder nicht erlaubt, z. B. falsche URL, fehlende Berechtigung oder ungültige Daten (z. B. 400, 401, 404). Sie muss vom Client geändert werden.
5xxServer-Fehler: Die Anfrage war in Ordnung, der Server konnte sie aber wegen eines eigenen Problems nicht ausführen (z. B. 500, 503).

b) Ursachen für 404 (2 P)

c) Statuscode bei Wartung (1 P)

503 Service Unavailable (idealerweise mit dem Header Retry-After).

Bewertung: a) je Klasse 1 P. b) je Ursache 1 P. „Die Kundin hat keine Abos“ ist bei sauberer API-Gestaltung kein 404 (sondern 200 mit leerer Liste) und wird nur mit dieser Einschränkung akzeptiert. c) 1 P.
Aufgabe SD-04.2 · 12 Punkte

a) Bestandteile des Requests (4 P)

BestandteilBedeutung
/v1/kunden/10427/abosPfad des Endpunkts (Ressource): Version 1 der API, Sammlung der Abos der Kundin 10427, in der das neue Abo angelegt wird
Content-Type / AcceptHeader: Content-Type gibt an, dass der Body JSON enthält; Accept, dass der Client die Antwort ebenfalls als JSON erwartet
X-API-Key: app-7Fq2-Lx91Header mit dem API-Schlüssel der Kunden-App; der Server prüft damit, ob die anfragende Anwendung zugriffsberechtigt ist
{"kisteId": "K-GEM-L", …}Body mit den Nutzdaten im JSON-Format: die Daten des neuen Abos (Gemüsekiste groß, 14-täglich, freitags, an Lieferadresse 2)

ba) Parameter der URL (3 P)

ParameterBeschreibung
start=… / ziel=…Koordinaten (Breiten- und Längengrad) des Start- und des Zielpunkts der Route, z. B. Lager und Kunde
profil=transporterFahrzeugprofil: Die Route wird für einen Transporter berechnet (andere erlaubte Straßen, Höhen- und Gewichtsgrenzen, Geschwindigkeiten als z. B. für Pkw oder Fahrrad)
apikey=GD-55K2-ZZ81API-Schlüssel, mit dem sich das KistenPortal beim Kartendienst ausweist (Zugriffsberechtigung, Abrechnung, Begrenzung der Anfragen)

bb) Nachteil des Schlüssels in der URL (1 P)

URLs werden in Server- und Proxy-Protokollen, im Browserverlauf und in Monitoring-Werkzeugen gespeichert; dort kann der Schlüssel ausgelesen und missbraucht werden (z. B. Anfragen auf Kosten der FrischeKiste). Besser: Übertragung im Header.

c) Statuscodes (4 P)

Nr.CodeBegründung
1400 Bad RequestDie Anfrage ist inhaltlich fehlerhaft (Pflichtfeld fehlt); der Client muss sie korrigieren. (422 Unprocessable Content wird ebenfalls akzeptiert.)
2403 ForbiddenDer Erzeugerbetrieb ist mit gültigem Schlüssel authentifiziert, als Partner aber nur zum Lesen berechtigt. 401 wäre falsch, weil der Schlüssel gültig ist.
3204 No ContentErfolgreich ausgeführt, die Antwort enthält bewusst keinen Body.
4301 Moved PermanentlyDie Ressource ist dauerhaft unter einer neuen Adresse erreichbar; der Header Location: /v1/kisten nennt sie, Clients sollen künftig die neue URL nutzen. (308 Permanent Redirect ebenfalls richtig.)
Bewertung: a) je Zeile 1 P. ba) je Parameter 1 P. bb) 1 P. c) je Situation 1 P (Code 0,5 P, Begründung 0,5 P); 405 bei (2) nur mit der Begründung, dass DELETE auf dieser Ressource grundsätzlich nicht angeboten wird (0,5 P).
Aufgabe SD-04.3 · 20 Punkte

a) createErrorResponse (8 P)

private funktion createErrorResponse(statusCode : integer, meldung : string) : HttpResponse
  body : string := "{\"status\": " + statusCode + ", \"fehler\": \"" + meldung + "\"}"
  response : HttpResponse := new HttpResponse()
  response.setStatus(statusCode)
  response.setHeader("Content-Type", "application/json; charset=utf-8")
  response.setHeader("Content-Length", body.byteLength().toString())   // Bytes, nicht Zeichen
  response.setBody(body)
  rückgabe response
Ende funktion

b) Zeichen und Bytes (3 P)

length() zählt Zeichen, Content-Length muss aber die Länge des Bodys in Bytes angeben. In UTF-8 belegen Umlaute und ß 2 Bytes, das Eurozeichen 3 Bytes. Enthält die Meldung solche Zeichen, ist der Wert zu klein: Der Client liest dann zu wenig Bytes und schneidet den Body ab (ungültiges JSON) bzw. der Rest wird als Beginn der nächsten Nachricht fehlinterpretiert.

Body {"status": 503, "fehler": "Katalog vorübergehend nicht verfügbar"}: 66 Zeichen, davon zweimal ü → Content-Length: 68.

c) handleGetKiste (9 P)

funktion handleGetKiste(request : HttpRequest) : HttpResponse
  // 1. Authentifizierung
  key : string := request.getHeader("X-API-Key")
  wenn key = null oder nicht schluessel.istGueltig(key) dann
    rückgabe createErrorResponse(401, "API-Schlüssel fehlt oder ist ungültig")
  Ende wenn
  // 2. Verfügbarkeit des Katalogs
  wenn nicht katalog.istVerfuegbar() dann
    rückgabe createErrorResponse(503, "Katalog vorübergehend nicht verfügbar")
  Ende wenn
  // 3. Kiste suchen
  kisteId : string := request.getPfadParameter("kisteId")
  kiste : Kiste := katalog.findeKiste(kisteId)
  wenn kiste = null dann
    rückgabe createErrorResponse(404, "Kiste " + kisteId + " nicht gefunden")
  Ende wenn
  // 4. Erfolg
  body : string := kiste.toJson()
  response : HttpResponse := new HttpResponse()
  response.setStatus(200)
  response.setHeader("Content-Type", "application/json; charset=utf-8")
  response.setHeader("Content-Length", body.byteLength().toString())
  response.setBody(body)
  rückgabe response
Ende funktion

Java · Aufgabe_SD_04_3.java (Auszug)

private HttpResponse createErrorResponse(int statusCode, String meldung) {
    String body = "{\"status\": " + statusCode + ", \"fehler\": \"" + meldung + "\"}";
    HttpResponse response = new HttpResponse();
    response.setStatus(statusCode);
    response.setHeader("Content-Type", "application/json; charset=utf-8");
    response.setHeader("Content-Length", String.valueOf(byteLength(body)));   // Bytes, nicht Zeichen!
    response.setBody(body);
    return response;
}

HttpResponse handleGetKiste(HttpRequest request) {
    String key = request.getHeader("X-API-Key");
    if (key == null || !schluessel.istGueltig(key)) {
        return createErrorResponse(401, "API-Schlüssel fehlt oder ist ungültig");
    }
    if (!katalog.istVerfuegbar()) {
        return createErrorResponse(503, "Katalog vorübergehend nicht verfügbar");
    }
    String kisteId = request.getPfadParameter("kisteId");
    Kiste kiste = katalog.findeKiste(kisteId);
    if (kiste == null) {
        return createErrorResponse(404, "Kiste " + kisteId + " nicht gefunden");
    }
    String body = kiste.toJson();
    HttpResponse response = new HttpResponse();
    response.setStatus(200);
    response.setHeader("Content-Type", "application/json; charset=utf-8");
    response.setHeader("Content-Length", String.valueOf(byteLength(body)));
    response.setBody(body);
    return response;
}

// byteLength() aus Anlage 4:
static int byteLength(String text) {
    return text.getBytes(StandardCharsets.UTF_8).length;
}

Ausgabe (Auszug)

=== 1) ohne X-API-Key
HTTP/1.1 401 Unauthorized
Content-Type: application/json; charset=utf-8
Content-Length: 68

{"status": 401, "fehler": "API-Schlüssel fehlt oder ist ungültig"}
[Kontrolle] Zeichen im Body: 66, Bytes (UTF-8): 68, Content-Length korrekt: true

=== 3) unbekannte Kiste
HTTP/1.1 404 Not Found
Content-Type: application/json; charset=utf-8
Content-Length: 57

{"status": 404, "fehler": "Kiste K-XXL-Z nicht gefunden"}
[Kontrolle] Zeichen im Body: 57, Bytes (UTF-8): 57, Content-Length korrekt: true

=== 4) Kiste gefunden
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Content-Length: 104

{"kisteId": "K-GEM-L", "bezeichnung": "Gemüsekiste groß", "groesse": "L", "preis": 27.90, "bio": true}
[Kontrolle] Zeichen im Body: 102, Bytes (UTF-8): 104, Content-Length korrekt: true

=== 5) Katalog nicht verfügbar (Teil b)
HTTP/1.1 503 Service Unavailable
Content-Type: application/json; charset=utf-8
Content-Length: 68

{"status": 503, "fehler": "Katalog vorübergehend nicht verfügbar"}
[Kontrolle] Zeichen im Body: 66, Bytes (UTF-8): 68, Content-Length korrekt: true
Bewertung: a) Body korrekt zusammengesetzt (maskierte Anführungszeichen, Statuscode als Zahl ohne, Meldung mit Anführungszeichen) 3 P; Objekt erzeugt und Statuscode gesetzt 1 P; Content-Type 1 P; Content-Length über byteLength() 2 P (mit length() 0 P); Body setzen und Rückgabe 1 P. b) Erläuterung 1 P, 66 Zeichen 1 P, 68 Bytes 1 P. c) Header lesen und auf fehlend/ungültig prüfen mit 401 2 P; 503-Prüfung 1 P; Pfadparameter lesen, findeKiste, 404 mit Kisten-ID in der Meldung 2 P; Erfolgsantwort mit Status 200, Body toJson(), beiden Headern 3 P; Reihenfolge eingehalten und nach jedem Fehler sofortige Rückgabe 1 P. Eine Variante mit sonst wenn-Kette statt frühem rückgabe ist gleichwertig.

Erklärung: Die Aufgabe entspricht W24 T2 A2a, erweitert um die Prüfreihenfolge. Typische Fehler: kein rückgabe nach einem Fehlerfall (die Methode läuft dann weiter und überschreibt die Antwort), istGueltig(null) vor der Null-Prüfung, Content-Length in Zeichen, und im 404-Text der wörtliche Text „kisteId“ statt des Werts der Variablen.

SD-05 · eRechnung

Aufgabe SD-05.1 · 6 Punkte

a) Hauptziel (2 P)

Das Hauptziel einer eRechnung ist die automatische, elektronische Verarbeitung beim Empfänger: Die Rechnungsdaten liegen strukturiert (als XML) vor und können ohne Abtippen direkt in Buchhaltung bzw. ERP-System übernommen, geprüft und gebucht werden. Das spart Zeit und Kosten und vermeidet Erfassungsfehler. Eine für Menschen lesbare Darstellung ist dabei zweitrangig.

b) eRechnung oder nicht? (4 P)

Nr.eRechnung?Begründung
1neinEine PDF enthält keine strukturierten Daten, sondern nur eine Darstellung für Menschen („sonstige Rechnung“). Die Daten müssten abgetippt oder per Texterkennung gelesen werden.
2jaXRechnung ist ein strukturiertes XML-Format nach EN 16931 und maschinell verarbeitbar; der Übertragungsweg (Portal) spielt keine Rolle.
3neinEin Bild enthält keine strukturierten Daten; es ist eine digitalisierte Papierrechnung.
4jaZUGFeRD ist ein hybrides Format: Die in das PDF/A-3 eingebettete XML-Datei entspricht im Profil EN 16931 der Norm und kann automatisch verarbeitet werden.
Bewertung: a) automatische Verarbeitung 1 P, Erläuterung (strukturierte Daten, kein manuelles Erfassen, Darstellung zweitrangig) 1 P. b) je Zeile 1 P (Entscheidung 0,5 P, Begründung 0,5 P).
Aufgabe SD-05.2 · 10 Punkte

a) Empfangen und ausstellen (3 P)

b) XRechnung und ZUGFeRD (4 P)

XRechnung ist ein rein XML-basiertes Format: Die Rechnung besteht nur aus strukturierten Daten für die Software (Datenaspekt). Ein Mensch kann sie nur mit einem Anzeigeprogramm (Viewer) lesen. Viele Behörden verlangen XRechnung und nutzen die Leitweg-ID, um die Rechnung an die zuständige Stelle weiterzuleiten.

ZUGFeRD ist ein hybrides Format: ein PDF/A-3-Dokument, das ein Mensch direkt lesen kann, mit einer eingebetteten XML-Datei, die die Software verarbeitet. Es vereint also visuellen und Datenaspekt. Weichen beide Teile voneinander ab, gelten die XML-Daten.

c) PDF-Rechnungen im Jahr 2027 (3 P)

Geschäftskunden (B2B): Nicht mehr zulässig. Die FrischeKiste erwartet für 2026 einen Umsatz von rund 4,6 Mio. € und liegt damit deutlich über 800.000 €. Die Übergangsregel für 2027 gilt daher nicht; ab dem 1. Januar 2027 müssen Rechnungen an Geschäftskunden als eRechnung ausgestellt werden (ausgenommen Kleinbetragsrechnungen bis 250 €).

Privatkunden (B2C): weiterhin zulässig, denn Rechnungen an Privatkunden sind von der Pflicht nicht betroffen.

Bewertung: a) Empfang ab 2025 1 P, Übergang 2025/2026 und 2027 mit 800.000-€-Grenze 1 P, Pflicht ab 2028 1 P. b) je Format 2 P (Aufbau 1 P, Folge für Mensch bzw. Software 1 P). c) Geschäftskunden mit Begründung über den Umsatz 2 P, Privatkunden 1 P. Antworten in englischer Sprache oder wörtliche Übersetzungen ohne eigene Formulierung erhalten Abzüge.

Erklärung: c) verlangt, den Text auf die Situation anzuwenden: Entscheidend ist der Umsatz des Vorjahres (2026) – diese Angabe steht in Anlage 1, nicht im Text.

Aufgabe SD-05.3 · 18 Punkte

a) Konformität sicherstellen (3 P)

b) Online-Prüfseite (4 P)

c) Warum die Schema-Validierung nicht reicht (6 P)

ZeileFehler
5Das Fälligkeitsdatum (16.11.2026) liegt vor dem Rechnungsdatum (30.11.2026).
25 (29)Der Nettobetrag der Position 2 ist falsch: 4 × 27,90 € = 111,60 €, angegeben sind 101,60 €. Dadurch stimmt auch die Summe der Positionen (148,00 € + 101,60 € = 249,60 €) nicht mit dem Nettogesamtbetrag von 259,60 € (Zeile 29) überein.
31Der Bruttogesamtbetrag ist falsch: 259,60 € + 18,17 € = 277,77 €, angegeben sind 278,77 €.

Erläuterung: Das XML-Schema prüft nur die Struktur (Elemente, Reihenfolge, Pflichtangaben) und die Datentypen einzelner Werte – hier, dass die Datumsangaben gültige Daten und die Beträge Dezimalzahlen sind. Das ist bei allen Werten der Fall. Logische und rechnerische Zusammenhänge zwischen mehreren Werten (Fälligkeit nach Rechnungsdatum, Menge × Preis = Nettobetrag, Summen) lassen sich mit einer XSD nicht ausdrücken. Dafür sind zusätzlich Geschäftsregeln nötig, z. B. die Schematron-Regeln der EN 16931/XRechnung im Validator sowie Plausibilitätsprüfungen und Tests der eigenen Berechnungslogik.

Kontrolle · Aufgabe_SD_05_3.java (Schema-Validierung mit javax.xml.validation, danach Geschäftsregeln)

Schritt 1 - Schema-Validierung: XSD-gültig
Schritt 2 - Geschäftsregeln:
  Regel 1 verletzt: Fälligkeitsdatum 2026-11-16 liegt vor dem Rechnungsdatum 2026-11-30
  Regel 2 verletzt: Position 2: Menge x Einzelpreis = 111.60, angegeben 101.60
  Regel 3 verletzt: Summe der Positionsbeträge = 249.60, Nettogesamt angegeben 259.60
  Regel 5 verletzt: Netto + USt = 277.77, Bruttogesamt angegeben 278.77

Gegenprobe A (Dezimalkomma 18,50): XSD-Fehler in Zeile 17: cvc-datatype-valid.1.2.1: '18,50' ist kein gültiger Wert für 'decimal'.
Gegenprobe B (ungültiges Datum 2026-11-31): XSD-Fehler in Zeile 4: cvc-datatype-valid.1.2.1: '2026-11-31' ist kein gültiger Wert für 'date'.
Gegenprobe C (Element summen fehlt): XSD-Fehler in Zeile 29: cvc-complex-type.2.4.b: Content des Elements 'rechnung' ist nicht vollständig. …

Die Gegenproben zeigen, was das Schema sehr wohl erkennt: falsche Datentypen, unmögliche Daten und fehlende Elemente. Die Umsatzsteuer (7 % von 259,60 € = 18,17 €) ist korrekt (Regel 4 nicht verletzt). Das verwendete Schema ist vereinfacht (Datei im Java-Programm); echte XRechnungen werden gegen die XSD von UBL bzw. CII geprüft.

d) Transportverschlüsselung (3 P)

da) TLS beruht auf einer hybriden Verschlüsselung: asymmetrisch für den sicheren Austausch eines Sitzungsschlüssels, symmetrisch für die schnelle Übertragung der Daten.

db) TLS verschlüsselt nur die jeweilige Teilstrecke (Mailprogramm → Mailserver, Mailserver → Mailserver). Auf jedem beteiligten Mailserver wird die Nachricht entschlüsselt und liegt dort im Klartext vor; Betreiber oder Angreifer mit Zugriff auf einen Server könnten sie lesen oder verändern. Alternative: Ende-zu-Ende-Verschlüsselung der E-Mail mit S/MIME oder PGP – oder die Übermittlung über Peppol bzw. ein Rechnungsportal (HTTPS).

e) Leitweg-ID (2 P)

Die Leitweg-ID ist eine eindeutige Kennung des öffentlichen Rechnungsempfängers (hier der Stadt Heidmoor bzw. der Kita Sonnenblume). Der Auftraggeber teilt sie mit, und sie muss in der XRechnung angegeben werden (Käuferreferenz). Mit ihr wird die Rechnung im Rechnungseingangssystem der Verwaltung automatisch an die zuständige Stelle weitergeleitet und zugeordnet; fehlt sie, wird die Rechnung in der Regel abgelehnt.

Bewertung: a) je Möglichkeit 1 P. b) je Punkt 2 P (Nennung 1 P, Erläuterung mit Begründung 1 P). c) je Fehler mit Zeile und Begründung 2 P (höchstens zwei Fehler werden gewertet), Erläuterung „Schema prüft nur Struktur und Datentypen, keine Zusammenhänge/Geschäftsregeln“ 2 P. d) da) 1 P, db) Begründung 1 P, Alternative 1 P. e) 2 P (Kennung des Empfängers 1 P, Zweck Weiterleitung/Zuordnung 1 P).

Erklärung: c) entspricht W25 T1 A3bc: Dort war der Nettogesamtbetrag aus den Bruttowerten statt aus den Nettowerten gebildet – formal korrekt, fachlich falsch. Genau diese Klasse von Fehlern deckt eine XSD nie ab.