How to traverse JSON in XSLT 3.0 when map vs array is unknown

Viewed 39

I am working with JSON data in XSLT 3.0 for the first time and have run into issue traversing a subset of the data depending on whether it is in map or array form.

Map:

"book": { "id": 32, "name": "Good Book" }

Array:

"book": [{ "id": 32, "name": "Good Book" }]

If I know whether it will be a map or array ahead of time, I can select a value from the data like "name", but the syntax for each is different.

For map:

?book?name

For array:

?book?*?name

The problem is that in our data set, there are multiple book nodes that come in a mix of map and array form and using either of the above syntaxes will result in an error when applied to the "wrong" form.

Am I missing a version of the selection syntax that would work on both forms?

Is there a way to test whether the book node is in map vs array form before selecting? (I've tried things like castable but it doesn't seem appropriate for this case.)

I have a workaround using try/catch:

<xsl:try select="?book?name">
  <xsl:catch>
    <xsl:value-of select="?book?*?name"/>
  </xsl:catch>
</xsl:try>

But I'm wondering if there is a "better" method for solving this issue.

Thanks for any ideas.

2 Answers

The best way in XSLT to deal with variable structure is through template rules. Unfortunately match patterns for maps and arrays aren't very expressive, but it's still a viable approach:

<xsl:apply-templates select="?book" mode="process-book"/>

<xsl:template match=".[. instance of array(*)]" mode="process-book">
  <xsl:apply-templates select="?*"/>
</xsl:template>

<xsl:template match=".[. instance of map(*)]" mode="process-book">
  <xsl:value-of select="?name"/>
</xsl:template>

You can check the type of ?book e.g. with if (?book instance of array(*)) then ?book?*?name else ?book?name. The type check for a map is e.g. ?book instance of map(*).

Related