When transforming an XML document with XSLT, is it possible to convert embedded JSON (i.e. JSON formatted content) in the process?
For example the following: -
<form>
<data>[{"id":1,"name":"Hello"},{"id":2,"name":"World"}]</data>
</form>
Would be converted to: -
<form>
<data>
<id name="Hello">1</id>
<id name="World">2</id>
</data>
</form>
Parsing JSON is supported in XSLT 3.0 so using the commercial versions of Saxon 9.7 you could use
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:math="http://www.w3.org/2005/xpath-functions/math"
exclude-result-prefixes="xs math"
version="3.0">
<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:mode on-no-match="shallow-copy"/>
<xsl:template match="data">
<xsl:copy>
<xsl:apply-templates select="parse-json(.)?*"/>
</xsl:copy>
</xsl:template>
<xsl:template match=".[. instance of map(xs:string, item())]">
<id name="{.?name}">
<xsl:value-of select=".?id"/>
</id>
</xsl:template>
</xsl:stylesheet>
Using the open source version of Saxon 9.7 (i.e. Saxon 9.7 HE) the following takes up the suggestion made by wero to use json-to-xml and shows how to implement the requirement:
<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"
exclude-result-prefixes="xs math fn"
version="3.0">
<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="data">
<xsl:copy>
<xsl:apply-templates select="json-to-xml(.)//fn:map"/>
</xsl:copy>
</xsl:template>
<xsl:template match="fn:map">
<id name="{fn:string[#key = 'name']}">
<xsl:value-of select="fn:number[#key = 'id']"/>
</id>
</xsl:template>
</xsl:stylesheet>
Saxon 9.7 HE is available on Maven and from http://saxon.sourceforge.net/
It should be possible in XSLT 3.0, given that it has a json-to-xml function:
Parses a string supplied in the form of a JSON text, returning the
results in the form of an XML document node.
You could try to get this to run with the current implementation in Saxon.
Related
I have an XML file here:
<?xml version="1.0" encoding="utf-16"?>
<class>
<student>
<name>Ben</name>
<age>1</age>
<ages>4</ages>
<entryprofile></entryprofile>
<node>1</node>
</student>
<student>
<name>Steve</name>
<age>2</age>
<ages>3</ages>
<entryprofile></entryprofile>
<node>1</node>
</student>
</class>
I am trying to promote the entryprofile element so that it becomes a child element of student, rather than an attribute of student--and I want it to contain node. I have tried to apply the following XSL in order to do this:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="student">
<xsl:copy-of select="name"/>
<new-level>
<xsl:copy-of select="entryprofile"/>
</new-level>
</xsl:template>
</xsl:stylesheet>
This doesn't seem to be doing much apart from this:
<?xml version="1.0" encoding="utf-16"?>
<name>Ben</name><new-level><entryprofile>g</entryprofile></new-level>
<name>Steve</name><new-level><entryprofile>g</entryprofile></new-level>
But what I am looking for is this:
<?xml version="1.0" encoding="utf-16"?>
<class>
<student>
<name>Ben</name>
<age>1</age>
<ages>4</ages>
<entryprofile>
<node>1</node>
</entryprofile>
</student>
<student>
<name>Steve</name>
<age>2</age>
<ages>3</ages>
<entryprofile>
<node>1</node>
</entryprofile>
</student>
</class>
You can see there that entryprofile for both stdudents has become a child on account of node becoming a child of entryprofile.
Would anyone know where I am going wrong, and what I can do to achieve my desired result? Many thanks.
This XSLT will produce the desired result:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="entryprofile">
<xsl:copy>
<xsl:copy-of select="../node"/>
</xsl:copy>
</xsl:template>
<xsl:template match="node"/>
</xsl:stylesheet>
Here are two alternatives that accomplish the same thing in slightly different ways:
This one is the correct version of what you tried to do:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<!-- identity transform -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="student">
<xsl:copy>
<xsl:copy-of select="name | age | ages | profile"/>
<entryprofile>
<xsl:copy-of select="node"/>
</entryprofile>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
This one is a shorter version of the above:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="/class">
<xsl:copy>
<xsl:for-each select="student">
<xsl:copy>
<xsl:copy-of select="name | age | ages | profile"/>
<entryprofile>
<xsl:copy-of select="node"/>
</entryprofile>
</xsl:copy>
</xsl:for-each>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
I'm trying to convert HTML content to uppercase using XSLT, but the requirement is to keep the tag hierarchy unaltered (i.e., just change the text).
For example: <p>some text <b>other text</b></p>
should result in
<p>SOME TEXT <b>OTHER TEXT</b></p>.
With the following XSLT I managed to convert the text to uppercase, but the result loses the tag hierarchy.
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output encoding="UTF-8" indent="no" method="xhtml" standalone="0" version="1.0"/>
<xsl:variable name="smallcase" select="'abcdefghijklmnopqrstuvwxyz'"/>
<xsl:variable name="uppercase" select="'ABCDEFGHIJKLMNOPQRSTUVWXYZ'"/>
<xsl:template match="/">
<xsl:value-of select="translate(/, $smallcase, $uppercase)"/>
</xsl:template>
</xsl:transform>
Is there any way to keep the tags unaltered?
Thanks in advance.
Try:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:variable name="smallcase" select="'abcdefghijklmnopqrstuvwxyz'"/>
<xsl:variable name="uppercase" select="'ABCDEFGHIJKLMNOPQRSTUVWXYZ'"/>
<!-- identity transform -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="text()">
<xsl:value-of select="translate(., $smallcase, $uppercase)"/>
</xsl:template>
</xsl:stylesheet>
Note that xhtml is not a valid output method in XSLT 1.0.
Updated with Minimal, Complete and Verifiable information thanks #Kenneth for drawing my attention.
So, I'm trying to rename an xml attribute using xslt, but I keep getting this error message:
Reference to undeclared namespace prefix: 'json'.
This is my source xml:
<?xml version="1.0" encoding="utf-8"?>
<data>
<code>SO000009</code>
<businessPartner>70833A356B9A428CBDDCD2A76A49681F</businessPartner>
<startDateTime>2018-01-25T15:24:27Z</startDateTime>
<subject>Test</subject>
<equipments jsonArray="true">80202</equipments>
</data>
This is how I want the xml to be:
<?xml version="1.0" encoding="utf-16"?>
<data xmlns:json="http://james.newtonking.com/projects/json">
<code>SO000009</code>
<businessPartner>70833A356B9A428CBDDCD2A76A49681F</businessPartner>
<startDateTime>2018-01-25T15:24:27Z</startDateTime>
<subject>Test</subject>
<equipments json:Array="true">80202</equipments>
</data>
And this is my xslt:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:template match="/data">
<data xmlns:json="http://james.newtonking.com/projects/json">
<xsl:apply-templates select="node()|#*" />
</data>
</xsl:template>
<xsl:template match="#*">
<xsl:attribute name="{local-name()}">
<xsl:value-of select="." />
</xsl:attribute>
</xsl:template>
<xsl:template match="node()">
<xsl:copy>
<xsl:apply-templates select="node()|#*" />
</xsl:copy>
</xsl:template>
<xsl:template match="#jsonArray">
<xsl:attribute name="json:Array">
<xsl:value-of select="."/>
</xsl:attribute>
</xsl:template>
</xsl:stylesheet>
When I'm trying to convert my xml, I keep getting this message:
Error occurred while compiling stylesheet 'CS_jsonArray.xslt'.
Code: 0x80004005
Reference to undeclared namespace prefix: 'json'.
After looking in the 'Related' sidebar, I did found the answer to my own question.
XSL transformation - Namespace prefix undeclared
So for those who have a same issue, be sure the namespace you want to change the attribute names for, is also in the xslt tag itself...
This is how my xslt started:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>...
This is how I changed it:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:json="http://james.newtonking.com/projects/json">
<xsl:output indent="yes"/>...
I worked with xslt 2.0 and Java( Saxon-HE ).Saxon-HE do not support xslt date-time format.I have current-date() and current-time() functions and I need them in the following format time(17:31), date(03/06).Please help me and give any suggestion hot to do it.Thanks a lot of
Please try the below tranformation:
<?xml version='1.0'?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xsl:output method="text"/>
<xsl:template match="test">
<xsl:variable name="currenttime" select="current-dateTime()" as="xs:dateTime"/>
<xsl:value-of select="format-dateTime($currenttime,'[H]')"/><xsl:text>:</xsl:text><xsl:value-of select="format-dateTime($currenttime,'[m]')"/>
<xsl:text>
</xsl:text>
<xsl:value-of select="format-dateTime($currenttime,'[M]')"/><xsl:text>/</xsl:text><xsl:value-of select="format-dateTime($currenttime,'[D]')"/>
</xsl:template>
</xsl:stylesheet>
I am using XSLTJSON to convert my XML to JSON. My raw XML is not in the format that I want, so I first pass it through an XSL stylesheet to clean it up and then pass the output of that stylesheet into XSLTJSON.
Right now I'm doing this by calling transformers serially. I'd like to streamline it and have only one call to the transformer necessary. Is there a way to write an XSL stylesheet that includes json.xsl, matches on "/", does it's thing and then passes its output to json:generate()?
This stylesheet:
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:json="http://json.org/">
<xsl:import href="xml-to-json.xsl"/>
<xsl:template match="/">
<xsl:variable name="vFirstPass">
<xsl:apply-templates/>
</xsl:variable>
<xsl:value-of select="json:generate($vFirstPass)"/>
</xsl:template>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>