4.1.7  Webservice Calls mit doc() und unparsed-text()

Eine verbreitete Paxis ist es, mit der Funktion document() oder kurz doc() entfernte Ressourcen in die Transformation einzubinden. Bei einer Schematron-Validierung, würde bspw. eine Regel, wie:

<sch:not-assert id="personal-check"
    role="error"
    test="doc(concat('https://tekturcms.de/personal.xqy?personal-id=',personal-id))/kuendigung">
        Angestellter mit ID "<sch:value-of select="personal-id"/>" hat gekündigt!
</sch:not-assert>
Plain Text

einen entferneten Webservice aufrufen und prüfen, ob für den Angestellten mit personal-id eine Kündigung vorliegt. Ist dies der Fall, so ist die negative Zusicherung not-assert nicht erfüllt, und die Schematron Regel feuert - was sich wohl im einfachsten Fall in einem Logfile Eintrag äussern sollte.

Was vermutlich viele noch nicht kennen - ich nehme jetzt einfach mal an, dass mein bisheriger Kenntnisstand dem der Mehrheit der XML-Entwickler entspricht - ist der Umstand, dass auch die Funktion unparsed-text() eine URL als Parameter nimmt:

<xsl:template match="angestellter"
    <xsl:copy>
        <xsl:apply-templates select="node()|@*"/>
        <hat-gekuendigt>
            <xsl:sequence select="json-to-xml(
                                  unparsed-text(
                                  concat('https://tekturcms.de/personal.xqy?personal-id=',
                                  personal-id))))/descendant::*[@key='gekuendigt']/text()"/>
        </hat-gekuendigt>
    </xsl:copy>
</xsl:template>
Plain Text

Während mit doc() oder document() ein zurückgeliefertes XML Fragment prozessiert wird, erwartet unparsed-text() z.B. einen JSON-String, der dann mittels der Funktion json-to-xml() nach XML konvertiert werden kann.

Beispielsweise könnte die Gegenseite zum angestellter Template mittels XQuery folgendermassen realisiert sein:

xquery version "1.0-ml";

declare variable $personal-id := xdmp:get-request-field('personal-id');

let $gekuendigt := if (collection('/personal')/*[personal-id = $personal-id and 
                                                 fn:exists(kuendingung)] then 
                                                 'ja' else 'nein'
return 
    common:render-response(concat('{"gekuendigt":"',$gekuendigt,'",
                                    "personal-id":"',$personal-id,'"}'))
Plain Text

(Mehr zu XQuery und den hier verwendeten Konstrukten, wie render-response() )

Das zurückgeklieferte JSON Dokument sieht dann so aus:

{"gekuendig":"ja","personal-id":"q5687500"}
Plain Text

Konvertiert nach XML erhält man eine Map Struktur:

<map xmlns="http://www.w3.org/2005/xpath-functions">
    <string key="gekuedigt">ja</string>
    <string key="personal-id">q5687500</string>
</map>
Plain Text

was den Selektorausdruck im obigen XPATH erklärt:

json-to-xml(
unparsed-text(
concat('https://tekturcms.de/personal.xqy?personal-id=',
personal-id))))/descendant::*[@key='gekuendigt']/text()
Plain Text

Resultat der Konvertierung wäre - wie erwartet - ein um das gekuendigt Flag erweitertes <angestellter> Element:

<angestellter>
    <perosnal-id>q5687500</perosnal-id>
    <name>Alex</name>
    [...]
    <gekuendigt>nein</gekuendigt>
</angestellter>
Plain Text

Sicherlich wird der XML Entwickler eine XML Datenbank, wie MarkLogic, vorziehen und sich gleich XML Fragmente ausliefern lassen. Tektur ist aber bspw. mit MongoDB realisiert, die auf JSON arbeitet... Nicht zuletzt deshalb finde ich JSON Verarbeitung mit XSLT recht spannend.