XSLT Apply Template for Specific list of value - java

I am beginner in XSLT.
My Source XML is as below
<Options>
<Option>
<Data>Data1</Data>
<Type>A</Type>
</Option>
<Option>
<Data>Data2</Data>
<Type>B</Type>
</Option>
<Option>
<Data>Data3</Data>
<Type>C</Type>
</Option>
<Option>
<Data>Data4</Data>
<Type>D</Type>
</Option>
...
</Options>
I have parameter which is used to filter the result from above method and it is as below
<xsl:param name="filterType" select="'A,C'"/>
The output should be as below:
<Result>
<Data Type="A">Data1<Data>
<Data Type="C">Data3<Data>
</Result>
Below is the XSLT i have created:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:param name="filterType" select="'A,C'"/>
<xsl:template match="Options">
<xsl:element name="Result">
<xsl:apply-templates select="Option"/>
</xsl:element>
</xsl:template?
<xsl:template match="Option">
<xsl:element name="Data">
<xsl:attribute name="Type">
<xsl:value-of select="Type"/>
</xsl:attribute>
<xsl:value-of select="Data"/>
</xsl:element>
</xsl:template?
</xsl:stylesheet>
While applying template for 'Option' tag i need to use filterType.
How can i do that? Please Help.

I think you simply want
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:param name="filterType" select="'A,C'"/>
<xsl:variable name="filter" select="concat(',', $filterType, ',')"/>
<xsl:template match="Options">
<Result>
<xsl:apply-templates select="Option[contains($filter, concat(',', Type, ','))]"/>
</Result>
</xsl:template>
<xsl:template match="Option">
<Data Type="{Type}">
<xsl:value-of select="Data"/>
</Data>
</xsl:template>
</xsl:stylesheet>

Related

Can you declare / generate xslt params dynamically?

I'm trying to generate an xml with the help of xslt templates and I have a tricky thing to do:
I have to generate a certain number of elements that have same tags but with different values inside them. Example of xslt stylesheet:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:param name="NbOfBatches"/>
<xsl:param name="wholeTag"/>
<xsl:output method="xml" encoding="utf-8" indent="yes" omit-xml-declaration="yes"/>
<xsl:template match="/">
<Document xmlns="urn:something" xmlns:file="someUrn>
<FileAppHdr>
<file:NbOfBatches><xsl:value-of select="$NbOfBatches"></xsl:value-of></file:NbOfBatches>
<xsl:call-template name="selects">
<xsl:with-param name="i">1</xsl:with-param>
<xsl:with-param name="count"><xsl:value-of select="$NbOfBatches"/></xsl:with-param>
</xsl:call-template>
</FileAppHdr>
</Document>
</xsl:template>
<xsl:template name="selects">
<xsl:param name="i" />
<xsl:param name="count" />
<xsl:if test="$i <= $count">
<xsl:call-template name="credit">
<xsl:with-param name="param0"/>
</xsl:call-template>
</xsl:if>
<xsl:if test="$i <= $count">
<xsl:call-template name="selects">
<xsl:with-param name="i">
<xsl:value-of select="$i + 1"/>
</xsl:with-param>
<xsl:with-param name="count">
<xsl:value-of select="$count"/>
</xsl:with-param>
</xsl:call-template>
</xsl:if>
</xsl:template>
<xsl:template name="credit">
<xsl:param name="param0"/>
<CreditTransfer>
<xsl:value-of select="$param0"/>
</CreditTransfer>
</xsl:template>
</xsl:stylesheet>
In this way, I can use Saxon or JAXP and call .setParameter() for NbOfBatches, it will generate that number of tags. However is there a way to generate / modify name of param0 inside CreditTransfer so that I can loop through data in Java and use .setParameter("param1", value1) and so on ?
Thank you
Rather than trying to pass structured data as a stylesheet parameter, why not generate that structured data as XML and pass that XML document as the source document of the transformation?
As an example, imagine a source XML document with param0 in each Batch:
<Batches>
<Batch>
<param0>it was the best of times</param0>
</Batch>
<Batch>
<param0>it was the worst of times</param0>
</Batch>
<Batch>
<param0>it was the age of wisdom</param0>
</Batch>
<Batch>
<param0>it was the age of foolishness</param0>
</Batch>
</Batches>
with a simplified version of your stylesheet:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" encoding="utf-8" indent="yes" omit-xml-declaration="yes"/>
<xsl:template match="/">
<Document xmlns:file="someUrn">
<FileAppHdr>
<file:NbOfBatches><xsl:value-of select="count(//Batch)"/></file:NbOfBatches>
<xsl:for-each select="//Batch">
<xsl:call-template name="selects">
<xsl:with-param name="param0" select="param0" />
</xsl:call-template>
</xsl:for-each>
</FileAppHdr>
</Document>
</xsl:template>
<xsl:template name="selects">
<xsl:param name="param0" />
<xsl:call-template name="credit">
<xsl:with-param name="param0" select="$param0" />
</xsl:call-template>
</xsl:template>
<xsl:template name="credit">
<xsl:param name="param0"/>
<CreditTransfer>
<xsl:value-of select="$param0"/>
</CreditTransfer>
</xsl:template>
</xsl:stylesheet>
produces this output:
<Document xmlns:file="someUrn">
<FileAppHdr>
<file:NbOfBatches>4</file:NbOfBatches>
<CreditTransfer>it was the best of times</CreditTransfer>
<CreditTransfer>it was the worst of times</CreditTransfer>
<CreditTransfer>it was the age of wisdom</CreditTransfer>
<CreditTransfer>it was the age of foolishness</CreditTransfer>
</FileAppHdr>
</Document>
It's fairly trivial to generate XML from structured data in Java using the Saxon sapling classes. https://www.saxonica.com/html/documentation10/javadoc/net/sf/saxon/sapling/package-summary.html

