0

I know the title doesn't sound too much descriptive of my problem.Let me explain further more below.

I have a java application that returns a JSON Array, my objectif is to parse this result in a String format to be used in another "C" Application, the only possible communication between these two applications is String format.

I have achieved this using XSLT by hard coding the values that I'm seeking for inside the JSON reponse and here's how i did it.

My XML is :

<?xml version="1.0" encoding="UTF-8"?>
<input>[{"media_key":"1-1UL-12528","media_value":"10 RUE DES TRELISSAC","media_type":"Courrier"},{"media_key":"2-1UL-12528","media_value":"0614263770","media_type":"SMS"}]</input>

and my XSL is :

<?xml version="1.0" encoding="UTF-8" ?>

<xsl:template match="input">
    <output>
        <xsl:call-template name="print-object-values">
            <xsl:with-param name="string" select="."/>
        </xsl:call-template>
    </output>
</xsl:template>

<!-- Recupere la valeur d'une cle json String de type "key":"val" -->
<xsl:template name="get_string_json">
    <xsl:param name = "json" />
    <xsl:param name = "key" />

    <xsl:variable name="needle">
        <xsl:value-of select="concat('&quot;', $key, '&quot;:&quot;')" />
    </xsl:variable>

    <xsl:value-of select="substring-before(substring-after($json, $needle), '&quot;')" />
</xsl:template>


<xsl:template name="print-object-values">
    <xsl:param name="string"/>
    <xsl:param name="sep_values" select="';'" />

    <xsl:if test="contains($string,'media_key')">
        <xsl:text>|</xsl:text>

        <!-- Partie à modifier en récupérant les valeurs désiré par les fonctions déclarer prècedemment -->
        <xsl:call-template name="get_string_json">
            <xsl:with-param name="json" select="$string" />
            <xsl:with-param name="key" select="'media_key'" />
        </xsl:call-template>
        <xsl:value-of select="$sep_values" />
        <xsl:call-template name="get_string_json">
            <xsl:with-param name="json" select="$string" />
            <xsl:with-param name="key" select="'media_type'" />
        </xsl:call-template>
        <xsl:value-of select="$sep_values" />
        <xsl:call-template name="get_string_json">
            <xsl:with-param name="json" select="$string" />
            <xsl:with-param name="key" select="'media_value'" />
        </xsl:call-template>

        <xsl:call-template name="print-object-values">
            <xsl:with-param name="string" select="substring-after($string,'&quot;},{')"/>
        </xsl:call-template>
    </xsl:if>
</xsl:template>

in my print-object-values template, I use print-object-values to get the value of a specific key in the JSON String.

What i want to do is instead of hard-coding the string that i'm searching for, is there a way to implement this in a variable as a map and then use it in a for-each loop to execute get_string_json ?

EDIT :

here's an example of what i want to achieve

<xsl:template match="input">
    <output>
        <xsl:variable name="values">  <!-- declaration of the values to search for -->
            <value>
                <param>media_key</param>
                <param>media_type</param>
                <param>media_value</param>
            </value> 
        </xsl:variable>

        <xsl:call-template name="print-object-values">
            <xsl:with-param name="string" select="."/>
        </xsl:call-template>
    </output>
</xsl:template>

<xsl:template name="get_string_json">
    <xsl:param name = "json" />
    <xsl:param name = "key" />

    <xsl:variable name="needle">
        <xsl:value-of select="concat('&quot;', $key, '&quot;:&quot;')" />
    </xsl:variable>

    <xsl:value-of select="substring-before(substring-after($json, $needle), '&quot;')" />
</xsl:template>


<xsl:template name="print-object-values">
    <xsl:param name="string"/>
    <xsl:param name="sep_values" select="';'" />

    <xsl:if test="contains($string,'media_key')">
        <xsl:text>|</xsl:text>

        <xsl:for-each select= ><!-- select params in the variable values -->
            <xsl:call-template name="get_string_json">
                <xsl:with-param name="json" select="$string" />
                <xsl:with-param name="key" select="" /><!-- pass the value of the param -->
            </xsl:call-template>
        </xsl:for-each>
        <xsl:call-template name="print-object-values">
            <xsl:with-param name="string" select="substring-after($string,'&quot;},{')"/>
        </xsl:call-template>
    </xsl:if>
