I'd like to apply template in which specified element contains value of array prefixed with some constant.
<xsl:variable name="coreTables"
select="('TAB1', 'TAB2')" />
<xsl:template match="node()[not(self::*)]">
<xsl:copy>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
<xsl:template match="*">
<xsl:element name="{local-name()}">
<xsl:apply-templates select="node()|#*"/>
</xsl:element>
</xsl:template>
<xsl:template match="#*">
<xsl:attribute name="{local-name()}">
<xsl:value-of select="."/>
</xsl:attribute>
</xsl:template>
<xsl:template match="databaseChangeLog">
<xsl:comment> CORE TABLES </xsl:comment>
<xsl:apply-templates select="changeSet[createTable/#tableName=$coreTables]"/>
<xsl:comment> CORE SEQUENCES </xsl:comment>
<xsl:apply-templates select="changeSet[createSequence/#sequenceName='SEQ_'[$coreTables]]"/>
</xsl:template>
this is sample xml:
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.6.xsd
http://www.liquibase.org/xml/ns/dbchangelog">
<changeSet id="1" author="a">
<createTable tableName="TAB1">
<column></column>
</createTable>
</changeSet>
<changeSet id="1-1" author="a">
<createSequence sequenceName="SEQ_TAB1" />
</changeSet>
<changeSet id="4" author="A">
<createTable tableName="TAB4">
<column></column>
</createTable>
</changeSet>
</databaseChangeLog>
So with with last apply-templates I'd like to match all nodes of createSequence where attribute sequenceName is SEQ_+value of some coreTables. But I don't know how to write this select or if it's even possible like this.
I'm using xslt 2 and saxon 9.8he.
There a number of ways you could do this. Here's a couple...
<xsl:apply-templates
select="changeSet[createSequence/#sequenceName = (for $i in $coreTables return concat('SEQ_', $i))]"/>
<xsl:apply-templates
select="changeSet[createSequence[starts-with(#sequenceName, 'SEQ_') and substring-after(#sequenceName, 'SEQ_') = $coreTables]]"/>
Related
Given the following xml :
<tree>
<val>0</val>
<tree>
<val>1</val>
<tree>
<val>3</val>
</tree>
<tree>
<val>4</val>
</tree>
</tree>
<tree>
<val>2</val>
<tree>
<val>5</val>
</tree>
<tree>
<val>6</val>
</tree>
</tree>
</tree>
I need to transform it into this xml:
<root>0
<frst>1
<leaf>3</leaf>
<leaf>4</leaf>
</frst>
<second>2
<leaf>5</leaf>
<leaf>6</leaf>
</second>
</root>
This is my attempt that gives the same result, I recently started learning XSLT i'm not sure what other option I have, can this be improved or done in another way ?
Thank you for your help
This is my attempt :
<?xml version='1.0' encoding='utf-8'?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" encoding="UTF-8"/>
<xsl:template match="/tree">
<root>
<xsl:apply-templates />
</root>
</xsl:template>
<xsl:template match="val">
<xsl:value-of select="."/>
</xsl:template>
<xsl:template match="tree">
<xsl:choose>
<!-- IF HAS CHILDREN -->
<xsl:when test="child::tree">
<xsl:if test="(count(preceding-sibling::tree)+1) = 1">
<frst>
<xsl:apply-templates/>
</frst>
</xsl:if>
<xsl:if test="(count(preceding-sibling::tree)+1) = 2">
<second>
<xsl:apply-templates/>
</second>
</xsl:if>
</xsl:when>
<!-- ELSE IS A LEAF -->
<xsl:otherwise>
<leaf>
<xsl:apply-templates/>
</leaf>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
test test
I would think the conditions can be written in match patterns:
<xsl:template match="tree[tree][1]">
<frst>
<xsl:apply-templates/>
</frst>
</xsl:template>
<xsl:template match="tree[tree][2]">
<second>
<xsl:apply-templates/>
</second>
</xsl:template>
<xsl:template match="tree[not(tree)]">
<leaf>
<xsl:apply-templates/>
</leaf>
</xsl:template>
although I guess your current code doesn't process the third/fourth/fifth.. tree children so an additional <xsl:template match="tree[tree][position() > 2]"/> might be necessary.
I've following xml
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.6.xsd">
<changeSet author="system (generated)" id="1538720867962-1">
<createTable tableName="AS_JOURNALEVENTDETAILATTRMAP">
<column name="JOURNALEVENTTYPEID" type="NUMBER(9, 0)">
<constraints primaryKey="true" primaryKeyName="PK$AS_JOURNALEVENTDETATTRMAP"/>
</column>
<column name="JOURNALEVENTDETAILATTRID" type="NUMBER(9, 0)">
<constraints primaryKey="true" primaryKeyName="PK$AS_JOURNALEVENTDETATTRMAP"/>
</column>
<column name="LISTORDER" type="NUMBER(9, 0)">
<constraints nullable="false"/>
</column>
</createTable>
</changeSet>
<changeSet id="c529c6ea-45c2-4ec2-8c9d-7bc935434d21" author="system">
<setTableRemarks remarks="this is wrong"
tableName="AS_JOURNALEVENTDETAILATTRMAP"/>
<setColumnRemarks tableName="AS_JOURNALEVENTDETAILATTRMAP"
columnName="JOURNALEVENTTYPEID"
remarks="Journal event type identifier"/>
<setColumnRemarks tableName="AS_JOURNALEVENTDETAILATTRMAP"
columnName="JOURNALEVENTDETAILATTRID"
remarks="Journal event detail attribute identifier"/>
<setColumnRemarks tableName="AS_JOURNALEVENTDETAILATTRMAP"
columnName="LISTORDER"
remarks="Order in list"/>
</changeSet>
</databaseChangeLog>
and the exact same document with name fixedremarks.xml but there is little change <setTableRemarks remarks="this is ok" tableName="AS_JOURNALEVENTDETAILATTRMAP"/>
with following template I'm trying to fix attribute remarks inside setTableRemarks but without success - I don't know how to properly copy that attribute from external xml.
<xsl:transform version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xpath-default-namespace="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns="http://www.liquibase.org/xml/ns/dbchangelog">
<xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>
<xsl:variable name="originalChangeLog" select="document('/tmp/fixedremarks.xml')"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:key name="remarkTableName" match="setTableRemarks" use="#tableName"/>
<xsl:template match="changeSet[setTableRemarks]">
<xsl:variable name="currentRemarkTable" select="setTableRemarks/#tableName"/>
<xsl:comment select="$currentRemarkTable"/>
<xsl:copy>
<xsl:copy-of select="#*"/>
<xsl:element name="setTableRemarks">
<xsl:attribute name="remarks"
select="$originalChangeLog/key('remarkTableName', $currentRemarkTable)"/>
<xsl:attribute name="tableName" select="setTableRemarks/#tableName"/>
</xsl:element>
<xsl:copy-of select="*[not(self::setTableRemarks)]"/>
</xsl:copy>
</xsl:template>
</xsl:transform>
can somebody tell me how to map properly remark from external document?
I think you just want the identity transformation template you have plus
<xsl:template match="changeSet/setTableRemarks[key('remarkTableName', #tableName, $originalChangeLog)]/#remarks">
<xsl:attribute name="{name()}" select="key('remarkTableName', ../#tableName, $originalChangeLog)/#remarks"/>
</xsl:template>
https://xsltfiddle.liberty-development.net/6qVRKwR has an online sample (there the secondary XML is inlined as a variable for a self-contained example but if you keep your <xsl:variable name="originalChangeLog" select="document('/tmp/fixedremarks.xml')"/> it will work as well.
this is sample xml:
<?xml version="1.0" encoding="UTF-8"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.6.xsd
http://www.liquibase.org/xml/ns/dbchangelog">
<changeSet id="1" author="a">
<createTable tableName="TABLE1">
<column>
</column>
</createTable>
</changeSet>
<changeSet id="1-1" author="a">
<createSequence sequenceName="SEQ_TABLE1" />
</changeSet>
<changeSet id="4" author="A">
<createTable tableName="TABLE4">
<column>
</column>
</createTable>
</changeSet>
</databaseChangeLog>
this is template:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:transform version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xpath-default-namespace="http://www.liquibase.org/xml/ns/dbchangelog">
<xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>
<xsl:variable name="coreTables" select="('TABLE1','TABLE2')"/>
<xsl:template match="node()[not(self::*)]">
<xsl:copy>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
<xsl:template match="*">
<xsl:element name="{local-name()}">
<xsl:apply-templates select="node()|#*"/>
</xsl:element>
</xsl:template>
<xsl:template match="#*">
<xsl:attribute name="{local-name()}">
<xsl:value-of select="."/>
</xsl:attribute>
</xsl:template>
<xsl:template match="databaseChangeLog">
<!-- CORE-->
<xsl:comment>CORE TABLES</xsl:comment>
<xsl:variable name="coreTablesVariable" select="changeSet[createTable/#tableName=$coreTables]"/>
<xsl:comment>CORE SEQUENCES</xsl:comment>
<xsl:variable name="coreSequencesVariable" select="changeSet[createSequence[starts-with(#sequenceName, 'SEQ_') and substring-after(#sequenceName, 'SEQ_') = $coreTables]]"/>
<xsl:comment>CORE INDEXES</xsl:comment>
<xsl:variable name="coreIndexesVariable" select="changeSet[createIndex/#tableName=$coreTables]"/>
<xsl:comment>CORE FOREIGN CONSTRAINTS</xsl:comment>
<xsl:variable name="coreForeignConstraintsVariable" select="changeSet[addForeignKeyConstraint/#baseTableName=$coreTables]"/>
<xsl:comment>CORE VIEWS</xsl:comment>
<xsl:variable name="coreViewsVariable" select="changeSet[createView/#viewName=$coreTables]"/>
<xsl:call-template name="createChangeLog">
<xsl:with-param name="outputFile" select="'core-changelog.xml'"/>
<xsl:with-param name="changeLogContent" select="$coreTablesVariable,$coreSequencesVariable,$coreIndexesVariable,$coreForeignConstraintsVariable,$coreViewsVariable"/>
</xsl:call-template>
<xsl:comment>UNMATCHED</xsl:comment>
<xsl:variable name="unmatchedChangeSets"
select="changeSet[not(some $set in ($coreTablesVariable | $coreSequencesVariable | $coreIndexesVariable |$coreForeignConstraintsVariable |$coreViewsVariable) satisfies $set is .)]"/>
<xsl:call-template name="createChangeLog">
<xsl:with-param name="outputFile" select="'unmatched-changes-changelog.xml'"/>
<xsl:with-param name="changeLogContent" select="$unmatchedChangeSets"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="createChangeLog">
<xsl:param name="outputFile"/>
<xsl:param name="changeLogContent"/>
<xsl:result-document encoding="UTF-8" indent="true" method="xml" href="{$outputFile}">
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.6.xsd http://www.liquibase.org/xml/ns/dbchangelog" logicalFilePath="TODO">
<xsl:copy-of select="$changeLogContent"/>
</databaseChangeLog>
</xsl:result-document>
</xsl:template>
</xsl:transform>
I'd like to add to output xml processed inside createChangelogTemplate to each element <changeSet> another attribute (context="legacy"). I was trying to add another template which matches databaseChangelog/changeSet with additional xsl:attribute element but that didn't worked for me. If is there way how to do this in one place that would be nice, because I will need to prepare more sections like CORE.
When I've added template:
<xsl:template match="changeSet" mode="legacy">
<xsl:copy>
<xsl:copy-of select="#*"/>
<xsl:attribute name="context">legacy</xsl:attribute>
<xsl:copy-of select="node()"/>
</xsl:copy>
</xsl:template>
and replaced <xsl:copy-of select="$changeLogContent"/> with <xsl:apply-templates select="$changeLogContent" mode="legacy"/> then output file core-changelog.xml was ok but file unmatched-changes-changelog.xml had no elements.
I'm using xslt 2.0 and saxon 9.8he.
When I run this I get changeSets 1 and 1-1 in core-changelog.xml, and changeSet 4 in unmatched-changelog.xml.
If you're still seeing nothing in unmatched-changelog then we need to look in more detail at exactly how you were running it.
Incidentally the selection of unmatched elements could be done as
select="changeSet except ($coreTablesVariable | $coreSequencesVariable | $coreIndexesVariable |$coreForeignConstraintsVariable |$coreViewsVariable)"/>
this is sample xml:
<?xml version="1.0" encoding="UTF-8"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.6.xsd
http://www.liquibase.org/xml/ns/dbchangelog">
<changeSet id="1" author="a">
<createTable tableName="TABLE1">
<column>
</column>
</createTable>
</changeSet>
<changeSet id="1-1" author="a">
<createSequence sequenceName="SEQ_TABLE1" />
</changeSet>
<changeSet id="4" author="A">
<createTable tableName="TABLE4">
<column>
</column>
</createTable>
</changeSet>
</databaseChangeLog>
this is template:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:transform version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xpath-default-namespace="http://www.liquibase.org/xml/ns/dbchangelog">
<xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>
<xsl:variable name="coreTables" select="('TABLE1','TABLE2')"/>
<xsl:template match="node()[not(self::*)]">
<xsl:copy>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
<xsl:template match="*">
<xsl:element name="{local-name()}">
<xsl:apply-templates select="node()|#*"/>
</xsl:element>
</xsl:template>
<xsl:template match="#*">
<xsl:attribute name="{local-name()}">
<xsl:value-of select="."/>
</xsl:attribute>
</xsl:template>
<xsl:template match="databaseChangeLog">
<!-- CORE-->
<xsl:comment>CORE TABLES</xsl:comment>
<xsl:variable name="coreTablesVariable" select="changeSet[createTable/#tableName=$coreTables]"/>
<xsl:comment>CORE SEQUENCES</xsl:comment>
<xsl:variable name="coreSequencesVariable" select="changeSet[createSequence[starts-with(#sequenceName, 'SEQ_') and substring-after(#sequenceName, 'SEQ_') = $coreTables]]"/>
<xsl:comment>CORE INDEXES</xsl:comment>
<xsl:variable name="coreIndexesVariable" select="changeSet[createIndex/#tableName=$coreTables]"/>
<xsl:comment>CORE FOREIGN CONSTRAINTS</xsl:comment>
<xsl:variable name="coreForeignConstraintsVariable" select="changeSet[addForeignKeyConstraint/#baseTableName=$coreTables]"/>
<xsl:comment>CORE VIEWS</xsl:comment>
<xsl:variable name="coreViewsVariable" select="changeSet[createView/#viewName=$coreTables]"/>
<xsl:call-template name="createChangeLog">
<xsl:with-param name="outputFile" select="'core-changelog.xml'"/>
<xsl:with-param name="changeLogContent" select="$coreTablesVariable,$coreSequencesVariable,$coreIndexesVariable,$coreForeignConstraintsVariable,$coreViewsVariable"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="createChangeLog">
<xsl:param name="outputFile"/>
<xsl:param name="changeLogContent"/>
<xsl:result-document encoding="UTF-8" indent="true" method="xml" href="{$outputFile}">
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.6.xsd http://www.liquibase.org/xml/ns/dbchangelog" logicalFilePath="TODO">
<xsl:copy-of select="$changeLogContent"/>
</databaseChangeLog>
</xsl:result-document>
</xsl:template>
</xsl:transform>
I'd like to add to output xml processed inside createChangelogTemplate to each element <changeSet> another attribute (context="legacy"). I was trying to add another template which matches databaseChangelog/changeSet with additional xsl:attribute element but that didn't worked for me. If is there way how to do this in one place that would be nice, because I will need to prepare more sections like CORE.
I'm using xslt 2.0 and saxon 9.8he.
Use a separate mode i.e. instead of <xsl:copy-of select="$changeLogContent"/> use <xsl:apply-templates select="$changeLogContent" mode="legacy"/>, then set up e.g.
<xsl:template match="changeSet" mode="legacy">
<xsl:copy>
<xsl:copy-of select="#*"/>
<xsl:attribute name="context">legacy</xsl:attribute>
<xsl:copy-of select="node()"/>
</xsl:copy>
</xsl:template>
If further processing of the attributes and or the child nodes is necessary then change the <xsl:copy-of select="#*"/> and/or the <xsl:copy-of select="node()"/> to use xsl:apply-templates mode="#current" and set up further templates for the mode that perform any processing.
In context to my earlier question at "XSLT transformation from XML to XML document". Further i was trying
to rename the "<value>" element to "<sName>" along with updating the value to "ABC" if the value of "dataset/type/text() ='test'". I wrote below code
for the same but it was not working as expected. please help.
Source XML-
<soapenv:Header/>
<soapenv:Body>
<v1:QueryRequest version="1">
<subject>
<dataList>
<!--1 or more repetitions:-->
<dataset>
<type>company</type>
<value>abc</value>
</dataset>
<dataset>
<type>user</type>
<value>xyz</value>
</dataset>
</dataList>
</subject>
<testList>
<!--1 or more repetitions:-->
<criteria>
<type>test</type>
<value>1234</value>
</criteria>
<criteria>
<type>test2</type>
<value>false</value>
</criteria>
</testList>
</v1:QueryRequest>
</soapenv:Body>
</soapenv:Envelope>
XSL file :-
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:old="http://www.abc.com/s/v1.0"
exclude-result-prefixes="old"
xmlns:v2="http://www.abc.com/s/v2.0">
<xsl:output method="xml" encoding="utf-8" indent="yes"
version="1.0" />
<xsl:param name="newversion" select="'2.0'" />
<xsl:param name="ABC" select="'ABC'" />
<xsl:param name="XYZ" select="'XYZ'" />
<xsl:param name="DFG" select="'DFG'" />
<!-- fix namespace declarations on root element -->
<xsl:template match="/*">
<xsl:element name="{name()}" namespace="{namespace-uri()}">
<!-- copy the v2: binding from the stylesheet document -->
<xsl:copy-of select="document('')/xsl:stylesheet/namespace::v2" />
<xsl:apply-templates select="#* | node()" />
</xsl:element>
</xsl:template>
<!-- replace namespace of elements in old namespace -->
<xsl:template match="old:*">
<xsl:element name="v2:{local-name()}">
<xsl:apply-templates select="#* | node()" />
</xsl:element>
</xsl:template>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()" />
</xsl:copy>
</xsl:template>
<xsl:template match="old:QueryRequest/#version">
<xsl:attribute name="version">
<xsl:value-of select="$newversion" />
</xsl:attribute>
</xsl:template>
<xsl:template match="criteria[type='service']/value">
<xsl:choose>
<xsl:when test="criteria[type='service']/value/text()='1234'">
<xsl:element name="sName">
<xsl:value-of select="$ABC"/>
</xsl:element>
</xsl:when>
<xsl:when test="criteria[type='service']/value/text()='5464'">
<xsl:element name="sName">
<xsl:value-of select="$XYZ"/>
</xsl:element>
</xsl:when>
<xsl:when test="criteria[type='service']/value/text()='8755'">
<xsl:element name="sName">
<xsl:value-of select="$DFG"/>
</xsl:element>
</xsl:when>
</xsl:choose>
</xsl:template>
<xsl:template match="type/text()">
<xsl:value-of
select="translate(., 'abcdefghijklmnopqrstuvwxyz','ABCDEFGHIJKLMNOPQRSTUVWXYZ')" />
</xsl:template>
</xsl:stylesheet>
Expected Output:
<testList>
<criteria>
<type>test</type>
<sName>ABC</sName>
</criteria>
<criteria>
<type>test2</type>
<value>false</value>
</criteria>
</testList>
Within a
<xsl:template match="criteria[type='service']/value">
the value element is the current context node, so your other XPath expressions need to be relative to that, i.e. instead of
<xsl:when test="criteria[type='service']/value/text()='1234'">
you would just use
<xsl:when test="text()='1234'">
or better
<xsl:when test=".='1234'">