Generate xpath of XML using XSLT for all Nodes

I was trying to find a way to convert the XML tags to their respective unique address like xPath. Nd I found an XSLT, where XML is processed and the unique address is created only for the nodes which doesnt have child elements and with attributes. [link]:Generate/get xpath from XML node java
XSLT :
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:variable name="vApos">'</xsl:variable>
<xsl:template match="*[#* or not(*)] ">
<xsl:if test="not(*)">
<xsl:apply-templates select="ancestor-or-self::*" mode="path"/>
<xsl:value-of select="concat('=',$vApos,.,$vApos)"/>
<xsl:text>
</xsl:text>
</xsl:if>
<xsl:apply-templates select="#*|*"/>
</xsl:template>
<xsl:template match="*" mode="path">
<xsl:value-of select="concat('/',name())"/>
<xsl:variable name="vnumPrecSiblings" select=
"count(preceding-sibling::*[name()=name(current())])"/>
<xsl:if test="$vnumPrecSiblings">
<xsl:value-of select="concat('[', $vnumPrecSiblings +1, ']')"/>
</xsl:if>
</xsl:template>
<xsl:template match="#*">
<xsl:apply-templates select="../ancestor-or-self::*" mode="path"/>
<xsl:value-of select="concat('[#',name(), '=',$vApos,.,$vApos,']')"/>
<xsl:text>
</xsl:text>
</xsl:template>
</xsl:stylesheet>
when passed to this
<?xml version="1.0" encoding="UTF-8"?>
<root>
<main>
<tag1>001</tag1>
<tag2>002</tag2>
<tag3>
<tag4>004</tag4>
</tag3>
<tag2>002</tag2>
<tag5>005</tag5>
</main>
</root>
produces
/root/main/tag1='001' /root/main/tag2='002' /root/main/tag3/tag4='004' /root/main/tag2[2]='002' /root/main/tag5='005'
So I need the xslt to generate in the following way
/root
/root/main
/root/main/tag1
/root/main/tag2
/root/main/tag3
/root/main/tag3/tag4
/root/main/tag2[2]
/root/main/tag5
Also I dont need values. So please help me with this
Your result could be produced rather simply by:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" encoding="UTF-8"/>
<xsl:template match="*">
<xsl:for-each select="ancestor-or-self::*">
<xsl:value-of select="name()" />
<xsl:variable name="i" select="count(preceding-sibling::*[name()=name(current())])"/>
<xsl:if test="$i">
<xsl:value-of select="concat('[', $i + 1, ']')"/>
</xsl:if>
<xsl:if test="position()!=last()">
<xsl:text>/</xsl:text>
</xsl:if>
</xsl:for-each>
<xsl:text>
</xsl:text>
<xsl:apply-templates select="*"/>
</xsl:template>
</xsl:stylesheet>
Note that this does not process attributes (or any other type of nodes other than elements).

