0

I have been checking related entries in SO and so far no luck, as my use case seems to have some commonalities but also differences with provided solutions (like: Converting json array to xml using XSLT or Convert JSON to XML using XSLT 3.0 functions).

so hopefully somebody can help me here please.

I call the transformation like this:

java -jar SaxonHE/saxon-he-11.4.jar -s:not_used.xml -xsl:rework_map.xsl -o:report-map.xml p1="list.json"

INPUT: list.json - file produced externally and not embedded in xml, containing array of entries, like:

[
  {
    "tc_id": "A_S_0001",
    "file_name": "\\scripts\\A_S_0001.cs",
    "version": "19",
    "is_automated": true
  },
  {
    "tc_id": "A_S_0002",
    "file_name": "\\scripts\\A_S_0002.cs",
    "version": "25",
    "is_automated": false
  }
]

EXPECTED OUTPUT: something like this:

<list>
<test_case>
<tc_id>A_S_0001</tc_id>
<file_name>\\scripts\\A_S_0001.cs</file_name>
<version>19</version>
<is_automated>true</is_automated>
</test_case>
<test_case>
<tc_id>A_S_0002</tc_id>
<file_name>\\scripts\\A_S_0002.cs</file_name>
<version>25</version>
<is_automated>false</is_automated>
</test_case>
</list>

my template rework_map.xsl (not working)

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:fn="http://www.w3.org/2005/xpath-functions"
    xmlns:math="http://www.w3.org/2005/xpath-functions/math"
    xmlns:map="http://www.w3.org/2005/xpath-functions/map"
    xmlns:array="http://www.w3.org/2005/xpath-functions/array"
    exclude-result-prefixes="xs math map array fn"
    version="3.0">


    <!-- OUTPUT -->
    <xsl:output method="xml" indent="yes"/>
    <!-- PARAMETERS -->
    <xsl:param name="p1"></xsl:param>    <!-- list.json -->

    <!-- VARIABLES -->
    <xsl:variable name="json-array" select="json-doc($p1)"/>

    <xsl:template match="/">
        <!-- <xsl:call-template name="common.INFO"/> -->

        <list>
            <xsl:apply-templates select="$json-array/*"/>

        </list>
    </xsl:template>

    <xsl:template match="fn:map">
        <test_case>
            <xsl:apply-templates/>
        </test_case>
    </xsl:template>

    <xsl:template match="fn:map/fn:*">
        <xsl:element name="{@key}">
            <xsl:value-of select="."/>
        </xsl:element>
    </xsl:template>

</xsl:stylesheet>
YaP
  • 339
  • 3
  • 13

1 Answers1

1

One option with Saxon 11 is to feed the JSON files with the -json:list.json option (don't use a -s option) and write code along the lines of

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  version="3.0"
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  xmlns:map="http://www.w3.org/2005/xpath-functions/map"
  exclude-result-prefixes="#all"
  expand-text="yes">

  <xsl:output indent="yes"/>

  <xsl:template match=".[. instance of array(map(*))]" name="xsl:initial-template">
    <list>
      <xsl:apply-templates select="?*"/>
    </list>
  </xsl:template>
  
  <xsl:template match=".[. instance of map(*)]">
    <test_case>
      <xsl:variable name="map" select="."/>
      <xsl:iterate select="map:keys(.)">
        <xsl:element name="{.}">{$map(.)}</xsl:element>
      </xsl:iterate>
    </test_case>
  </xsl:template>
  
</xsl:stylesheet>

This directly processes the JSON as XPath 3.1 arrays/maps, the disadvantage is that XPath 3.1 maps have no order for the items in them so the result can be e.g.

<list>
   <test_case>
      <version>19</version>
      <is_automated>true</is_automated>
      <file_name>\scripts\A_S_0001.cs</file_name>
      <tc_id>A_S_0001</tc_id>
   </test_case>
   <test_case>
      <version>25</version>
      <is_automated>false</is_automated>
      <file_name>\scripts\A_S_0002.cs</file_name>
      <tc_id>A_S_0002</tc_id>
   </test_case>
</list>

The other option is to start with -it for the initial-template and use json-to-xml and just write templates e.g.

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  version="3.0"
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  xmlns:fn="http://www.w3.org/2005/xpath-functions"
  exclude-result-prefixes="#all"
  expand-text="yes">

  <xsl:output indent="yes"/>
  
  <xsl:param name="json-string" as="xs:string" select="unparsed-text('list.json'"/>

  <xsl:template name="xsl:initial-template">
    <list>
      <xsl:apply-templates select="json-to-xml($json-string)/*"/>
    </list>
  </xsl:template>
  
  <xsl:template match="fn:map[not(@key)]">
    <test_case>
      <xsl:apply-templates/>
    </test_case>
  </xsl:template>
  
  <xsl:template match="fn:*[@key and not(*)]">
    <xsl:element name="{@key}">{.}</xsl:element>
  </xsl:template>
  
</xsl:stylesheet>
Martin Honnen
  • 160,499
  • 6
  • 90
  • 110
  • oh great! I will try after lunch! one question aside, do you know this will work with Saxon 9 as well? – YaP Oct 17 '22 at 10:31
  • 1
    The -json option was introduced in Saxon 11, as far as I remember. – Martin Honnen Oct 17 '22 at 10:31
  • good morning Martin, the first XSLT works fine, but I don't manage to get the second working. And this one looks more intuitive for me. Maybe could you please add details how to invoke transformation via command line? -s:, -o: flags are needed here, or -json: ? can we pass "list.json" as parameter? – YaP Oct 18 '22 at 07:38
  • 1
    @YaP, the second stylesheet has a named template with the predefined name `xsl:initial-template` that allows you to run it with Saxon 9.8 and later with e.g. `-xsl:sheet.xsl -it`. In the current example the file name/URL of the JSON file is hard coded in the default value of the parameter ``, so you have two options if you need to run the same XSLT against changing JSON files, either introduce e.g. `` and use `json-uri=list2.json` on the command line. – Martin Honnen Oct 18 '22 at 11:05
  • 1
    For that you need to change the other parameter then to say e.g. ``. The second option is to stick with param as shown in the answer but on the command line use e.g. `?json-string="unparsed-text('list2.json')"`, i.e. use Saxon's support of e.g `?foo=XPathExpression` (question mark + parameter name `=` some XPath expression computing the parameter value) to set the parameter at runtime. It can be tricky with the various command line shells around to use the right combination of quotes to work with a particular shell – Martin Honnen Oct 18 '22 at 11:08