JSON to XML SXLT - json

Currently, i try to mapping json response from Rocket Chat (RC) webhook to OTRS XSLT.
Here what i received from RC.
{
"_id":"4jA2dgTGzwsHSsiLd",
"messages":[
{
"username":"guest-3",
"msg":"youuu",
"ts":"2018-12-13T15:13:56.906Z",
},
{
"username":"agent",
"msg":"hahahaha",
"ts":"2018-12-13T15:14:06.268Z"
},
{
"username":"agent",
"msg":"mane tn?",
"ts":"2018-12-13T15:14:08.817Z"
},
]
}
and below is my current OTRS XSLT mapping.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:variable name="title">
<xsl:value-of select="//type" />
</xsl:variable>
<xsl:variable name="custname">
<xsl:value-of select="//visitor/name" />
</xsl:variable>
<xsl:variable name="custemail">
<xsl:value-of select="//visitor/email/address" />
</xsl:variable>
<xsl:variable name="tn">
<xsl:value-of select="//tags" />
</xsl:variable>
<xsl:variable name="createtime">
<xsl:value-of select="//createdAt" />
</xsl:variable>
<xsl:template match="node() | #*">
<xsl:copy>
<xsl:apply-templates select="node() | #*" />
</xsl:copy>
</xsl:template>
<xsl:template match="RootElement">
<xsl:copy>
<TicketNumber><xsl:value-of select="$tn" /></TicketNumber>
<Article>
<Body> ? </Body>
<ContentType>text/html; charset=ISO-8859-15</ContentType>
<Subject><xsl:value-of select="$title" /></Subject>
<From><xsl:value-of select="$custemail" /></From>
<ArticleType>webrequest</ArticleType>
<SenderType>customer</SenderType>
<To>Misc</To>
<HistoryType>AddNote</HistoryType>
<HistoryComment>Note from RC</HistoryComment>
</Article>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="content" />
</xsl:stylesheet>
I would like to get all the value of messages->username and messages->msg from JSON and pass it to the Body tag. Perhaps like :
Msg: hello world
By: guest3
Msg: hello there
By: agent
or maybe html table ?
Thanks ind advance.

Figured this out..
this should be put in tag
<xsl:for-each select="//messages">
<xsl:value-of select="msg" />
<br/>
<xsl:value-of select="username" />
<br/>
<br/>
<xsl:if test="./following-sibling::cd">,</xsl:if>
</xsl:for-each>

Related

Handle excluded element in a foreach

In first template, I am intentionally excluding an element ('milk') because the parsed data map is relatively flat and I would like to use XSLT to categorize and structure the data. The aim is to process the excluded element ('milk') in the second template. The both templates works running them one at a time. Running the templates together will not show the result of the excluded element ('milk') which should set another attribute name and attribute value.
JSON:
<data>
{
"storage": {
"pencils": 12,
"milk": 8,
"rulers": 4
}
}
</data>
XSL:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:transform
version="3.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:storage="http://www.exammple.com/1"
xmlns:office="http://www.exammple.com/2"
xmlns:item="http://www.exammple.com/3"
expand-text="yes">
<xsl:output method="xml" indent="yes"/>
<xsl:mode on-no-match="shallow-skip"/>
<!-- Parse JSON to XML -->
<xsl:template match="data">
<storage:one>
<xsl:apply-templates select="json-to-xml(.)"/>
</storage:one>
</xsl:template>
<!-- Print map -->
<!-- <xsl:template match="*[#key = 'storage']"> <xsl:copy-of select=".."/> </xsl:template> -->
<xsl:template match="*[#key='storage']">
<xsl:for-each select="*[not(#key='milk')]">
<xsl:element name="item:{#key}">
<xsl:attribute name="office">plant-1</xsl:attribute>
<xsl:value-of select="text()"/>
</xsl:element>
</xsl:for-each>
</xsl:template>
<xsl:template match="*[#key='milk']">
<xsl:for-each select=".">
<xsl:element name="item:{#key}">
<xsl:attribute name="beverage">plant-2</xsl:attribute>
<xsl:value-of select="text()"/>
</xsl:element>
</xsl:for-each>
</xsl:template>
</xsl:transform>
Result:
<?xml version="1.0" encoding="UTF-8"?>
<storage:one xmlns:item="http://www.exammple.com/3"
xmlns:office="http://www.exammple.com/2"
xmlns:storage="http://www.exammple.com/1">
<item:pencils office="plant-1">12</item:pencils>
<item:rulers office="plant-1">4</item:rulers>
</storage:one>
Wanted result:
<?xml version="1.0" encoding="UTF-8"?>
<storage:one xmlns:item="http://www.exammple.com/3"
xmlns:office="http://www.exammple.com/2"
xmlns:storage="http://www.exammple.com/1">
<item:pencils office="plant-1">12</item:pencils>
<item:rulers office="plant-1">4</item:rulers>
<item:milk beverage="plant-2">8</item:milk>
</storage:one>
Your second template is never matched, because it is never reached. All elements are processed by <xsl:template match="*[#key='storage']"> - which doesn't have an <xsl:apply-templates ...> to reach further templates.
Your first template does not recurse into its children. So add an <xsl:apply-templates select="*" /> to the end of the first template:
<xsl:template match="*[#key='storage']">
<xsl:for-each select="*[not(#key='milk')]">
<xsl:element name="item:{#key}">
<xsl:attribute name="office">plant-1</xsl:attribute>
<xsl:value-of select="text()"/>
</xsl:element>
</xsl:for-each>
<xsl:apply-templates select="*" />
</xsl:template>
This will try to apply further templates at the level of "storage" and therefore match the second template.
I would write templates for each different output type and if the order of the output is different from the order of the input throw in an xsl:sort or an XPath 3.1 sort call to change the order:
<xsl:template match="data">
<storage:one>
<xsl:apply-templates select="json-to-xml(.)"/>
</storage:one>
</xsl:template>
<xsl:template match="*[#key = 'storage']">
<xsl:apply-templates select="sort(*, (), function($el) { $el/#key = 'milk' })"/>
</xsl:template>
<xsl:template match="*[#key='storage']/*[not(#key='milk')]">
<xsl:element name="item:{#key}">
<xsl:attribute name="office">plant-1</xsl:attribute>
<xsl:value-of select="text()"/>
</xsl:element>
</xsl:template>
<xsl:template match="*[#key='storage']/*[#key='milk']">
<xsl:element name="item:{#key}">
<xsl:attribute name="beverage">plant-2</xsl:attribute>
<xsl:value-of select="text()"/>
</xsl:element>
</xsl:template>

