I'm getting following JSON format when I convert XML data to JSON in SQL developer.
JSON :
{"list":{"obj":{"att":{"val":"ABC"}}}}
But I want the JSON to be formatted like the following
{"list":{"obj":{"att":{"id":"FirstName", "val":"ABC"}}}}
The following is the oracle function which i created to convert the xml to the JSON format.
create or replace FUNCTION get_json(name IN VARCHAR2 ) RETURN clob IS
l_xml xmltype;
v_XML_data clob;
l_json xmltype;
l_json_result clob;
begin
v_XML_data := '<list><obj class="Plan"><att id="FirstName">
<val>ABC</val></att> </obj></list>';
IF(v_XML_data is not null) then
l_xml := xmltype.createxml(v_XML_data);
dbms_output.put_line(l_xml.getclobVal());
-- convert the JSON
l_json :=
l_xml.transform(xmltype(roelvt.json_util_pkg.get_xml_to_json_stylesheet));
-- dbms_output.put_line(l_json);
l_json_result :=l_json.getclobVal();
-- Display the JSON
dbms_output.put_line(l_json.getclobVal());
RETURN l_json_result;
else
RETURN null;
end if;
exception
when others then
null;
RETURN null;
END;
The following is the XSLT stylesheet :
<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheetversion="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="no" omit-xml-declaration="yes"
method="text" encoding="UTF-8" media-type="text/x-json"/>
<xsl:strip-space elements="*"/>
<!--contant-->
<xsl:variable name="d">0123456789</xsl:variable>
<!-- ignore document text -->
<xsl:template match="text()[preceding-sibling::node()
or following- sibling::node()]"/>
<!-- string -->
<xsl:template match="text()">
<xsl:call-template name="escape-string">
<xsl:with-param name="s" select="."/>
</xsl:call-template>
</xsl:template>
<xsl:template name="escape-string">
<xsl:param name="s"/>
<xsl:text>"</xsl:text>
<xsl:call-template name="escape-bs-string">
<xsl:with-param name="s" select="$s"/>
</xsl:call-template>
<xsl:text>"</xsl:text>
</xsl:template>
<!-- Escape the backslash (\) before everything else. -->
<xsl:template name="escape-bs-string">
<xsl:param name="s"/>
<xsl:choose>
<xsl:when test="contains($s,''\'')">
<xsl:call-template name="escape-quot-string">
<xsl:with-param name="s" select="concat(substring-
before($s,''\''),''\\'')"/>
</xsl:call-template>
<xsl:call-template name="escape-bs-string">
<xsl:with-param name="s" select="substring-after($s,''\'')"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="escape-quot-string">
<xsl:with-param name="s" select="$s"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<!-- Escape the double quote ("). -->
<xsl:template name="escape-quot-string">
<xsl:param name="s"/>
<xsl:choose>
<xsl:when test="contains($s,'';'')">
<xsl:call-template name="encode-string">
<xsl:with-param name="s" select="concat(substring-
before($s,'';''),''"'')"/>
</xsl:call-template>
<xsl:call-template name="escape-quot-string">
<xsl:with-param name="s" select="substring-after($s,'';'')"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="encode-string">
<xsl:with-param name="s" select="$s"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<!-- Replace tab, line feed and/or carriage return by its matching escape
code. Can''t escape backslash
or double quote here, because they don''t replace characters (; becomes
\t), but they prefix
characters (\ becomes \\). Besides, backslash should be seperate anyway,
because it should be
processed first. This function can''t do that. -->
<xsl:template name="encode-string">
<xsl:param name="s"/>
<xsl:choose>
<!-- tab -->
<xsl:when test="contains($s,'';'')">
<xsl:call-template name="encode-string">
<xsl:with-param name="s" select="concat(substring-
before($s,'';''),''\t'',substring-after($s,'';''))"/>
</xsl:call-template>
</xsl:when>
<!-- line feed -->
<xsl:when test="contains($s,'';'')">
<xsl:call-template name="encode-string">
<xsl:with-param name="s" select="concat(substring-
before($s,'';''),''\n'',substring-after($s,'';''))"/>
</xsl:call-template>
</xsl:when>
<!-- carriage return -->
<xsl:when test="contains($s,'';'')">
<xsl:call-template name="encode-string">
<xsl:with-param name="s" select="concat(substring-
before($s,'';''),''\r'',substring-after($s,'';''))"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise><xsl:value-of select="$s"/></xsl:otherwise>
</xsl:choose>
</xsl:template>
<!-- number (no support for javascript mantise) -->
<xsl:template match="text()[not(string(number())=''NaN'')]">
<xsl:value-of select="."/>
</xsl:template>
<!-- boolean, case-insensitive -->
<xsl:template match="text()
[translate(.,''TRUE'',''true'')=''true'']">true</xsl:template>
<xsl:template match="text()
[translate(.,''FALSE'',''false'')=''false'']">false</xsl:template>
<!-- item:null -->
<xsl:template match="*[count(child::node())=0]">
<xsl:call-template name="escape-string">
<xsl:with-param name="s" select="local-name()"/>
</xsl:call-template>
<xsl:text>:null</xsl:text>
<xsl:if test="following-sibling::*">,</xsl:if>
<xsl:if test="not(following-sibling::*)">}</xsl:if> <!-- MBR 30.01.2010:
added this line as it appeared to be missing from stylesheet -->
</xsl:template>
<!-- object -->
<xsl:template match="*" name="base">
<xsl:if test="not(preceding-sibling::*)">{</xsl:if>
<xsl:call-template name="escape-string">
<xsl:with-param name="s" select="name()"/>
</xsl:call-template>
<xsl:text>:</xsl:text>
<xsl:apply-templates select="child::node()"/>
<xsl:if test="following-sibling::*">,</xsl:if>
<xsl:if test="not(following-sibling::*)">}</xsl:if>
</xsl:template>
<!-- array -->
<xsl:template match="*[count(../*[name(../*)=name(.)])=count(../*) and
count(../*)>1]">
<xsl:if test="not(preceding-sibling::*)">[</xsl:if>
<xsl:choose>
<xsl:when test="not(child::node())">
<xsl:text>null</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="child::node()"/>
</xsl:otherwise>
</xsl:choose>
<xsl:if test="following-sibling::*">,</xsl:if>
<xsl:if test="not(following-sibling::*)">]
</xsl:if>
</xsl:template>
<!-- convert root element to an anonymous container -->
<xsl:template match="/">
<xsl:apply-templates select="node()"/>
</xsl:template>
</xsl:stylesheet>
Related
I'm new to XSL programming and facing an issue while trying to remove the HTML tags from the XML file.
My Input XML file is: Just adding the tag that I'm interested in
<Ingredients>
<p><b>INGREDIENTS:</b> Reduced Fat Cheese (84%) [Reduced Fat<strong>Milk</strong>, Salt, Starter Cultures, Enzyme (Animal Rennet, Lipase)],<strong>Milk</strong> Solids, Water, Emulsifying Salt (331), Salt, Acidity Regulator (330), Preservative (200), Natural Colours (100, 160b).</p>
</Ingredients>
My XSL file is:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:ns1="http://www.micros.com/creations/core/domain/dto/v1p0/full" xmlns:ns2="http://www.micros.com/creations/core/domain/dto/v1p0/simple" exclude-result-prefixes="ns1 ns1">
<xsl:template name="replace-string">
<xsl:param name="text"/>
<xsl:param name="replace"/>
<xsl:param name="with"/>
<xsl:choose>
<xsl:when test="contains($text,$replace)">
<xsl:value-of select="substring-before($text,$replace)"/>
<xsl:value-of select="$with"/>
<xsl:call-template name="replace-string">
<xsl:with-param name="text" select="substring-after($text,$replace)"/>
<xsl:with-param name="replace" select="$replace"/>
<xsl:with-param name="with" select="$with"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$text"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="strip-html-tags">
<xsl:param name="text"/>
<xsl:choose>
<xsl:when test="contains($text, '<')">
<xsl:value-of select="substring-before($text, '<')"/>
<xsl:call-template name="strip-html-tags">
<xsl:with-param name="text" select="substring-after($text, '>')"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="replace-string">
<xsl:with-param name="text" select="$text"/>
<xsl:with-param name="replace" select="' '" />
<xsl:with-param name="with" select="''"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="/">
<ItemDetails>
<SourceSystem>
<xsl:text>Fusion</xsl:text>
</SourceSystem>
<ActionType>
<xsl:text>Create</xsl:text>
</ActionType>
<CreateDateTime>
<xsl:text>2021-11-10T08:00:00</xsl:text>
</CreateDateTime>
<Ingredients>
<xsl:call-template name="strip-html-tags">
<xsl:with-param name="text" select="ns1:productSpecificationFullDTO/ns1:specificationSectionDetail/ns1:specificationSectionFoodRecipeAndRawMaterialsSection/ns1:onPackIngredientsList"/>
<!--<xsl:with-param name="replace" select="' '"/>
<xsl:with-param name="with" select="''"/>-->
</xsl:call-template>
</Ingredients>
</ItemDetails>
</xsl:template>
</xsl:stylesheet>
Using the above XSL file I'm trying to remove the HTML tags and also trying to replace the special character like with empty. Hence I'm calling 2 templates.
<xsl:template name="strip-html-tags"> strips the other tags but not " "
So created a <xsl:template name="replace-string"> to replace " " with ''.
However this is not working. either of the templates works when invoked first.
If <xsl:template name="strip-html-tags"> is invoked first, it removes all the HTML tags except " "
If <xsl:template name="replace-string"> is invoked first, it replace the " " with '' but the other HTML tags are not removed.
I am calling these templates in the when clause.
How can this issue be solved? I need all the HTML tags to be removed. Is there a way to do it at single go or is it something that I'm missing?
If you want to use the output of one template as the input to the other template, then call the first template within the xsl:with-param instruction of the second template - for example:
<xsl:call-template name="replace-string">
<xsl:with-param name="text">
<xsl:call-template name="strip-html-tags">
<xsl:with-param name="text" select="Ingredients"/>
</xsl:call-template>
</xsl:with-param>
<xsl:with-param name="replace" select="' '" />
<xsl:with-param name="with" select="''"/>
</xsl:call-template>
Simplified demo: https://xsltfiddle.liberty-development.net/eiorv1s
Ensure that you are scrubbing the value that is between the tags, not just selecting it. Instead of xsl:value-of, run it through the replace-string template.
Also, you may want to replace with a space ' ' instead of empty string. Otherwise, you will get ReducedFat instead of Reduced Fat
<xsl:when test="contains($text, '<')">
<!--<xsl:value-of select="substring-before($text, '<')"/>-->
<xsl:call-template name="replace-string">
<xsl:with-param name="text" select="substring-before($text, '<')"/>
<xsl:with-param name="replace" select="' '" />
<xsl:with-param name="with" select="' '"/>
</xsl:call-template>
<xsl:call-template name="strip-html-tags">
<xsl:with-param name="text" select="substring-after($text, '>')"/>
</xsl:call-template>
</xsl:when>
I have an xml that needs to be converted to JSON and am using XSLT to transform it. In one of the element am not required to pass pair name, only values. See below
My XML:
<?xml version="1.0" encoding="UTF-8"?>
<map xmlns="http://www.w3.org/2005/xpath-functions">
<array key="cars">
<map>
<string key="doors">4</string>
<string key="price">6L</string>
</map>
<map>
<string key="doors">5</string>
<string key="price">13L</string>
</map>
</array>
</map>
Using XSL: https://xsltfiddle.liberty-development.net/b4GWVd
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="3.0">
<xsl:output omit-xml-declaration="yes"/>
<xsl:template match="/">
<xsl:value-of select="translate(xml-to-json(., map { 'indent' : true() }),'Ø', 'ø')"/>
</xsl:template>
</xsl:stylesheet>
Gives output: Which is correct
{ "cars" :
[
{ "doors" : "4",
"price" : "6L" },
{ "doors" : "5",
"price" : "13L" } ] }
But then I would like to get a JSON with below structure, Without Pair Name(Reason: Its the structure required to be submitted to an API)
{ "cars" :
[
{ "4",
"6L" },
{ "5",
"13L" } ] }
As the XSLT 3.0 spec also provides an implementation of xml-to-json as an XSLT 3.0 package you could use that code and override templates where you want to eliminate the "keys" of JSON objects/XDM map items:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:j="http://www.w3.org/2013/XSLT/xml-to-json"
xmlns:fn="http://www.w3.org/2005/xpath-functions"
exclude-result-prefixes="#all"
version="3.0">
<xsl:use-package name="http://www.w3.org/2013/XSLT/xml-to-json" package-version="1.0">
<xsl:override>
<!-- Template rule for fn:map elements, representing JSON objects , overridden for content of cars array -->
<xsl:template match="fn:array[#key = 'cars']/fn:map" mode="indent">
<xsl:value-of>
<xsl:variable name="depth" select="count(ancestor::*) + 1"/>
<xsl:text>{</xsl:text>
<xsl:for-each select="*">
<xsl:if test="position() gt 1">
<xsl:text>, </xsl:text>
<xsl:value-of select="j:indent($depth)"/>
</xsl:if>
<!--<xsl:apply-templates select="snapshot(#key)" mode="key-attribute"/>
<xsl:text> : </xsl:text>-->
<xsl:apply-templates select="." mode="#current"/>
</xsl:for-each>
<xsl:text>}</xsl:text>
</xsl:value-of>
</xsl:template>
<xsl:template match="fn:array[#key = 'cars']/fn:map/*" mode="no-indent">
<xsl:value-of>
<xsl:text>{</xsl:text>
<xsl:for-each select="*">
<xsl:if test="position() gt 1">
<xsl:text>,</xsl:text>
</xsl:if>
<!--<xsl:apply-templates select="snapshot(#key)" mode="key-attribute"/>
<xsl:text>:</xsl:text>-->
<xsl:apply-templates select="." mode="#current"/>
</xsl:for-each>
<xsl:text>}</xsl:text>
</xsl:value-of>
</xsl:template>
</xsl:override>
</xsl:use-package>
<xsl:output method="text"/>
<xsl:template match="/">
<xsl:value-of select="j:xml-to-json(., map { 'indent' : true() })"/>
</xsl:template>
</xsl:stylesheet>
The above can be run with Saxon 10 HE and higher or Saxon 9.8 PE or EE and later by using the command line -s:your-xml.xml -xsl:above-xslt-xsl -lib:w3c-xml-to-json.xsl where the last option refers to the file https://www.w3.org/TR/xslt-30/xml-to-json.xsl linked from the XSLT 3 spec with one path/errata added, namely the declaration of the default mode with <xsl:mode name="j:xml-to-json"/> so it would look like
<xsl:package
name="http://www.w3.org/2013/XSLT/xml-to-json"
package-version="1.0"
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:j="http://www.w3.org/2013/XSLT/xml-to-json"
exclude-result-prefixes="xs fn j" default-mode="j:xml-to-json" version="3.0">
<xsl:variable name="quot" visibility="private">"</xsl:variable>
<xsl:param name="indent-spaces" select="2"/>
<!-- The static parameter STREAMABLE controls whether the stylesheet is declared as streamable -->
<xsl:param name="STREAMABLE" static="yes" as="xs:boolean" select="true()"/>
<!-- fix for https://github.com/w3c/qtspecs/blob/master/errata/xslt-30/errata.xml#L1154 -->
<xsl:mode name="j:xml-to-json"/>
<xsl:mode name="indent" _streamable="{$STREAMABLE}" visibility="public"/>
<xsl:mode name="no-indent" _streamable="{$STREAMABLE}" visibility="public"/>
<xsl:mode name="key-attribute" streamable="false" on-no-match="fail" visibility="public"/>
<!-- The static parameter VALIDATE controls whether the input, if untyped, should be validated -->
<xsl:param name="VALIDATE" static="yes" as="xs:boolean" select="false()"/>
<xsl:import-schema namespace="http://www.w3.org/2005/xpath-functions" use-when="$VALIDATE"/>
<!-- Entry point: function to convert a supplied XML node to a JSON string -->
<xsl:function name="j:xml-to-json" as="xs:string" visibility="public">
<xsl:param name="input" as="node()"/>
<xsl:sequence select="j:xml-to-json($input, map{})"/>
</xsl:function>
<!-- Entry point: function to convert a supplied XML node to a JSON string, supplying options -->
<xsl:function name="j:xml-to-json" as="xs:string" visibility="public">
<xsl:param name="input" as="node()"/>
<xsl:param name="options" as="map(*)"/>
<xsl:variable name="input" as="node()" use-when="$VALIDATE">
<xsl:copy-of select="$input" validation="strict"/>
</xsl:variable>
<xsl:choose>
<xsl:when test="$options('indent') eq true()">
<xsl:apply-templates select="$input" mode="indent">
<xsl:with-param name="fallback" as="(function(element()) as xs:string)?"
select="$options('fallback')" tunnel="yes"/>
</xsl:apply-templates>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="$input" mode="no-indent">
<xsl:with-param name="fallback" as="(function(element()) as xs:string)?"
select="$options('fallback')" tunnel="yes"/>
</xsl:apply-templates>
</xsl:otherwise>
</xsl:choose>
</xsl:function>
<!-- A document node is ignored -->
<xsl:template match="/" mode="indent no-indent">
<xsl:apply-templates mode="#current"/>
</xsl:template>
<!-- Template rule for fn:map elements, representing JSON objects -->
<xsl:template match="fn:map" mode="indent">
<xsl:value-of>
<xsl:variable name="depth" select="count(ancestor::*) + 1"/>
<xsl:text>{</xsl:text>
<xsl:for-each select="*">
<xsl:if test="position() gt 1">
<xsl:text>, </xsl:text>
<xsl:value-of select="j:indent($depth)"/>
</xsl:if>
<xsl:apply-templates select="snapshot(#key)" mode="key-attribute"/>
<xsl:text> : </xsl:text>
<xsl:apply-templates select="." mode="#current"/>
</xsl:for-each>
<xsl:text>}</xsl:text>
</xsl:value-of>
</xsl:template>
<xsl:template match="fn:map" mode="no-indent">
<xsl:value-of>
<xsl:text>{</xsl:text>
<xsl:for-each select="*">
<xsl:if test="position() gt 1">
<xsl:text>,</xsl:text>
</xsl:if>
<xsl:apply-templates select="snapshot(#key)" mode="key-attribute"/>
<xsl:text>:</xsl:text>
<xsl:apply-templates select="." mode="#current"/>
</xsl:for-each>
<xsl:text>}</xsl:text>
</xsl:value-of>
</xsl:template>
<!-- Template rule for fn:array elements, representing JSON arrays -->
<xsl:template match="fn:array" mode="indent">
<xsl:value-of>
<xsl:variable name="depth" select="count(ancestor::*) + 1"/>
<xsl:text>[</xsl:text>
<xsl:for-each select="*">
<xsl:if test="position() gt 1">
<xsl:text>, </xsl:text>
<xsl:value-of select="j:indent($depth)"/>
</xsl:if>
<xsl:apply-templates select="." mode="#current"/>
</xsl:for-each>
<xsl:text>]</xsl:text>
</xsl:value-of>
</xsl:template>
<xsl:template match="fn:array" mode="no-indent">
<xsl:value-of>
<xsl:text>[</xsl:text>
<xsl:for-each select="*">
<xsl:if test="position() gt 1">
<xsl:text>,</xsl:text>
</xsl:if>
<xsl:apply-templates select="." mode="#current"/>
</xsl:for-each>
<xsl:text>]</xsl:text>
</xsl:value-of>
</xsl:template>
<!-- Template rule for fn:string elements in which
special characters are already escaped -->
<xsl:template match="fn:string[#escaped='true']" mode="indent no-indent">
<xsl:sequence select="concat($quot, ., $quot)"/>
</xsl:template>
<!-- Template rule for fn:string elements in which
special characters need to be escaped -->
<xsl:template match="fn:string[not(#escaped='true')]" mode="indent no-indent">
<xsl:sequence select="concat($quot, j:escape(.), $quot)"/>
</xsl:template>
<!-- Template rule for fn:boolean elements -->
<xsl:template match="fn:boolean" mode="indent no-indent">
<xsl:sequence select="xs:string(xs:boolean(.))"/>
</xsl:template>
<!-- Template rule for fn:number elements -->
<xsl:template match="fn:number" mode="indent no-indent">
<xsl:value-of select="xs:string(xs:double(.))"/>
</xsl:template>
<!-- Template rule for JSON null elements -->
<xsl:template match="fn:null" mode="indent no-indent">
<xsl:text>null</xsl:text>
</xsl:template>
<!-- Template rule matching a key within a map where
special characters in the key are already escaped -->
<xsl:template match="fn:*[#key-escaped='true']/#key" mode="key-attribute">
<xsl:value-of select="concat($quot, ., $quot)"/>
</xsl:template>
<!-- Template rule matching a key within a map where
special characters in the key need to be escaped -->
<xsl:template match="fn:*[not(#key-escaped='true')]/#key" mode="key-attribute">
<xsl:value-of select="concat($quot, j:escape(.), $quot)"/>
</xsl:template>
<!-- Template matching "invalid" elements -->
<xsl:template match="*" mode="indent no-indent">
<xsl:param name="fallback" as="(function(element()) as xs:string)?"
tunnel="yes" required="yes"/>
<xsl:choose>
<xsl:when test="exists($fallback)">
<xsl:value-of select="$fallback(snapshot(.))"/>
</xsl:when>
<xsl:otherwise>
<xsl:message terminate="yes">>Inc</xsl:message>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<!-- Template rule matching (and discarding) whitespace text nodes in the XML -->
<xsl:template match="text()[not(normalize-space())]" mode="indent no-indent"/>
<!-- Function to escape special characters -->
<xsl:function name="j:escape" as="xs:string" visibility="final">
<xsl:param name="in" as="xs:string"/>
<xsl:value-of>
<xsl:for-each select="string-to-codepoints($in)">
<xsl:choose>
<xsl:when test=". gt 65535">
<xsl:value-of select="concat('\u', j:hex4((. - 65536) idiv 1024 + 55296))"/>
<xsl:value-of select="concat('\u', j:hex4((. - 65536) mod 1024 + 56320))"/>
</xsl:when>
<xsl:when test=". = 34">\"</xsl:when>
<xsl:when test=". = 92">\\</xsl:when>
<xsl:when test=". = 08">\b</xsl:when>
<xsl:when test=". = 09">\t</xsl:when>
<xsl:when test=". = 10">\n</xsl:when>
<xsl:when test=". = 12">\f</xsl:when>
<xsl:when test=". = 13">\r</xsl:when>
<xsl:when test=". lt 32 or (. ge 127 and . le 160)">
<xsl:value-of select="concat('\u', j:hex4(.))"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="codepoints-to-string(.)"/>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</xsl:value-of>
</xsl:function>
<!-- Function to convert a UTF16 codepoint into a string of four hex digits -->
<xsl:function name="j:hex4" as="xs:string" visibility="final">
<xsl:param name="ch" as="xs:integer"/>
<xsl:variable name="hex" select="'0123456789abcdef'"/>
<xsl:value-of>
<xsl:value-of select="substring($hex, $ch idiv 4096 + 1, 1)"/>
<xsl:value-of select="substring($hex, $ch idiv 256 mod 16 + 1, 1)"/>
<xsl:value-of select="substring($hex, $ch idiv 16 mod 16 + 1, 1)"/>
<xsl:value-of select="substring($hex, $ch mod 16 + 1, 1)"/>
</xsl:value-of>
</xsl:function>
<!-- Function to output whitespace indentation based on
the depth of the node supplied as a parameter -->
<xsl:function name="j:indent" as="text()" visibility="public">
<xsl:param name="depth" as="xs:integer"/>
<xsl:value-of select="'
', string-join((1 to ($depth + 1) * $indent-spaces) ! ' ', '')"/>
</xsl:function>
</xsl:package>
To run code with Java code you basically need to use an XsltCompiler created from Processor e.g.
Processor processor = new Processor(true);
XsltCompiler xsltCompiler = processor.newXsltCompiler();
XsltPackage xmlToJsonPackage = xsltCompiler.compilePackage(new StreamSource("w3c-xml-to-json.xsl"));
xsltCompiler.importPackage(xmlToJsonPackage);
XsltExecutable xsltExecutable = xsltCompiler.compile(new StreamSource("sheet.xsl"));
Xslt30Transformer xslt30Transformer = xsltExecutable.load30();
// now run stylesheet with e.g. transform() or applyTemplates()
xslt30Transformer.transform(new StreamSource("input.xml"), xslt30Transformer.newSerializer(System.out));
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('"', $key, '":"')" />
</xsl:variable>
<xsl:value-of select="substring-before(substring-after($json, $needle), '"')" />
</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,'"},{')"/>
</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('"', $key, '":"')" />
</xsl:variable>
<xsl:value-of select="substring-before(substring-after($json, $needle), '"')" />
</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,'"},{')"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
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="'"},{'" />
<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, '"')" />
<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('"', $key, '":"')" />
</xsl:variable>
<xsl:value-of select="substring-before(substring-after($json, $needle), '"')" />
</xsl:template>
</xsl:stylesheet>
I have a string like below and trying to convert into json format:
Test "out" and \new
Expected output is
Test \"out\" and \new
I tried by calling templates for escapequote - working fine for escape quotes:
<xsl:template name="escapeQuote">
<xsl:param name="pText" select="concat(normalize-space(.), '')" />
<xsl:if test="string-length($pText) >0">
<xsl:value-of select="substring-before(concat($pText, '"'), '"')" />
<xsl:if test="contains($pText, '"')">
<xsl:text>\"</xsl:text>
<xsl:call-template name="escapeQuote">
<xsl:with-param name="pText" select="substring-after($pText, '"')" />
</xsl:call-template>
</xsl:if>
</xsl:if>
</xsl:template>
Template for escape backslash - working for only backslash:
<xsl:template name="jsonescape">
<xsl:param name="str" select="."/>
<xsl:choose>
<xsl:when test="contains($str, '\')">
<xsl:value-of select="concat(substring-before($str, '\'), '\\' )"/>
<xsl:call-template name="jsonescape">
<xsl:with-param name="str" select="substring-after($str, '\')"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$str"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
My question how to call both templates or merge, please help me
Here is an example of how you can combine the two template calls, so that the output from jsonescape is used as an input parameter to escapequote
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output method="text" />
<xsl:template match="input">
<xsl:call-template name="escapeQuote">
<xsl:with-param name="pText">
<xsl:call-template name="jsonescape">
<xsl:with-param name="str" select="." />
</xsl:call-template>
</xsl:with-param>
</xsl:call-template>
</xsl:template>
<xsl:template name="escapeQuote">
<xsl:param name="pText" select="concat(normalize-space(.), '')" />
<xsl:if test="string-length($pText) >0">
<xsl:value-of select="substring-before(concat($pText, '"'), '"')" />
<xsl:if test="contains($pText, '"')">
<xsl:text>\"</xsl:text>
<xsl:call-template name="escapeQuote">
<xsl:with-param name="pText" select="substring-after($pText, '"')" />
</xsl:call-template>
</xsl:if>
</xsl:if>
</xsl:template>
<xsl:template name="jsonescape">
<xsl:param name="str" select="."/>
<xsl:choose>
<xsl:when test="contains($str, '\')">
<xsl:value-of select="concat(substring-before($str, '\'), '\\' )"/>
<xsl:call-template name="jsonescape">
<xsl:with-param name="str" select="substring-after($str, '\')"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$str"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
So, given this an input:
<input>Test "out" and \new</input>
The following is output
Test \"out\" and \\new
Note that the order is important, because if you reversed the order of the template calls, the " would get converted to \" by the escapequote template, which would then get converted to \\" by the jsonescape template.
Alternatively, as both template do a similar thing, of putting a \ before specific characters, you could combine the two templates into one.
Try this XSLT too
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output method="text" />
<xsl:template match="input">
<xsl:call-template name="jsonescape">
<xsl:with-param name="str" select="." />
</xsl:call-template>
</xsl:template>
<xsl:template name="jsonescape">
<xsl:param name="str" select="."/>
<xsl:param name="escapeChars" select="'\"'" />
<xsl:variable name="first" select="substring(translate($str, translate($str, $escapeChars, ''), ''), 1, 1)" />
<xsl:choose>
<xsl:when test="$first">
<xsl:value-of select="concat(substring-before($str, $first), '\', $first)"/>
<xsl:call-template name="jsonescape">
<xsl:with-param name="str" select="substring-after($str, $first)"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$str"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
I have been scouring the web for something like this and being a lazy programmer, before I attempt to do so myself, I thought I would ask here.
What I would like is a method to take any xml with node names following a convention (like cakephp or ruby) and then for that data to be presented in a ready to print format.
ie:
<xml>
<home_address>The Street</home_address>
</xml>
to:
<tr><td>Home Address</td><td>The Street</td></tr>
etc. with children having separate tables.
It seems pretty straightforward and something that would have been desired many times before. I found one useful discussion, but with no conclusion.
http://www.daniweb.com/software-development/xml-xslt-and-xpath/threads/363621/xml-to-html-using-xslt-without-hardcoding-node-names-in-xslt
Have I missed something here? Is there a simple generic xslt/css method for doing this? Or is this work being repeated hundreds of times a day in cubicles around the world?
Thanks in advance,
F
use the following XSL stylesheet. I have tested it and it works.
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl">
<xsl:template match="/">
<xsl:for-each select="*">
<xsl:for-each select="*">
<tr>
<xsl:variable name="NodeNameClearText">
<xsl:call-template name="repalceNodeName">
<xsl:with-param name="value" select="local-name(.)"/>
</xsl:call-template>
</xsl:variable>
<td>
<xsl:value-of select="$NodeNameClearText" />
</td>
<td>
<xsl:value-of select="." />
</td>
</tr>
</xsl:for-each>
</xsl:for-each>
</xsl:template>
<xsl:template name="repalceNodeName">
<xsl:param name="value"/>
<xsl:variable name="valueWithoutUnderscores">
<xsl:value-of select="translate($value, '_',' ')"/>
</xsl:variable>
<xsl:call-template name="caseLowerAcceptFirstWord">
<xsl:with-param name="data" select="$valueWithoutUnderscores"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="caseDown">
<xsl:param name="data"/>
<xsl:if test="$data">
<xsl:choose>
<xsl:when test="starts-with($data,' ')">
<xsl:text> </xsl:text>
<xsl:call-template name="caseLowerAcceptFirstWord">
<xsl:with-param name="data" select="normalize-space(substring($data,2))"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="translate(substring($data,1,1),
'ABCDEFGHIJKLMNOPQRSTUVWXYZ','abcdefghijklmnopqrstuvwxyz')"/>
<!-- put all the chars you want to change
into the last two strings -->
<xsl:call-template name="caseDown">
<xsl:with-param name="data" select="substring($data,2)"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:if>
</xsl:template>
<xsl:template name="caseUP">
<xsl:param name="data"/>
<xsl:if test="$data">
<xsl:choose>
<xsl:when test="starts-with($data,' ')">
<xsl:text> </xsl:text>
<xsl:call-template name="caseLowerAcceptFirstWord">
<xsl:with-param name="data" select="substring($data,2)"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="translate(substring($data,1,1),
'abcdefghijklmnopqrstuvwxyz','ABCDEFGHIJKLMNOPQRSTUVWXYZ')"/>
<!-- put all the chars you want to change
into the last two strings -->
<xsl:call-template name="caseDown">
<xsl:with-param name="data" select="substring($data,2)"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:if>
</xsl:template>
<xsl:template name="caseLowerAcceptFirstWord">
<xsl:param name="data"/>
<xsl:variable name="upperData">
<xsl:call-template name="caseUP">
<xsl:with-param name="data" select="$data"/>
</xsl:call-template>
</xsl:variable>
<xsl:if test="$upperData">
<xsl:value-of select="substring($upperData,1,1)"/>
<xsl:call-template name="caseDown">
<xsl:with-param name="data" select="substring($data,2)"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
A little more info on what I tested. The following XML:
<xml>
<home_address>The Street</home_address>
<po_box>474</po_box>
</xml>
Was output to
<tr><td>Home Address</td><td>The Street</td></tr>
<tr><td>Po Box</td><td>474</td></tr>
If thats not what you wanted your question is to vague