XPath / XPath-Funktionen / XPath: Sequenz-Funktionen / JSON / XPath: fn:json-doc
![]() |
![]() |
➪ fn:json-doc liest eine externe Datei aus und gibt das Ergebnis als JSON zurück.
Wie in https://www.w3.org/TR/xpath-functions-31/#func-json-doc beschrieben, hat fn:json-doc zwei Signaturen:
Nehmen Sie die Datei ../input/json2.txt mit diesem Inhalt:
{"id":"1","name":"Holzflos","vorname":"Hugo"}
Die Auswertung mit fn:json-doc im Hinblick auf den Inhalt von name ...
<xsl:template match="/">
<Nachname>
<xsl:value-of
select="fn:json-doc('../input/json2.txt')?name"/>
</Nachname>
</xsl:template>
... ergibt:
<Nachname>Holzflos</Nachname>
Wenn die Datei ../input/json2.txt ein ganzes Array darstellt, wie in diesem Ansatz:
[
{"id":"1","name":"Holzflos","vorname":"Hugo"},
{"id":"2","name":"Türschlos","vorname":"Theo"}
]
... dann lässt sich das name-Property der jeweiligen JavaScript-Objekte so auswerten:
<xsl:template match="/">
<Nachnamen>
<xsl:for-each
select="fn:json-doc('../input/json2.txt')?*?name">
<NN>
<xsl:value-of select="."/>
</NN>
</xsl:for-each>
</Nachnamen>
</xsl:template>
Das Ergebnis sieht dann so aus:
<Nachnamen>
<NN>Holzflos</NN>
<NN>Türschlos</NN>
</Nachnamen>
Wer alle Informationen auf einen Schlag haben möchte, der kann mit den beliebten Schleifen <xsl:for-each select=".?*"> arbeiten. Das einzelne Feld im JSON-Array kann als map betrachtet werden, sodass hier auch mit der XPath-Funktion map:size gearbeitet werden kann.
<xsl:template match="/">
<Info>
<xsl:for-each
select="fn:json-doc('../input/json2.txt')?*">
<JS-Objekt anzahlItems="{map:size(.)}">
<xsl:for-each select=".?*">
<info>
<xsl:value-of select="."/>
</info>
</xsl:for-each>
</JS-Objekt>
</xsl:for-each>
</Info>
</xsl:template>
In diesem Fall erhalten Sie die Werte, aber noch ohne die Namen der Felder:
<Info>
<JS-Objekt anzahlItems="3">
<info>1</info>
<info>Holzflos</info>
<info>Hugo</info>
</JS-Objekt>
<JS-Objekt anzahlItems="3">
<info>2</info>
<info>Türschlos</info>
<info>Theo</info>
</JS-Objekt>
</Info>
Die Feldnamen brauchen Sie auch? Bitte sehr:
<xsl:template match="/">
<Info>
<xsl:for-each
select="fn:json-doc('../input/json2.txt')?*">
<JS-Objekt>
<xsl:for-each
select="map:for-each(.,
function ($k, $v){
fn:concat($k, '|', $v)
}
)">
<info name="{substring-before(xs:string(.), '|')}">
<xsl:value-of select="substring-after(., '|')"/>
</info>
</xsl:for-each>
</JS-Objekt>
</xsl:for-each>
</Info>
</xsl:template>
Die vorstehende Lösung funktioniert zwar, sie beruht jedoch auf der Verkettung der strukturierten JSON-Daten in einen String, der dann via substring-before und substring-after wieder aufgeplittet wird. Eine zweifellos bessere Alternative ist, die map in eine Sequenz von map-entries umzuwandeln und so die Datenstruktur zu erhalten (vielen Dank für die Hinweise!):
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:map="http://www.w3.org/2005/xpath-functions/map"
exclude-result-prefixes="map"
version="3.0">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/">
<Info>
<xsl:for-each select="json-doc('../input/json2.txt')?*">
<JS-Objekt>
<xsl:for-each
select="map:for-each(., function ($k, $v){ map:entry($k, $v) })">
<info name="{map:keys(.)}">
<xsl:value-of select="?*"/>
</info>
</xsl:for-each>
</JS-Objekt>
</xsl:for-each>
</Info>
</xsl:template>
</xsl:stylesheet>
Und so kann das Ergebnis dann aussehen:
<Info>
<JS-Objekt>
<info name="id">1</info>
<info name="name">Holzflos</info>
<info name="vorname">Hugo</info>
</JS-Objekt>
<JS-Objekt>
<info name="id">2</info>
<info name="name">Türschlos</info>
<info name="vorname">Theo</info>
</JS-Objekt>
</Info>
https://www.w3.org/TR/xpath-functions-31/#func-map-for-each weist darauf hin, dass die Reihenfolge des Resultats nicht festgelegt ist. Siehe auch XPath: map:for-each.
wg / 10. März 2019
Fragen? Anmerkungen? Tipps?
Bitte nehmen Sie Kontakt zu mir auf.
V.i.S.d.P.: Wilfried Grupe * Klus 6 * 37643 Negenborn
☎ 0151. 750 360 61 * eMail: info10@wilfried-grupe.de