</xsl:template>
  • XSLT 3 with XPath 3.1 has functions like `parse-json` https://www.w3.org/TR/xpath-functions/#func-parse-json so using Saxon 9.8 or later you can use plain XSLT/XPath to parse and process JSON without trying to write a JSON parser in XSLT. – Martin Honnen Mar 03 '19 at 14:27
  • i'm new to XSLT, in my application i only found how to implement XSLT 1.0 to the existing code, i don't know if i can switch to XSLT 3 that easy, is changing the version from 1.0 to 3.0 enough or i need to rewrite part of my code ? – Mohamed Amine BELHAJJA Mar 03 '19 at 14:45
  • You need to use an XSLT 3 processor like Saxon 9.8 or 9.9, so in the Java world (https://mvnrepository.com/artifact/net.sf.saxon/Saxon-HE) or in .NET (https://www.nuget.org/packages/Saxon-HE/) it is easy as there are open-source versions of Saxon 9.8 or 9.9 for these platforms. You haven't shown or explained how you use XSLT, so it is not possible to tell what you have to change. – Martin Honnen Mar 03 '19 at 15:04
  • First of all ,Thank you Martin for your answers. I just need to specify that i don't have access to java application Code and i can't change a thing only the XSL. I'm an intern in a company that uses XSL to transform HTTP responses into a String value and then send it to a C application for further use. The JSON is inside an XML response. My solution is functionnal, i already test it and works like a charm but it always need to hard code the values i'm seeking for inside the template. What i need is a method to loop over an array or an XML variables and for-each element call the template – Mohamed Amine BELHAJJA Mar 03 '19 at 15:09
  • Loops, arrays? Well, good luck. – Martin Honnen Mar 03 '19 at 15:17
  • From your answer, i think it's not possible! like i said, i've never used XSLT. – Mohamed Amine BELHAJJA Mar 03 '19 at 15:20
  • @MohamedAmineBELHAJJA XSLT 1.0 is Turing complete, so *anything* is possible. However, (a) it is not clear what exactly you mean by *"implement this in a variable as a map"* (of course you can use a variable instead of any of your hard-coded string) and (b) how generic you want to make this (your JSON has a rather simple structure, writing a general JSON parser would be **much** more difficult). – michael.hor257k Mar 03 '19 at 16:01
  • @michael.hor257k I have edited my post to show an example of what I'm trying to achieve. i hope i explained well what I'm trying to do. Thank you for your Help – Mohamed Amine BELHAJJA Mar 03 '19 at 16:25
  • Can you tell us which XSLT processor your application is using? See here how to find out: https://stackoverflow.com/questions/25244370/how-can-i-check-which-xslt-processor-is-being-used-in-solr/25245033#25245033 – michael.hor257k Mar 03 '19 at 16:42
  • @michael.hor257k Apache Software Foundation (Xalan XSLTC)1.0 – Mohamed Amine BELHAJJA Mar 03 '19 at 17:59
  • With Java you can easily switch from Xalan and XSLT 1 to Saxon 9 and XSLT 3 by putting the `saxon9he.jar` from Saxon 9.9 (https://sourceforge.net/projects/saxon/files/Saxon-HE/9.9/) or 9.8 on the class path, then you can use XPath 3.1 and code like https://xsltfiddle.liberty-development.net/ncdD7ma/ which uses `` with a parameter `` to parse the JSON and access the named properties of the objects in the array. – Martin Honnen Mar 03 '19 at 20:23

1 Answers1

1

IMPORTANT:
The purpose of this answer is to address only this part of your question:

instead of hard-coding the string that i'm searching for, is there a way to implement this in a variable as a map and then use it in a for-each loop to execute get_string_json ?

I found it necessary to make some adjustments to other parts of your XSLT, but I did not examine your method of parsing the JSON.

XSLT 1.0

<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exsl="http://exslt.org/common"
extension-element-prefixes="exsl">
<xsl:output method="xml" encoding="UTF-8" indent="yes" />

<!-- declaration of the values to search for -->
<xsl:variable name="keys">  
    <key>media_key</key>
    <key>media_type</key>
    <key>media_value</key>
</xsl:variable>

<xsl:template match="input">
    <output>
        <xsl:call-template name="print-object-values">
            <xsl:with-param name="string" select="."/>
        </xsl:call-template>
    </output>
</xsl:template>

<xsl:template name="print-object-values">
    <xsl:param name="string"/>
    <xsl:param name="separator" select="'&quot;},{'" />

    <xsl:variable name="object" select="substring-before(concat($string, $separator), $separator)" />

    <xsl:text>|</xsl:text>

    <!-- HERE IS THE RELEVANT PART -->
    <xsl:for-each select="exsl:node-set($keys)/key">
        <xsl:call-template name="get_string_json">
            <xsl:with-param name="json" select="concat($object, '&quot;')" />
            <xsl:with-param name="key" select="." />
        </xsl:call-template>
        <xsl:if test="position()!=last()">
            <xsl:text>;</xsl:text>
        </xsl:if>
    </xsl:for-each>

    <xsl:if test="contains($string, $separator)">
        <xsl:call-template name="print-object-values">
            <xsl:with-param name="string" select="substring-after($string, $separator)"/>
        </xsl:call-template>
    </xsl:if>
</xsl:template>

<!-- Recupere la valeur d'une cle json String de type "key":"val" -->
<xsl:template name="get_string_json">
    <xsl:param name = "json" />
    <xsl:param name = "key" />

    <xsl:variable name="needle">
        <xsl:value-of select="concat('&quot;', $key, '&quot;:&quot;')" />
    </xsl:variable>

    <xsl:value-of select="substring-before(substring-after($json, $needle), '&quot;')" />
</xsl:template>

</xsl:stylesheet>
michael.hor257k
  • 113,275
  • 6
  • 33
  • 51