Producing html xslt from xsl:fo

I'm tring to transform an xsl:fo into xslt (for HTML output). Then, I would apply xslt instead of xsl:fo obtaining the HTML output instead of a PDF.
How can do this?
I need API for XML Processing, or JAXP that transforms XML and XSL to another output. So, I tried to write the xslt template:
<xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:fo="http://www.w3.org/1999/XSL/Format" exclude-result-prefixes="fo"
>
<xsl:template match="/xsl:template[#match='/root']/fo:root">
<xsl:apply-templates select="fo:page-sequence"/>
</xsl:template>
<xsl:template match="fo:page-sequence">
<xsl:for-each select="fo:flow[#flow-name='xsl-region-body']">
<xsl:call-template name="xsl-regional-body">
<xsl:with-param name="fontsize"><xsl:value-of select="#font-size"/></xsl:with-param>
</xsl:call-template>
</xsl:for-each>
</xsl:template>
<xsl:template name="xsl-regional-body">
<xsl:param name="fontsize" />
<body>
<xsl:if test="$fontsize"> <!-- costruisce <font size=""> -->
<font>
<xsl:attribute name="size">
<xsl:value-of select="$fontsize"/>
</xsl:attribute>
</font>
</xsl:if>
<xsl:for-each select="*/xsl:choose">
<xsl:call-template name="xsl-choose"/>
</xsl:for-each>
<xsl:apply-templates select="."/>
</body>
</xsl:template>
<xsl:template name="xsl-choose">
<xsl:value-of select="."/>
</xsl:template>
I obtain something like
<body><font size="10pt"/>
...
text words..
</body>
But it delete all xsl:choose xsl:when and other tags like
I need all these tags because i need to pass xml data in second pass using Jaxp and producing html..
I would obtain
<body><font size="10pt"/>
<xsl:choose>
<xsl:when test="ddx[#id='LET.....>
<xsl::value-of select="ddx[#id='Lx']/r/PE...>
</xsl:when>..
</xsl:choose>
text words..
</body>
How can get the XSL nodes like text node?
If you want to use XSLT to output XSLT elements (i.e. elements in the XSLT namespace) then you need to use a namespace alias as shown in http://www.w3.org/TR/xslt#literal-result-element:
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:fo="http://www.w3.org/1999/XSL/Format"
xmlns:axsl="http://www.w3.org/1999/XSL/TransformAlias">
<xsl:namespace-alias stylesheet-prefix="axsl" result-prefix="xsl"/>
<xsl:template match="/">
<axsl:stylesheet>
<xsl:apply-templates/>
</axsl:stylesheet>
</xsl:template>
<xsl:template match="block">
<axsl:template match="{.}">
<fo:block><axsl:apply-templates/></fo:block>
</axsl:template>
</xsl:template>
</xsl:stylesheet>
i put xsl: code between < ! CDATA [... ] ] >
anyway using another namespace

Convert text to xml using XSLT

