Dieses Jahr hat wieder spannend begonnen. Direkt nach meinem Urlaub ging es mit einem BiPRO TAA Projekt für die Gewerbeversicherung eines deutschen Versicherungsunternehmens los. Während sich die Programmierung des BiPRO Servers relativ einfach gestaltete, traten bei der Umsetzung des BiPRO Clients diverse Probleme bei der Validierung des gesendeten XML auf. Genau darum geht es in diesem Artikel. Zu erwartende Probleme bei der Validierung von XML gegen das BiPRO Schema.
Grundsätzlich war ich bisher der Meinung, dass es vollkommen egal ist, in welcher Reihenfolge XML Elemente geliefert werden. Ich wurde aber eines Besseren belehrt. XML an sich ist es vollkommen egal, in welcher Reihenfolge die Elemente angeordnet sind. Erst, wenn gegen eine gültige XML Schema Definition (XSD) validiert wird, kann die Reihenfolge von Elementen eine gewichtige Rolle spielen. Wusste ich bisher auch nicht. Aber man lernt ja bekanntlich nie aus.
Das praktische Beispiel
Nehmen wir einfach ein Beispiel aus der Praxis. Zu sehen ist hier ein stark vereinfachtes XML Beispiel aus der BiPRO Praxis. Bei der Tarifierung kommt es durchaus mal vor, dass man diverse Konditionen zu einem Produkt mit liefern muss.
Ein sehr vereinfachtes XML Beispiel ohne Namespaces, welches die Nennung von zwei Konditionen innerhalb eines Produkts zeigt. Der folgende Code-Schnipsel ist eine stark vereinfachte XML Schema Definition, die wir auf den eben gezeigten XML Ausschnitt anwenden können.
Hier definieren wir lediglich, wie unser XML auszusehen hat. Im Grunde genommen ist hier einfach definiert, dass das Eltern-Element „Produkt“ das Kindelement „Kondition“ in einer nicht festgelegten Häufigkeit enthalten kann. Das Kindelement „Kondition“ muss aber mindestens einmal erscheinen. Der komplexe Typ „Kondition“ definiert in einer Sequenz, welche Elemente in welcher Häufigkeit enthalten sein dürfen.
Die Validierung
Die Validierung mit PHP ist an dieser Stelle denkbar einfach. Im folgenden Code Beispiel wird mittels der PHP DomDocument Klasse das XML eingelesen und gegen das XSD validiert.
Wird der oben gezeigte PHP Code nun ausgeführt, endet alles in einem hässlichen Fehler.
Element ‚ArtID‘: This element is not expected. Expected is one of ( Wert, Werteinheit, Grund )
PHP7.2 XML Validierung mit DomDocument
Wie jetzt? Das Element „ArtID“ wird nicht erwartet? Aber in der Schema Definition steht doch, dass ich ein „ArtID“ Element angeben kann. Wo liegt bitte das Problem? An diesem Punkt waren BiPRO und ich schon wieder keine Freunde mehr.
Die Lösung
Die Ursache ist die offizielle XML Schema Definition der W3C. Durch die Angabe einer Sequenz müssen die Elemente für das Kondition Objekt in der Reihenfolge angegeben werden, wie sie auch in der XSD Sequenz genannt sind. Anderenfalls ist das XML nicht valide. In diesem Fall wurde das Element „ArtID“ an zweiter Stelle genannt. Laut Definition in der XSD Datei muss das Element „ArtID“ aber vor dem „KategorieID“ Element erscheinen. Also wird bei der Validierung ein Fehler erzeugt.
Die Definition der W3C für den <sequence> Block in der XSD Datei lautet wie folgt:
Sequence (the element information items match the particles in sequential order)
https://www.w3.org/TR/xmlschema-1/
Sequenzen geben also die Reihenfolge der genannten Elemente vor. Alternativ könnte man <xsd:all> Blöcke verwenden. Allerdings sind diese auch sehr stark eingeschränkt, denn hier kann ich die enthaltenen Elemente nur kein- oder einmal nennen. Eine mehrfache Nennung von Elementen ist in <xsd:all> Blöcken nicht möglich. Ebenfalls keine Alternative sind <xsd:choice> Blöcke. Hier können die enthaltenen Elemente zwar kein-, einmal oder mehrfach genannt werden. Allerdings verhalten sich <xsd:choice> Blöcke wie XOR Anweisungen. Lediglich eines der genannten Elemente darf kein-, einmal oder mehrfach genannt werden. Der BiPRO e.V. macht also nichts verkehrt, wenn Elemente für komplexe Typen in <xsd:sequence> Blöcken angegeben werden. Eine genauere Definition könnte eine Kombination aus alles drei genannten Blöcken sein. Allerdings wird das XSD damit sehr groß und unübersichtlich.
Es handelt sich auch nicht um einen Bug im so oft gescholtenen PHP SoapClient oder gar im PHP SoapServer. Nein, auch PHP selbst ist diesmal nicht Schuld. Es ist eine einfache Definitionssache, die von der W3C vorgegeben wird. Eine Definition, die in allen anderen Programmiersprachen, sofern sie sich an die W3C Vorgaben halten, ebenfalls so umgesetzt wird.
Da ich mit PHP Entitäten arbeite und für jeden komplexen Typen, der im BiPRO Schema definiert ist, auch eine gesonderte PHP Klasse vorhalte, bleibt mir lediglich das Sortieren der Elemente vor dem Absenden. Komischerweise verhält sich die native PHP SoapClient Klasse in diesem Fall etwas undurchsichtig. Übergebe ich ein Konstrukt aus SoapVar Objekten als Parameter an eine Soap Funktion, scheint der Soap Client die Reihenfolge so anzunehmen, wie sie übergeben wurde. Übergebe ich lediglich ein Konstrukt aus einfachen PHP Objekten an eine SoapFunktion, scheint der SoapClient die enthaltenen Attribute eigenständig zu sortieren. Allerdings fehlte mir bisher die Zeit, um dies nachhaltig und stichfest zu testen.
Ich hoffe ich konnte ein wenig weiter helfen. Zumindest wissen wir jetzt, dass es eine Reihenfolge von XML Elementen gibt und woher diese Reihenfolge kommt. Haben wir wieder was gelernt. 😉
Download zum Artikel
Natürlich gibt es auch einen Download. In diesem ZIP Archiv sind sowohl die XML als auch die XSD Datei enthalten, die von der enthaltenen PHP Datei verwendet werden. Damit könnt ihr einfach mal selbst testen, wie sich die Validierung der Elemente verhält.