Extract a specific field from JSON output using XSLT - xslt

I have an xml field like below
<INPUT>{"data":"abc1234":"format":"text"}</INPUT>
I need to extract only data value from the JSON string, in this case it abc1234
i am expecting the following output
<OUTPUT>abc1234</OUPUT>
Any help here?

Assuming that your input actually contains a valid JSON in the form of:
<INPUT>{"data":"abc1234","format":"text"}</INPUT>
you could do something like:
<xsl:template match="INPUT">
<OUTPUT>
<xsl:value-of select="substring-before(substring-after(., '"data":"'), '"')"/>
</OUTPUT>
</xsl:template>
However, this will fail if the formatting of the input JSON changes.

With XSLT 3.0 you can use the parse-json() function, and then select the data property value:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="3.0">
<xsl:template match="INPUT">
<OUTPUT><xsl:value-of select="parse-json(text())?data"/></OUTPUT>
</xsl:template>
</xsl:stylesheet>
If your data is JSON. The example is not, you have : separating the properties, instead of ,.

Related

XSLT transformation and CDATA

I have to transform my input xml using XSLT.
It contains, CDATA and I need to extract elements from CDATA and then I have to rename the tag.
Below is my input xml :
<getArtifactContentResponse>
<return>
<![CDATA[
<metadata>
<overview>
<name>scannapp</name>
<developerId>developer702</developerId>
<stateId>2</stateId>
<serverURL>dddd</serverURL>
<id>cspapp1103</id>
<description>scann doc</description>
<hostingTypeId>1</hostingTypeId>
</overview>
</metadata>
]]>
</return>
</getArtifactContentResponse>
And the expected output is :
<?xml version="1.0" encoding="UTF-8"?>
<metadata >
<information>
<name>scannapp</name>
<developerId>developer702</developerId>
<stateId>2</stateId>
<serverURL>ddddd</serverURL>
<id>cspapp1103</id>
<description>scann doc</description>
<hostingTypeId>1</hostingTypeId>
</Information>
</metadata>
XSLT I am using is below :
<xsl:output method="xml" version="1.0" encoding="UTF-8" />
<xsl:template match="/">
<xsl:value-of select="//ns:getArtifactContentResponse/ns:return/text()" disable-output-escaping="yes"/>
</xsl:template>
<xsl:template match="overview">
<Information>
<xsl:apply-templates select="#* | node()" />
</Information>
</xsl:template>
With this I am able to exrtact the CDATA but it is not renaming the element 'overview' to 'Information' .
Transformed xml is below :
<?xml version="1.0" encoding="UTF-8"?>
<metadata>
<overview>
<name>scannapp</name>
<developerId>developer702</developerId>
<stateId>2</stateId>
<serverURL>dddddd</serverURL>
<id>cspapp1103</id>
<description>scann doc</description>
<hostingTypeId>1</hostingTypeId>
</overview>
</metadata>
Can someone tell me how I can rename the tag after extracting the CDATA?
I don't understand what I am missing here?
Thanks in Advance
There are no elements in your CDATA, there is only text. That's what CDATA means: "this stuff might look like markup, but I want it treated as text".
Turning text into elements is called parsing, so to extract the elements from the text in your CDATA you are going to have to parse it. There's no direct way to do this in XSLT until you get to XSLT 3.0 (which has a parse-xml() function). Some XSLT processors have an extension function to do it; in some (I believe) the exslt:node-set() function does this if you supply a string as input. With others, you can call out to your own Java or Javascript code to do the parsing. So it all becomes processor-dependent.
Another approach is to output the XML in your CDATA section using the disable-output-escaping trick, and then process it in a second transformation.
The best approach is to get rid of the CDATA tags before you start. They should never have been put there in the first place.

Escaping Double Quotes, Space and Allowing for an Extra Forward Slash