I have a text file as given below:
value1 value2 value3 value4
I want to convert it as following xml using XSLT
<values>
<value>value1</value>
<value>value2</value>
<value>value3</value>
<value>value4</value>
</values>
Thanks in advance.
Assuming XSLT 2.0,
<xsl:template name="main">
<values>
<xsl:for-each select="tokenize(unparsed-text('input.txt'), '\s+')">
<value><xsl:value-of select="."/></value>
</xsl:for-each>
</values>
</xsl:template>
if you can edit your input such that it contains a root element, and a tab character as a separator, such as below:
<root>value1 value2 value3 value4</root>
then, you can apply the following stylesheet:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:output indent="yes" omit-xml-declaration="yes"/>
<xsl:template match="/">
<values>
<xsl:call-template name="tokenizeString">
<xsl:with-param name="list" select="."/>
<xsl:with-param name="delimiter" select="' '"/>
</xsl:call-template>
</values>
</xsl:template>
<xsl:template name="tokenizeString">
<!--passed template parameter -->
<xsl:param name="list"/>
<xsl:param name="delimiter"/>
<xsl:choose>
<xsl:when test="contains($list, $delimiter)">
<value>
<!-- get everything in front of the first delimiter -->
<xsl:value-of select="substring-before($list,$delimiter)"/>
</value>
<xsl:call-template name="tokenizeString">
<!-- store anything left in another variable -->
<xsl:with-param name="list" select="substring-after($list,$delimiter)"/>
<xsl:with-param name="delimiter" select="$delimiter"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:choose>
<xsl:when test="$list = ''">
<xsl:text/>
</xsl:when>
<xsl:otherwise>
<value>
<xsl:value-of select="$list"/>
</value>
</xsl:otherwise>
</xsl:choose>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
and produce:
<values>
<value>value1</value>
<value>value2</value>
<value>value3</value>
<value>value4</value>
</values>

Why does this xslt transformation replace all occurences?

Using http://xslt.online-toolz.com/tools/xslt-transformation.php
.xml
<?xml version="1.0"?>
<my:project xmlns:my="http://myns">
<my:properties>
<my:property>
<my:name>customerId</my:name>
<my:value>1</my:value>
</my:property>
<my:property>
<my:name>userId</my:name>
<my:value>20</my:value>
</my:property>
</my:properties>
</my:project>
I now want to look for the name customerId and want to replace the value.
It almost works, but it replaces ALL values in the document. What am I doing wrong to just replace the value where the name mached?
.xsl
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:my="http://myns" xmlns:saxon="http://saxon.sf.net">
<xsl:param name="name" select="'customerId'"/>
<xsl:param name="value" select="'0'"/>
<xsl:template match="node() | #*">
<xsl:copy>
<xsl:apply-templates select="node() | #*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/*/my:properties/my:property/my:value/text()" >
<xsl:choose>
<xsl:when test="/*/my:properties/my:property/my:name = $name">
<xsl:value-of select="$value"/>
</xsl:when>
<xsl:otherwise><xsl:copy-of select="saxon:parse(.)" /></xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
The test for /*/my:properties/my:property/my:name = $name always succeed because it uses an absolute path, and so the result is independent of the surrounding template context. A test with a relative xpath should work.
XSL:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:my="http://myns" xmlns:saxon="http://saxon.sf.net">
<xsl:param name="name" select="'customerId'"/>
<xsl:param name="value" select="'0'"/>
<xsl:template match="node() | #*">
<xsl:copy>
<xsl:apply-templates select="node() | #*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/*/my:properties/my:property/my:value/text()" >
<xsl:choose>
<xsl:when test="../../my:name = $name">
<xsl:value-of select="$value"/>
</xsl:when>
<xsl:otherwise>otherwise</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
XML:
<my:project xmlns:my="http://myns">
<my:properties>
<my:property>
<my:name>customerId</my:name>
<my:value>1</my:value>
</my:property>
<my:property>
<my:name>userId</my:name>
<my:value>20</my:value>
</my:property>
</my:properties>
</my:project>
Result of saxonb-xslt -s:test.xml -xsl:test.xsl
<my:project xmlns:my="http://myns">
<my:properties>
<my:property>
<my:name>customerId</my:name>
<my:value>0</my:value>
</my:property>
<my:property>
<my:name>userId</my:name>
<my:value>otherwise</my:value>
</my:property>
</my:properties>
</my:project>

Categories

Resources