Search code examples
javaxmlxsltxslt-3.0xml-to-json

XSLT 3.0 - Getting Error "duplicate key value" in XSLT 3.0 xml-to-json()


I am trying to convert given json data from one form to another using XSLT 3.0. I am using json-to-xml and xml-to-json functions provides by XSLT 3.0 to convert data from.to json to/from xml .

I am having below json data.

{
 "serviceCode":"ATOM",
 "action":"SCHEDULE",
"customerId":864,
"instance":"DWHPRD",
"serviceParameters":[
  {
     "parameterName":"team",
     "parameterValue":"EBS"
  }
],
"arguments":[
  {
     "argumentKey":"rfc",
     "argumentValue":"3-BW9R3UA"
  },
  {
     "argumentKey":"sid",
     "argumentValue":"DWHPRD"
  },
  {
     "argumentKey":"schedule_at",
     "argumentValue":"2023-07-02 15:10:00"
  },
  {
     "argumentKey":"update_rfc",
     "argumentValue":false
  },
  {
     "argumentKey":"dynamic_args",
     "argumentValue":[
        {
           "argumentKey":"task_name",
           "argumentValue":"Exa CPU Bursting Task"
        },
        {
           "argumentKey":"arg_name",
           "argumentValue":"$CPU Count",
           "parent":{
              "argumentKey":"task_name",
              "argumentValue":"Exa CPU Bursting Task"
           }
        },
        {
           "argumentKey":"arg_value",
           "argumentValue":"2",
           "parent":{
              "argumentKey":"task_name",
              "argumentValue":"Exa CPU Bursting Task"
           }
        },
        {
           "argumentKey":"task_name",
           "argumentValue":"Exa CPU Bursting DB Task"
        },
        {
           "argumentKey":"arg_name",
           "argumentValue":"$Target DB CPU Count",
           "parent":{
              "argumentKey":"task_name",
              "argumentValue":"Exa CPU Bursting DB Task"
           }
        },
        {
           "argumentKey":"arg_value",
           "argumentValue":"3",
           "parent":{
              "argumentKey":"task_name",
              "argumentValue":"Exa CPU Bursting DB Task"
           }
        }
     ]
    }
   ]
  }

I am trying to convert it to below form using XSLT 3.0

{
"rfc":"3-BW9R3UA",
"sid":"DWHPRD",
"job_id":972,
"schedule_at":"2023-07-02 15:10:00",
"update_rfc":false,
"dynamic_args":[
  {
     "task_name":"Exa CPU Bursting Task",
     "arg_name":"$CPU Count",
     "arg_value":"2"
  },
  {
     "task_name":"Exa CPU Bursting DB Task",
     "arg_name":"$Target DB CPU Count",
     "arg_value":"3"
   }
  ]
}

I came up with below XSLT (incomplete as i am getting error at level 1)

 <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
 version="3.0"
 xmlns="http://www.w3.org/2005/xpath-functions"
 xpath-default-namespace="http://www.w3.org/2005/xpath-functions"
 expand-text="yes">
 <xsl:param name="jsonText"/>

 <xsl:output method="text"/>

 <xsl:template name="init">
 <xsl:variable name="input-as-xml" select="json-to-xml($jsonText)"/>
 <xsl:variable name="transformed-xml">

 <map>
 <xsl:for-each select="$input-as-xml//array[@key='arguments']//map">
 <string key="{string[@key='argumentKey']}"> <xsl:value-of 
select="string[@key='argumentValue']"/></string>
 </xsl:for-each>
</map>
</xsl:variable>
 <xsl:value-of select="xml-to-json($transformed-xml)"/>
 </xsl:template>
</xsl:stylesheet>

Can anyone help me on how i can get desired output json.


