binarek
binarek

Reputation: 23

XSLT 3.0 iterate over JSON array wrapped in XML

I am processing various XML files with XSLT. In one XML I found a wrapped JSON list:

<list><![CDATA[[
    {
    "title": "Title 1",
    "value": "Value 1",
    "order": 1
    },
    {
    "title": "Title 2",
    "value": "Value 2",
    "order": 2
    }
]]]>
</list>

My problem is that I need to iterate over the list. For example:

<xsl:variable name="listVar">
    <!-- do something with list -->
</xsl:variable>
<xsl:for-each select="$listVar">
    <!-- do something with objects in list e.g. -->
    <xsl:value-of select="title"/>
    <xsl:value-of select="value"/>
</xsl:for-each>

How to do this with XSLT? I use XSLT 3.0 and Saxon engine, version 9.8 HE.

Considered solutions:

1. Use parse-json function:

But then I cannot iterate over the result because of XPathException: "Required item type of the context item for the child axis is node(); supplied value (.) has item type array(function(*))" or "Maps cannot be atomized". I found that there are functions that probably I should take into account like map:get, map:entry, but I've failed to use them in my case so far.

2. Addidiotnal transform before the one mentioned above:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                version="3.0">
    <xsl:output method="xml" encoding="UTF-8" indent="no"/>
    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="list">
        <list>
            <xsl:copy-of select="json-to-xml(.)"/>
        </list>
    </xsl:template>
</xsl:stylesheet>

And then:

<xsl:variable name="listVar" select="list/array/map"/>

But it does not work - probably due to added namespace

<list>
    <array xmlns="http://www.w3.org/2005/xpath-functions">
        <map>
...

Upvotes: 2

Views: 2940

Answers (1)

Martin Honnen
Martin Honnen

Reputation: 167641

Your JSON structure when parsed with parse-json gives you on the XSLT/XPath side an array of maps and the most straightforward way to process the individual array items is with the ?* lookup operator, then you can use for-each or even apply-templates:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:math="http://www.w3.org/2005/xpath-functions/math"
    exclude-result-prefixes="xs math"
    version="3.0">

    <xsl:template match="list">
        <xsl:apply-templates select="parse-json(.)?*"/>
    </xsl:template>

    <xsl:template match=".[. instance of map(xs:string, item())]">
        <xsl:value-of select="?title, ?value"/>
    </xsl:template>
</xsl:stylesheet>

where to access the map values you can again use ?foo as shown above.

As for working with the XML returned by json-to-xml, it returns elements in the XPath function namespace so to select them, as with any other elements in a namespace you need to make sure you set up a namespace with e.g. xpath-default-namespace for the section you want to process elements from that namespace or you can use the namespace wild card e.g. *:array/*:map.

Upvotes: 1

Related Questions