sum of previous nodes as value of current node in XSLT

I am trying to obtain the target xml such that value of a node is comma seperated values of sum of previous similar node values. For e.g:
Input:
<Items>
<Item>
<name>Drakshi</name>
<price>50</price>
</Item>
<Item>
<name>Godambi</name>
<price>30</price>
</Item>
<Item>
<name>Badami</name>
<price>70</price>
</Item>
</Items>
Output:
<result>
50,80,150
</result>
As you can see above it is 50, (50+30), (50+30+70)
I tried with for-each Item and was able to find the sum of only current node and previous selected node. Can you please guide me here
Try something like this:
<xsl:template match="Items">
<result>
<xsl:for-each select="Item">
<xsl:value-of select="sum(preceding-sibling::Item/price | price) " />
<xsl:if test="position() != last()">, </xsl:if>
</xsl:for-each>
</result>
</xsl:template>
While you could use sum(preceding-sibling::...) a more efficient solution would use a recursive template to accumulate the sum one value at a time:
<xsl:template match="/Items">
<result>
<xsl:call-template name="run-total">
<xsl:with-param name="values" select="Item/price"/>
</xsl:call-template>
</result>
</xsl:template>
<xsl:template name="run-total">
<xsl:param name="values"/>
<xsl:param name="i" select="1"/>
<xsl:param name="total" select="0"/>
<xsl:variable name="balance" select="$total + $values[$i]" />
<xsl:value-of select="$balance" />
<xsl:if test="$i < count($values)">
<xsl:text>,</xsl:text>
<xsl:call-template name="run-total">
<xsl:with-param name="values" select="$values"/>
<xsl:with-param name="i" select="$i + 1"/>
<xsl:with-param name="total" select="$balance"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
If you want to solve it with XSLT 2.0 then I don't think you need any user defined function, you can write a single XPath 2.0 expression:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs"
version="2.0">
<xsl:template match="Items">
<result>
<xsl:value-of
select="for $pos in 1 to count(Item)
return sum(subsequence(Item, 1, $pos)/price)"
separator=","/>
</result>
</xsl:template>
</xsl:stylesheet>
Thanks for the replies. I followed this approach which worked for me:
<xsl:function name="kk:getSumTillIndex">
<xsl:param name="index"/>
<xsl:variable name="Var_PriceArray" select="$Items/item[position()<=$index]//price/text()"/>
<xsl:for-each select="$Var_PriceArray">
<Item>
<xsl:value-of select="current()"/>
</Item>
</xsl:for-each>
</xsl:function>
<xsl:function name="kk:calulatePriceSequence">
<xsl:variable name="set" select="$Items/Item" />
<xsl:variable name="count" select="count($set)" />
<xsl:for-each select="$set">
<xsl:if test="position() <= $count">
<xsl:value-of select="sum(kk:getSumTillIndex(position()))"/>
<xsl:if test="position() < number($count)-1">
<xsl:value-of select="','" />
</xsl:if>
</xsl:if>
</xsl:for-each>
</xsl:function>