I have XML
<?xml version="1.0" encoding="UTF-8"?>
<icestats>
<stats_connections>0</stats_connections>
<source mount="/live">
<bitrate>Some data</bitrate>
<server_description>This is what I want to return</server_description>
</source>
</icestats>
And I have XSL
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template match="/">
<xsl:copy-of select="/icestats/source mount="/live"/server_description/node()" />
</xsl:template>
</xsl:stylesheet>
I want the output
This is what I want to return
If I remove the double quotes, space and forward slash from the source it works, but I haven't been able to successfully escape the non standard characters yet using suggested methods in other posts.
For clarity, below is the solution thanks to Lego Stormtroopr
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template match="/">
<xsl:copy-of select="/icestats/source[#mount='/live']/server_description/node()" />
</xsl:template>
</xsl:stylesheet>
There are a couple of issues you will need to resolve before your processor will produce the output you're looking for.
1) Your XML input must be made well-formed. The closing tag of the source element should not include the mount attribute that is specified on the opening tag.
<source mount="/live">
...
</source>
2) The XPath on your xsl:copy-of element must be made valid. The syntax for an XPath expression is (fortunately) not like the syntax for XML elements and attributes. Specifying which source element to match is done by predicating on an attribute value, like you have done, except that you need to use square brackets:
/icestats/source[#mount="/live"]/server_description
In order to use this XPath expression in an XSLT select statement, you will need to make sure that you enclose the entire select attribute value with one type of quotes, and use the other type of quotes within the attribute value, e.g.:
<xsl:value-of select="/icestats/source[#mount='/live']/server_description" />
With This input
<?xml version="1.0" encoding="UTF-8"?>
<icestats>
<stats_connections>0</stats_connections>
<source mount="/live">
<bitrate>Some data</bitrate>
<server_description>This is what I want to return</server_description>
</source>
</icestats>
and this stylesheet
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="text"/>
<xsl:template match="/">
<xsl:value-of select="/icestats/source[#mount='/live']/server_description" />
</xsl:template>
</xsl:stylesheet>
I get the following line of text from xsltproc and saxon:
This is what I want to return
The xsl:value-of element will return the string value of an element (here, that one text node). If you actually wanted the server_description element, then you can use xsl:copy-of to get the whole thing, tags and all. (You would have to update xsl:output as well.)
It looks like you are doing a select based on the attribute, so you just need to properly capture the attribute in the XPath. The quotes you use in the document and the XPath don't need to match, so you can switch them to single quotes ('):
<xsl:copy-of select="/icestats/source[#mount='/live']/server_description/node()" />
(Edited to correct the the missing / from the mount attribute.)
Also, your original document isn't valid XML, as XML doesn't allow attributes in the closing tag.
I think all you need to do is escape the quotes in the attribute string with ":
<xsl:copy-of select="/icestats/source mount="/live"/server_description/node()" />

Filter data from XSLT

I have a XSL file in which I am creating a field like this:
<ServiceText>
<xsl:value-of select="concat(Yrs,'-',Mos,'-',Days,'-',Hrs)" />
</ServiceText>
The values of 'Yrs,'-',Mos,'-',Days,'-',Hrs , I am receiving from a Web service response and assiging it to the XSL directly. I cannot do any modification to the data in code for these fields, because that is how the ocde is. All data manipulation is on the xslt.
I want to do a data filtering on xslt as follows:
if value of yrs =-1 then yrs=""
if value of mos =-1 then mos=""
if value of Days =-1 then Days=""
if value of Hrs =-1 then Hrs=""
How can I do it on the XSL file?
XSLT 2.0:
<xsl:template match="/root">
<ServiceText>
<xsl:value-of select="string-join((Yrs, Mos, Days, Hrs)[.!=-1],'-')" />
</ServiceText>
</xsl:template>
See link for a Working example
In XSLT 1.0 use something like this:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:strip-space elements="*"/>
<xsl:template match="*/*[not(.= -1)]">
<xsl:value-of select="concat(., substring('-', 2 - not(self::Hrs)))"/>
</xsl:template>
<xsl:template match="*/*[. = -1]"/>
</xsl:stylesheet>
When this transformation is applied on the following XML document:
<t>
<Yrs>-1</Yrs>
<Mos>7</Mos>
<Days>15</Days>
<Hrs>3</Hrs>
</t>
the wanted result is produced:
7-15-3
Do Note:
It seems that there is an assumption that the "-1" values form a contiguous group (right to left in the sequence Yrs, Mos,Days).
If this assumption is violated, it would be impossible to understand what is the missing part in 2013-10-8 -- is it the months or the days ?

