David Maus

Hallo XSLT 3.0

Gestern hatte ich erstmals die Gelegenheit, einige der Neuerung in XSLT 3.0 nutzen zu können. Die (selbstgestellte) Aufgabe bestand darin, die RDF/XML-kodierten Metadaten zu meiner Person in JSON-LD umzuwandeln.

Im Zentrum stand die mit XSLT 3.0 eingeführte native Verarbeitung von JSON. Die Funktionen json-to-xml und xml-to-json wandeln JSON in XML bzw. umgekehrt XML in JSON. Die Spezifikation definiert eine XML-Repräsentation, die Ausgangs- bzw. Endpunkt der Umwandlung ist.

Die Hauptaufgabe meiner Transformation besteht darin, die in RDF/XML kodierten Daten in die XML-Repräsentation von JSON umzuwandeln. Diese Umwandlung ist straight forward: Ich erstelle eine json:map, die ich mit Schlüssel-Wert-Paaren der ausgewählten Informationen befülle.

Da ich die Umwandlung im Stile des push processing (Kay 2008, 74ff) schreibe, muss ich sie in zwei Schritten vornehmen. Der erste Schritt erstellt Einträge in der json:map ohne gleiche Eigenschaften zu gruppieren.

Also aus

<foaf:Person rdf:about="http://dmaus.name#me">  <owl:sameAs rdf:resource="http://orcid.org/0000-0001-9292-5673"/>  <owl:sameAs rdf:resource="https://orcid.org/0000-0001-9292-5673"/>  <owl:sameAs rdf:resource="http://d-nb.info/gnd/1141920794"/></foaf:Person>

und

<xsl:template match="owl:sameAs">  <json:string key="sameAs">{@rdf:resource}</json:string></xsl:template>

wird im ersten Schritt

<json:map>  <json:string key="@context">http://schema.org</json:string>  <json:string key="@type">Person</json:string>  <json:string key="@id">http://dmaus.name#me</json:string>  <json:string key="sameAs">http://orcid.org/0000-0001-9292-5673</json:string>  <json:string key="sameAs">https://orcid.org/0000-0001-9292-5673</json:string>  <json:string key="sameAs">http://d-nb.info/gnd/1141920794</json:string></json:map>

Der zweite Schritt gruppiert gleiche Eigenschaften in ein json:array.

xsl:template name="normalize">  <xsl:param name="json-map" required="true"/>  <json:map>    <xsl:for-each-group select="$json-map/*" group-by="@key">      <xsl:choose>        <xsl:when test="count(current-group()) eq 1">          <xsl:sequence select="current-group()"/>        </xsl:when>        <xsl:otherwise>          <json:array key="{current-grouping-key()}">            <xsl:for-each select="current-group()">              <xsl:copy>                <xsl:sequence select="node()"/>              </xsl:copy>            </xsl:for-each>          </json:array>        </xsl:otherwise>      </xsl:choose>    </xsl:for-each-group>  </json:map></xsl:template>

Das Ergebnis des zweiten Schritts kann ich dann mit xml-to-json umwandeln und in meine Webseite einfügen.

<xsl:template match="foaf:Person[@rdf:about = $me]">  <xsl:variable name="json-ld">    <xsl:apply-templates select="." mode="json.header"/>  </xsl:variable>  <script type="application/ld+json">    <xsl:value-of select="xml-to-json($json-ld)"/>  </script></xsl:template>

Merke, dass ich im ersten Schritt auch ein weiteres Feature von XSLT 3.0 benutze. Mit der Einführung von text value templates wurde das Konzept der bereits in XSLT 1.0 definierten attribute value templates verallgemeinert. Wenn die Expansion von text value templates aktiviert ist, dann lassen sich auch in Elementinhalten XPath-Ausdrücke in geschweifte Klammern fassen, die während der Transformation mit dem Ergebnis des XPath-Ausdrucks ersetzt werden.