Saxon XSLT 2.0 casting error the doesnt make sense for me

I am trying to perform simple transformation.
The error reported by the proccessor is: An attribute node (name) cannot be created after the children of the containing element. The error is pointing to this line <xsl:apply-templates select="#*|node()"/> into the last template.
EDIT#1:
This is the input:
<root>
<processor>../../library/saxon-he-9-6-0-7j/saxon9he.jar</processor>
<test-case name="test-case-1">
<object-under-test category="template" name="text-align"/>
<parameters>
<input name="text">text</input>
<input name="min-lenght">8</input>
<input name="align">left</input>
<output name="result"/>
</parameters>
<criteria>
<criterion class="equal" to="'text '"/>
</criteria>
</test-case>
</root>
This is the XSL:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xslAlt="dummy" version="1.0">
<xsl:namespace-alias stylesheet-prefix="xslAlt" result-prefix="xsl"/>
<!--~~~-->
<xsl:template match="root">
<xslAlt:stylesheet version="1.0">
<xslAlt:output method="xml"/>
<xslAlt:include href="../../../product/templates.xsl"/>
<xslAlt:template name="root" match="/">
<xsl:apply-templates select="test-case"/>
</xslAlt:template>
</xslAlt:stylesheet>
</xsl:template>
<!--~~-->
<xsl:template match="test-case">
<test-case name="{concat('test-case-', string(position()))}">
<xsl:variable name="test-case-return" select="concat('test-case-',string(position()),'-return')"/>
<xslAlt:variable name="{$test-case-return}">
<xslAlt:call-template name="{object-under-test/#name}">
<xsl:for-each select="parameters/input">
<xsl:choose>
<xsl:when test="string(number()) = 'NaN'">
<xslAlt:with-param name="{#name}" select="'{.}'"/>
</xsl:when>
<xsl:otherwise>
<xslAlt:with-param name="{#name}" select="{.}"/>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</xslAlt:call-template>
</xslAlt:variable>
<!--~~-->
<xslAlt:variable name="result" select="translate(${$test-case-return},{string('$space')},{string('$nbsp')})"/>
<xsl:apply-templates select="#*|node()"/>
</test-case>
</xsl:template>
<!--~~-->
<xsl:template match="criteria">
<criteria>
<xslAlt:variable name="test-result">
<xslAlt:choose>
<xslAlt:when test="{translate(criterion/#to,' ',' ')} = $result">TEST-PASSED</xslAlt:when>
<xslAlt:otherwise>TEST-FAILED</xslAlt:otherwise>
</xslAlt:choose>
</xslAlt:variable>
<xsl:apply-templates select="#*|node()"/>
</criteria>
</xsl:template>
<!--~~-->
<xsl:template match="#to">
<xsl:attribute name="to">
<xsl:value-of select="translate(.,' ',' ')"/>
<!--translate replace-->
</xsl:attribute>
<xsl:attribute name="result">{<xsl:value-of select="string('$test-result')"/></xsl:attribute>
</xsl:template>
<!--~~-->
<xsl:template match="parameters/output">
<output name="{#name}">
<xslAlt:value-of select="$result"/>
</output>
</xsl:template>
<!--~~-->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/><!--THE ERROR IS POINTING HERE!!!-->
</xsl:copy>
</xsl:template>
<!--~~-->
</xsl:stylesheet>
The error reported by the proccessor is: An attribute node (name)
cannot be created after the children of the containing element.
This is not a "casting error". What it says is that you are trying to create an attribute after some children have already been created.
AFAICT, it happens here:
<xsl:template match="test-case">
<test-case name="{concat('test-case-', string(position()))}">
<!--here child nodes are being created!!! ~-->
<xsl:apply-templates select="#*|node()"/>
</test-case>
</xsl:template>
If you change (line #35):
<xsl:apply-templates select="#*|node()"/>
to:
<xsl:apply-templates select="node()"/>
the error will go away, because now it will stop trying to copy the existing attributes. You would not want this to happen anyway, since it would overwrite the name attribute you have created yourself.
Caveat:
I have not examined your stylesheet in-depth.

How this style sheet can produce HTML output

How to complete the following style sheet that should produce HTML output in figure 1.
I have styled some part of it but could not make it exactly the same as in figure 1. I have tried "copy element" in XSL but gave me duplicate results.
This is my XML:
<?xml version="1.0"?>
<?xml-stylesheet type="text/xsl" href="research.xsl"?>
<ResearchGroups xmlns="http://www.sam.com/sam.html">
<Group name="Intelligent Systems Group" id="ISG"> The Intelligent
Systems Group pursues internationally-leading research in a wide
range of intelligent systems. </Group>
<Group name="Robotics" id="RBT"> The Essex robotics group is one of
the largest mobile robotics groups in the UK. </Group>
<Staff name="Callaghan, Vic" title="Professor" groups="ISG RBT">
Intelligent environments and robotics. </Staff>
<Staff name="Gu, Dongbing" title="Dr" groups="RBT"> Multi-agent
and distributed control systems. </Staff>
</ResearchGroups>
My XSLT:
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:rg="http://www.sam.com/sam.html"
xmlns="http://wwww.w3.org/1999/xhtml"
version="2.0"> <xsl:template match="/">
<html>
<head> <title>Research Groups</title> </head>
<body> <xsl:apply-templates select="//rg:Group"/> </body>
</html> </xsl:template> <xsl:template match="rg:Group">
<xsl:variable name="ID" select="#id"/>
<h3> <a name="{$ID}"> <xsl:value-of select="#name"/> </a> </h3>
<p> <xsl:value-of select="text()"/> </p>
</xsl:template>
</xsl:stylesheet>
HTML Figure 1
the XSLT style sheet should output the following HTML
Something like this I think:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:rg="http://www.sam.com/sam.html" xmlns="http://wwww.w3.org/1999/xhtml" version="2.0">
<xsl:template match="/">
<html>
<head>
<title>Research Groups</title>
</head>
<body>
<xsl:apply-templates select="//rg:Group"/>
</body>
</html>
</xsl:template>
<xsl:template match="rg:Group">
<xsl:variable name="ID" select="#id"/>
<h3>
<a name="{$ID}">
<xsl:value-of select="#name"/>
</a>
</h3>
<p>
<xsl:value-of select="text()"/>
</p>
<ul>
<xsl:apply-templates select="//rg:Staff[contains(#groups, current()/#id)]">
<xsl:with-param name="curGroup"><xsl:value-of select="#id"/></xsl:with-param>
</xsl:apply-templates>
</ul>
</xsl:template>
<xsl:template match="rg:Staff">
<xsl:param name="curGroup"/>
<li>
<xsl:value-of select="#name"/>
<xsl:text>: </xsl:text>
<xsl:value-of select="text()"/>
<xsl:if test="//rg:Group[(#id != $curGroup) and contains(current()/#groups, #id)]">
<xsl:text> ( </xsl:text>
<xsl:apply-templates select="//rg:Group[(#id != $curGroup) and contains(current()/#groups, #id)]" mode="otherGroups"/>
<xsl:text> ) </xsl:text>
</xsl:if>
</li>
</xsl:template>
<xsl:template match="rg:Group" mode="otherGroups">
<a href="#{#id}">
<xsl:value-of select="#id"/>
</a>
</xsl:template>
</xsl:stylesheet>

Replace HTML Quotes with Plain Text in XSLT

We are using XSLT to translate a RIXML file to XML. Our RIXML contains the following text:
<Title><![CDATA[Looking into the future: Reiterate ‘Buy’]]></Title>
8216 is a left quote and 8217 is a right quote.
How would I replace these HTML codes with their plain text representation, or more simply, just a single quote? This is XSLT version 1.0.
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:variable name="vQ2">""</xsl:variable>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="text()">
<xsl:value-of select=
"translate(.,'‘’',$vQ2)"/>
</xsl:template>
</xsl:stylesheet>
I came upon this function which worked well for me. I call it to replace all the characters I need to:
<xsl:template name="string-replace-all">
<xsl:param name="text" />
<xsl:param name="replace" />
<xsl:param name="by" />
<xsl:choose>
<xsl:when test="contains($text, $replace)">
<xsl:value-of select="substring-before($text,$replace)" />
<xsl:value-of select="$by" />
<xsl:call-template name="string-replace-all">
<xsl:with-param name="text"
select="substring-after($text,$replace)" />
<xsl:with-param name="replace" select="$replace" />
<xsl:with-param name="by" select="$by" />
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$text" />
</xsl:otherwise>
</xsl:choose>
</xsl:template>