Solution

  • It seems you want to skip some stuff but group nested array contents:

    <?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="http://www.w3.org/2005/xpath-functions"
      xpath-default-namespace="http://www.w3.org/2005/xpath-functions"
      exclude-result-prefixes="#all"
      expand-text="yes">
      
      <xsl:param name="json" as="xs:string" expand-text="no">{
     "serviceCode":"ATOM",
     "action":"SCHEDULE",
    "customerId":864,
    "instance":"DWHPRD",
    "serviceParameters":[
      {
         "parameterName":"team",
         "parameterValue":"EBS"
      }
    ],
    "arguments":[
      {
         "argumentKey":"rfc",
         "argumentValue":"3-BW9R3UA"
      },
      {
         "argumentKey":"sid",
         "argumentValue":"DWHPRD"
      },
      {
         "argumentKey":"schedule_at",
         "argumentValue":"2023-07-02 15:10:00"
      },
      {
         "argumentKey":"update_rfc",
         "argumentValue":false
      },
      {
         "argumentKey":"dynamic_args",
         "argumentValue":[
            {
               "argumentKey":"task_name",
               "argumentValue":"Exa CPU Bursting Task"
            },
            {
               "argumentKey":"arg_name",
               "argumentValue":"$CPU Count",
               "parent":{
                  "argumentKey":"task_name",
                  "argumentValue":"Exa CPU Bursting Task"
               }
            },
            {
               "argumentKey":"arg_value",
               "argumentValue":"2",
               "parent":{
                  "argumentKey":"task_name",
                  "argumentValue":"Exa CPU Bursting Task"
               }
            },
            {
               "argumentKey":"task_name",
               "argumentValue":"Exa CPU Bursting DB Task"
            },
            {
               "argumentKey":"arg_name",
               "argumentValue":"$Target DB CPU Count",
               "parent":{
                  "argumentKey":"task_name",
                  "argumentValue":"Exa CPU Bursting DB Task"
               }
            },
            {
               "argumentKey":"arg_value",
               "argumentValue":"3",
               "parent":{
                  "argumentKey":"task_name",
                  "argumentValue":"Exa CPU Bursting DB Task"
               }
            }
         ]
        }
       ]
      }</xsl:param>
    
      <xsl:output method="text"/>
      
      <xsl:mode on-no-match="shallow-skip"/>
    
      <xsl:template match="/" name="xsl:initial-template">
        <xsl:variable name="json-xml" select="json-to-xml($json)"/>
        <xsl:variable name="transformed-json-xml">
          <map>
            <xsl:apply-templates select="$json-xml//array[@key = 'arguments']/map"/>
          </map>
        </xsl:variable>
        <xsl:value-of select="xml-to-json($transformed-json-xml, map { 'indent' : true() })"/>
      </xsl:template>
      
      <xsl:template match="map[string[@key = 'argumentKey'] and string[@key = 'argumentValue']]">
        <string key="{string[@key = 'argumentKey']}">{string[@key = 'argumentValue']}</string>
      </xsl:template>
      
      <xsl:template match="map[string[@key = 'argumentKey'] and string[@key = 'argumentValue']][string[@key = 'argumentValue'] castable as xs:double]">
        <number key="{string[@key = 'argumentKey']}">{string[@key = 'argumentValue']}</number>
      </xsl:template>
      
      <xsl:template match="map[string[@key = 'argumentKey'] and boolean[@key = 'argumentValue']]">
        <boolean key="{string[@key = 'argumentKey']}">{boolean[@key = 'argumentValue']}</boolean>
      </xsl:template>
      
      <xsl:template match="map[string[@key = 'argumentKey'] and array[@key = 'argumentValue']]">
        <array key="{string[@key = 'argumentKey']}">
          <xsl:for-each-group select="array/map" group-starting-with="map[not(map[@key = 'parent'])]">
            <map>
              <xsl:apply-templates select="current-group()"/>
            </map>
          </xsl:for-each-group>
        </array>
      </xsl:template>
    
    </xsl:stylesheet>
    

    I haven't understood where "job_id":972 comes from and when numbers should be numbers or strings in the output.