Generate dynamic xmlns

I would like to dynamically generate xmlns attributes.
I want to generate this in XSL :
<Common:MainPageBase xmlns:Common="clr-namespace:ThisPartIsDynamic;assembly=ThisPartIsDynamic">
</Common:MainPageBase>
How can I do that in XSL?
Thanks,
Alex
Update:
To be more precise, here is what I need to generate. The parts that I want to be able to change with variables are "THISPARTISDYNAMIC":
<Common:MainPageBase
xmlns:Common="clr-namespace:THISPARTISDYNAMIC;assembly=THISPARTISDYNAMIC"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:df="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data.DataForm.Toolkit"
xmlns:controlsToolkit="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Toolkit"
xmlns:basics="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls"
xmlns:data="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data"
xmlns:uc="clr-namespace:THISPARTISDYNAMIC"
mc:Ignorable="d" d:DesignWidth="640" d:DesignHeight="480"
></Common:MainPageBase>
Any ideas?
You can set the namespace of an element dynamically:
<param name="ns1" >http://localhost/ns1</param>
...
<xsl:element name="test" namespace="{$ns1}" >... </xsl:element>
But that doesn't output a namespace prefix -- it changes the default namespace on that element.
I don't think there is a way to output the prefixes with a dynamic namespace URI.
Something like: <xyz:test xmlns:xyz="{$ns1}">
outputs exactly that literally: <xyz:test xmlns:xyz="{$ns1}">
If that is really the exact output you require, then I think you either have
to modify the serializer, or just produce the output with a placeholder URI and
do a text replacement on the output xml text.
[ XSLT does not process XML syntax. It processes XML trees.
Parsing the input and serializing the output are outside of it's realm. ]
Take a look at the article Namespaces in XSLT, and at the section XSLT 1.0: Creating dynamic namespace nodes in particular.
This transformation:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes"/>
<xsl:variable name="vDynamicPart1" select="'DynPArt1'"/>
<xsl:variable name="vDynamicPart2" select="'DynPArt2'"/>
<xsl:template match="/">
<xsl:element name="Common:MainPageBase"
namespace="clr-namespace:{$vDynamicPart1};assembly={$vDynamicPart2}"/>
</xsl:template>
</xsl:stylesheet>
when applied on any XML document (not used), produces the desired result.

Using xslt to alter nodes from a copied xml file

I would like to copy and alter data from another xml data. I have in addition to the normal two input xml files an extra xml file. I would like to embed the entire content of this xml file into my output xml and then change some aspect of it. I have managed to do this by copying the entire file into the right area as desired thus (Thanks to this post here):
<test>
<xsl:copy-of select="document('filename.xml')/*"/>
</test>
Problem is, I want to change some of the data in filename and I don't know how I can get this done. Something along this line, perhaps?
<xsl:template match="document('filename.xml')/root/elemntToBeChanged">
<xsl:apply-templates select="Test/changeItToThis"/>
Try This,
XSLT 2.0:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="#*|node()">
<xsl:copy><xsl:apply-templates select="#*|node()"/></xsl:copy>
</xsl:template>
<xsl:template match="root"><!--Input document's root element -->
<xsl:copy><xsl:apply-templates select="*|document('External-Doc.xml')"/></xsl:copy><!--Extrenal document called here -->
</xsl:template>
</xsl:stylesheet>