- 1 Intro
- 2 Anwendungsgebiete
- 2.1 XSLT - die Programmiersprache im XML Bereich
- 2.2 Aktuelle und vergangene Anwendungen
- 2.3 Professionelle XML Verarbeitung
- 2.4 Technische Dokumentation
- 3 Wichtige Konzepte
- 3.1 Push vs. Pull Stylesheets
- 3.2 Eindeutigkeit der Regelbasis
- 3.3 Namespaces
- 3.4 Schemata
- 3.5 Standards
- 3.5.1 DITA
- 3.5.2 DITA Inhaltsmodell
- 3.5.1 DITA
- 4 Ausgewählte Themen
- 4.1 Transformationen mit XSLT
- 4.1.1 Vortransformationen
- 4.1.2 Komplexe XML-2-XML Transformationen
- 4.1.2.8 Vererbung
- 4.1.2.8 Vererbung
- 4.1.3 XSLT Streaming
- 4.1.3.1 XSLT Akkumulator
- 4.1.3.2 XSLT Iterator
- 4.1.4 Reguläre Ausdrücke
- 4.1.5 Modus vs. Tunnel Lösung
- 4.1.6 Identifikation mit
generate-id()
- 4.1.6.4 XPath-Achsenbereich selektieren
- 4.1.6.4.1 Funktionen und Module
- 4.1.6.4.1 Funktionen und Module
- 4.1.6.4 XPath-Achsenbereich selektieren
- 4.1.7 Webservice Calls mit doc() und unparsed-text()
- 4.1.8 Stylesheet-Parameter auf der Kommandozeile
- 4.1.9 Leerzeichenbehandlung
- 4.1.10 Mit
translate
Zeichen ersetzen
- 4.1.10.1 Spass mit dem Sequenzvergleich
- 4.1.11 Character Mappings in der Ausgabe
- 4.1.12 JSON mit XSLT 1.0 und Python lxml
- 4.1.1 Vortransformationen
- 4.2 Abfragen mit XQuery
- 4.2.5 XQuery als Programmiersprache
- 4.2.5.3
if..then..else
Ausdrücke
- 4.2.5.3.2 SQL Views in MarkLogic
- 4.2.5.3
if..then..else
Ausdrücke
- 4.2.6 Hilfreiche XQuery Schippsel
- 4.2.5 XQuery als Programmiersprache
- 4.3 XML Datenbanken
- 4.3.1 Connector zu Marklogic in Oxygen
- 4.3.2 Bi-Temporale Dokumente
- 4.3.2.1 Anlegen des Testszenarios auf der ML Konsole
- 4.3.2.2 Ausführen einiger Beispiel-Queries
- 4.3.3 Webapps mit MarkLogic
- 4.3.3.5 Wikipedia Scrapper Applikation
- 4.3.3.5 Wikipedia Scrapper Applikation
- 4.3.4 Dokument-Rechte in MarkLogic
- 4.3.5 MarkLogic Tools
- 4.3.5.1 EXPath Konsole
- 4.3.5.2 mlcp - MarkLogic Content Pump
- 4.3.5.3 Deployment-Tools
- 4.4 XSL-FO mit XSLT1.x
- 4.5 Testing
- 4.5.1 Validierung mit Schematron
- 4.5.2 Erste Schritte mit XSpec
- 4.5.1 Validierung mit Schematron
- 4.6 Performanz-Optimierung
- 4.1 Transformationen mit XSLT
- 5 Zusätzliches Know-How
- 5.1 XML Editoren
- 5.2 Quellcode-Versionskontrolle
- 5.2.1 Kurze Geschichte zur Versionskontrolle Test
- 5.2.2 GIT Kommandos
- 5.2.1 Kurze Geschichte zur Versionskontrolle Test
- 5.1 XML Editoren
- 6 Glossary
- 7 Tektur CCMS
4.1.12 JSON mit XSLT 1.0 und Python lxml
Dabei steht nur XSLT 1.0 zur Verfügung mit den EXSLT ↗ Erweiterungen.
Unter diesen Bedingungen war es nicht ganz einfach eine Transformation zu erstellen, die XML als Eingabe nimmt und sauberes JSON als Ausgabe produziert. Sicherlich ist hierfür ein mehrstufiger Prozess erforderlich.
Um genau zu sein braucht man mindestens eine Vortransformation, die die JSON Elemente als XML erzeugt und ein Postprocessing, das möglichst generisch die spitzen Klammern auf geschweifte abbildet und einige Elementnamen verwirft.
Die folgenden JSON Strukturen lassen sich leicht identifizieren:
1. | Properties Dabei kann der propertyValue vom Typ String, Number oder Boolean sein. |
"propertyName" : propertyValue
2. | Benannte Objekte |
"objektName" : { ... }
3. | Bennante Listen |
"listenName" : [ { ... }, { ... }]
4. | Anonyme Objekte |
{ }
In Objekten können Properties, benannte Objekte oder benannte Listen enthalten sein. In benannten Listen können nur anonyme Objekte enthalten sein. Diese Unterscheidung hat erst einmal für meinen Anwendungsfall ausgereicht.
Umgemünzt auf XSLT Regeln, kommt man auf vier Regeln die anhand einer Attributbelegung für
@json
matchen:
<xsl:template match="*[@json='anonymous-object']" mode="post"> <xsl:text>{</xsl:text> <xsl:apply-templates select="node()|@*" mode="post"/> <xsl:text>}</xsl:text> <xsl:if test="following-sibling::*">,</xsl:if> </xsl:template> <xsl:template match="*[@json='named-list']" mode="post"> <xsl:text>"</xsl:text> <xsl:value-of select="name()"/> <xsl:text>"</xsl:text> <xsl:text>:[</xsl:text> <xsl:apply-templates select="node()|@*" mode="post"/> <xsl:text>]</xsl:text> <xsl:if test="following-sibling::*">,</xsl:if> </xsl:template> <xsl:template match="*[@json='named-object']" mode="post"> <xsl:text>"</xsl:text> <xsl:value-of select="name()"/> <xsl:text>"</xsl:text> <xsl:text>:{</xsl:text> <xsl:apply-templates select="node()|@*" mode="post"/> <xsl:text>}</xsl:text> <xsl:if test="following-sibling::*">,</xsl:if> </xsl:template> <xsl:template match="*[@json='property']" mode="post"> <xsl:text>"</xsl:text> <xsl:value-of select="name()"/> <xsl:choose> <xsl:when test="@type='boolean' or @type='integer'"> <xsl:text>":</xsl:text> <xsl:value-of select="."/> </xsl:when> <xsl:otherwise> <xsl:text>":"</xsl:text> <xsl:value-of select="."/> <xsl:text>"</xsl:text> </xsl:otherwise> </xsl:choose> <xsl:if test="following-sibling::*">,</xsl:if> </xsl:template>
In einer Vortransformation in eine Variable
pre
werden die JSON Strukturen erstmal als XML aufgebaut und dann in einem Postprocessing Step
post
auf die geschweiften Klammern gemappt:
<xsl:template match="/"> <root> <!-- first processing step --> <xsl:variable name="pre"> <xsl:copy> <xsl:apply-templates/> </xsl:copy> </xsl:variable> <!-- second processing step --> <xsl:apply-templates select="exsl:node-set($pre)" mode="post"/> </root> </xsl:template>
Das Aufbauen der XML-artigen JSON Struktur mit den annotierten @json Attributen, könnte z.B. so aussehen:
<xsl:template match="testsuite"> <sarif-report json="anonymous-object"> <runs json="named-list"> <run json="anonymous-object"> [...] </run> </runs> <version json="property">2.1.0</version> </sarif-report> </xsl:template>
Mit folgender finaler Ausgabe:
{ "runs" : [{ ... }], "version": "2.1.0" }
Da Python lxml bzw. libxslt anscheinend kein
xsl:output method="text"
beherscht, muss im Python Code der Textknoten eines Root-Elements ausgelesen werden. Wenn man diesen als JSON in ein Python dictionary liest und anschliessend wieder als JSON mit Indention herausschreibt, hat man auch gleich ein schönes Pretty-Printing der JSON Strukturen. Der relevante Python Code sieht dabei so aus:
with fileobj: # We register namespace functions that can be called inside the XSLT transformation register_stylesheet_functions() # Read the XSLT stylesheet xsl = etree.XML(open(SARIF_TRANSFORMATION_FILE, "r", encoding="utf-8").read()) # Transform the XML to Sarif JSON and put it into the root text node transform = etree.XSLT(xsl) result = transform(tree) if INDENT: # Load into python dict and pretty print when writing back to JSON jsn = json.loads(result.getroot().text) fileobj.write(json.dumps(jsn, indent=2)) else: fileobj.write(result.getroot().text)
Das vollständige Beispiel findet man in meinem Github Repo ↗.