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>

1 Answer 1

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>
Sign up to request clarification or add additional context in comments.

5 Comments

oh great! I will try after lunch! one question aside, do you know this will work with Saxon 9 as well?
The -json option was introduced in Saxon 11, as far as I remember.
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, 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 <xsl:param name="json-string" as="xs:string" select="unparsed-text('list.json'"/>, so you have two options if you need to run the same XSLT against changing JSON files, either introduce e.g. <xsl:param name="json-uri" as="xs:string" select="'list.json'"/> and use json-uri=list2.json on the command line.
For that you need to change the other parameter then to say e.g. <xsl:param name="json-string" as="xs:string" select="unparsed-text($json-uri)"/>. 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

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.